Repository: louthy/language-ext Branch: main Commit: 041f74a00f66 Files: 1863 Total size: 7.7 MB Directory structure: gitextract_5rnkpet5/ ├── .editorconfig ├── .gitattributes ├── .github/ │ └── FUNDING.yml ├── .gitignore ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE.md ├── LanguageExt.Benchmarks/ │ ├── HashMapAddBenchmarks.cs │ ├── HashMapContainsKeyBenchmarks.cs │ ├── HashMapIterateBenchmarks.cs │ ├── HashMapRandomRemovalBenchmarks.cs │ ├── HashSetAddBenchmarks.cs │ ├── HashSetContainsBenchmarks.cs │ ├── HashSetIterationBenchmarks.cs │ ├── HashSetRandomRemovalBenchmarks.cs │ ├── LanguageExt.Benchmarks.csproj │ ├── ListAddBenchmarks.cs │ ├── ListIterationBenchmarks.cs │ ├── Program.cs │ ├── ValuesGenerator.MapSetup.cs │ ├── ValuesGenerator.SetSetup.cs │ └── ValuesGenerator.cs ├── LanguageExt.Core/ │ ├── Catch.cs │ ├── Class Instances/ │ │ ├── Eq/ │ │ │ ├── EqArr.cs │ │ │ ├── EqArray.cs │ │ │ ├── EqBigInt.cs │ │ │ ├── EqBool.cs │ │ │ ├── EqChar.cs │ │ │ ├── EqCompositions.cs │ │ │ ├── EqDateTime.cs │ │ │ ├── EqDecimal.cs │ │ │ ├── EqDefault.cs │ │ │ ├── EqDouble.cs │ │ │ ├── EqEdit.cs │ │ │ ├── EqEither.cs │ │ │ ├── EqEnumerable.cs │ │ │ ├── EqException.cs │ │ │ ├── EqFloat.cs │ │ │ ├── EqGuid.cs │ │ │ ├── EqHashSet.cs │ │ │ ├── EqIdentity.cs │ │ │ ├── EqInt.cs │ │ │ ├── EqIterable.cs │ │ │ ├── EqLong.cs │ │ │ ├── EqLst.cs │ │ │ ├── EqMap.cs │ │ │ ├── EqOption.cs │ │ │ ├── EqPatch.cs │ │ │ ├── EqQue.cs │ │ │ ├── EqRecord.cs │ │ │ ├── EqSeq.cs │ │ │ ├── EqSet.cs │ │ │ ├── EqShort.cs │ │ │ ├── EqStck.cs │ │ │ ├── EqString.cs │ │ │ ├── EqTask.cs │ │ │ ├── EqTrue.cs │ │ │ ├── EqTuple2.cs │ │ │ └── EqTypeInfo.cs │ │ ├── Hashable/ │ │ │ ├── HashableArr.cs │ │ │ ├── HashableArray.cs │ │ │ ├── HashableBigInt.cs │ │ │ ├── HashableBool.cs │ │ │ ├── HashableChar.cs │ │ │ ├── HashableCompositions.cs │ │ │ ├── HashableDateTime.cs │ │ │ ├── HashableDecimal.cs │ │ │ ├── HashableDefault.cs │ │ │ ├── HashableDouble.cs │ │ │ ├── HashableEdit.cs │ │ │ ├── HashableEither.cs │ │ │ ├── HashableEnumerable.cs │ │ │ ├── HashableException.cs │ │ │ ├── HashableFloat.cs │ │ │ ├── HashableGuid.cs │ │ │ ├── HashableHashSet.cs │ │ │ ├── HashableIdentity.cs │ │ │ ├── HashableInt.cs │ │ │ ├── HashableIterable.cs │ │ │ ├── HashableLong.cs │ │ │ ├── HashableLst.cs │ │ │ ├── HashableMap.cs │ │ │ ├── HashableOption.cs │ │ │ ├── HashablePair.cs │ │ │ ├── HashablePatch.cs │ │ │ ├── HashableQue.cs │ │ │ ├── HashableRecord.cs │ │ │ ├── HashableSeq.cs │ │ │ ├── HashableSet.cs │ │ │ ├── HashableShort.cs │ │ │ ├── HashableStck.cs │ │ │ ├── HashableString.cs │ │ │ ├── HashableTask.cs │ │ │ ├── HashableTuple.cs │ │ │ └── HashableTypeInfo.cs │ │ ├── Monoid/ │ │ │ ├── Addition.cs │ │ │ ├── All.cs │ │ │ ├── Any.cs │ │ │ ├── MError.cs │ │ │ ├── MUnit.cs │ │ │ └── Product.cs │ │ ├── Ord/ │ │ │ ├── OrdArr.cs │ │ │ ├── OrdArray.cs │ │ │ ├── OrdBigInt.cs │ │ │ ├── OrdBool.cs │ │ │ ├── OrdChar.cs │ │ │ ├── OrdDateTime.cs │ │ │ ├── OrdDecimal.cs │ │ │ ├── OrdDefault.cs │ │ │ ├── OrdDouble.cs │ │ │ ├── OrdEither.cs │ │ │ ├── OrdEnumerable.cs │ │ │ ├── OrdException.cs │ │ │ ├── OrdFloat.cs │ │ │ ├── OrdGuid.cs │ │ │ ├── OrdHashSet.cs │ │ │ ├── OrdInt.cs │ │ │ ├── OrdIterable.cs │ │ │ ├── OrdLong.cs │ │ │ ├── OrdLst.cs │ │ │ ├── OrdMap.cs │ │ │ ├── OrdOption.cs │ │ │ ├── OrdQue.cs │ │ │ ├── OrdRecord.cs │ │ │ ├── OrdSeq.cs │ │ │ ├── OrdSet.cs │ │ │ ├── OrdShort.cs │ │ │ ├── OrdStck.cs │ │ │ ├── OrdString.cs │ │ │ ├── OrdTask.cs │ │ │ ├── OrdTrue.cs │ │ │ ├── OrdTupleFirst.cs │ │ │ └── OrdTypeInfo.cs │ │ ├── README.md │ │ ├── TBigInt.cs │ │ ├── TBool.cs │ │ ├── TBoolBool.cs │ │ ├── TChar.cs │ │ ├── TDecimal.cs │ │ ├── TDouble.cs │ │ ├── TFloat.cs │ │ ├── TInt.cs │ │ ├── TLong.cs │ │ ├── TNumericChar.cs │ │ ├── TShort.cs │ │ └── TString.cs │ ├── Combinators.cs │ ├── Comment Alternatives.cs │ ├── Common/ │ │ ├── Error.cs │ │ ├── ErrorCodes.cs │ │ ├── ErrorException.cs │ │ ├── Errors.cs │ │ ├── Exceptions.cs │ │ └── README.md │ ├── Concurrency/ │ │ ├── Async/ │ │ │ ├── Async.Module.cs │ │ │ └── AsyncEnumerable/ │ │ │ ├── AsyncEnumberable.Extensions.cs │ │ │ └── AsyncEnumberable.LINQ.Extensions.cs │ │ ├── Atom/ │ │ │ ├── Atom.Metadata.cs │ │ │ ├── Atom.cs │ │ │ ├── AtomChangedEvent.cs │ │ │ └── README.md │ │ ├── AtomHashMap/ │ │ │ ├── AtomHashMap.Eq.cs │ │ │ ├── AtomHashMap.Module.Eq.cs │ │ │ ├── AtomHashMap.Module.cs │ │ │ ├── AtomHashMap.cs │ │ │ └── Events.cs │ │ ├── AtomQue/ │ │ │ ├── AtomQue.Extensions.cs │ │ │ ├── AtomQue.Trait.Implementation.cs │ │ │ └── AtomQue.cs │ │ ├── AtomSeq/ │ │ │ └── AtomSeq.cs │ │ ├── Conflict.cs │ │ ├── Prelude.Concurrency.cs │ │ ├── README.md │ │ ├── STM/ │ │ │ ├── CommuteRef.cs │ │ │ ├── Isolation.cs │ │ │ ├── Ref.cs │ │ │ └── STM.cs │ │ ├── Signals/ │ │ │ ├── CountdownSignal.cs │ │ │ ├── Signal.Module.cs │ │ │ └── Signal.cs │ │ ├── Task/ │ │ │ ├── Task.Extensions.cs │ │ │ ├── Task.Prelude.cs │ │ │ └── Tasks.cs │ │ ├── ValueTask/ │ │ │ ├── ValueTask.Extensions.cs │ │ │ ├── ValueTask.Prelude.cs │ │ │ └── ValueTasks.cs │ │ ├── VectorClock/ │ │ │ ├── Relation.cs │ │ │ ├── VectorClock.A.cs │ │ │ ├── VectorClock.OrdA.NumB.A.B.cs │ │ │ └── VectorClock.cs │ │ ├── VersionHashMap/ │ │ │ ├── VersionHashMap.ConflictV.K.V.cs │ │ │ ├── VersionHashMap.ConflictV.OrdActor.EqK.Actor.K.V.cs │ │ │ └── VersionHashMap.K.V.cs │ │ └── VersionVector/ │ │ ├── Version.cs │ │ └── VersionVector.cs │ ├── DataTypes/ │ │ ├── BigInt/ │ │ │ └── BigInt.cs │ │ ├── Change/ │ │ │ ├── Change.cs │ │ │ ├── EntryAdded.cs │ │ │ ├── EntryMapped.cs │ │ │ ├── EntryRemoved.cs │ │ │ └── NoChange.cs │ │ ├── Compositions/ │ │ │ ├── Compositions.Extensions.cs │ │ │ ├── Compositions.Module.cs │ │ │ ├── Compositions.cs │ │ │ └── FoldCompositions.cs │ │ ├── Fail/ │ │ │ ├── Fail.Extensions.cs │ │ │ └── Fail.cs │ │ ├── Loop/ │ │ │ ├── Loop.Extensions.cs │ │ │ ├── Loop.Prelude.cs │ │ │ └── Loop.cs │ │ ├── Lower/ │ │ │ └── Lower.cs │ │ ├── MapPatch/ │ │ │ └── HashMapPatch.cs │ │ ├── Next/ │ │ │ ├── Next.Module.cs │ │ │ └── Next.cs │ │ ├── Patch/ │ │ │ ├── Edit.cs │ │ │ ├── Patch.Internal.cs │ │ │ ├── Patch.Module.cs │ │ │ ├── Patch.cs │ │ │ └── PatchParams.cs │ │ ├── Pure/ │ │ │ ├── Pure.Extensions.cs │ │ │ └── Pure.cs │ │ ├── README.md │ │ ├── Range/ │ │ │ ├── Range.Extensions.cs │ │ │ ├── Range.Module.cs │ │ │ ├── Range.Monad.cs │ │ │ └── Range.cs │ │ ├── Ratio/ │ │ │ └── Ratio.cs │ │ ├── Record/ │ │ │ ├── Attributes.cs │ │ │ ├── Record.cs │ │ │ ├── RecordType.cs │ │ │ ├── RecordTypeIgnoreBase.cs │ │ │ └── TypeInfoExt.cs │ │ ├── SpanArray/ │ │ │ └── SpanArray.cs │ │ ├── StringM/ │ │ │ ├── OrdString.cs │ │ │ ├── StringM.Ord.cs │ │ │ ├── StringM.Trait.cs │ │ │ └── StringM.cs │ │ ├── Unit/ │ │ │ ├── Unit.Prelude.cs │ │ │ └── Unit.cs │ │ └── ValueTuple/ │ │ ├── Tuple1/ │ │ │ ├── ValueTuple1.Extensions.cs │ │ │ └── ValueTuple1.Prelude.cs │ │ ├── Tuple2/ │ │ │ ├── ValueTuple2.Extensions.cs │ │ │ └── ValueTuple2.Prelude.cs │ │ ├── Tuple3/ │ │ │ ├── ValueTuple3.Extensions.cs │ │ │ └── ValueTuple3.Prelude.cs │ │ ├── Tuple4/ │ │ │ ├── ValueTuple4.Extensions.cs │ │ │ └── ValueTuple4.Prelude.cs │ │ ├── Tuple5/ │ │ │ ├── ValueTuple5.Extensions.cs │ │ │ └── ValueTuple5.Prelude.cs │ │ ├── Tuple6/ │ │ │ ├── ValueTuple6.Extensions.cs │ │ │ └── ValueTuple6.Prelude.cs │ │ └── Tuple7/ │ │ ├── ValueTuple7.Extensions.cs │ │ └── ValueTuple7.Prelude.cs │ ├── Deriving/ │ │ ├── Alternative.cs │ │ ├── Applicative.cs │ │ ├── Choice.cs │ │ ├── Cofunctor.cs │ │ ├── Decidable.cs │ │ ├── Divisible.cs │ │ ├── Fallible.cs │ │ ├── Final.cs │ │ ├── Foldable.cs │ │ ├── Functor.cs │ │ ├── Maybe/ │ │ │ ├── MonadIO.cs │ │ │ └── MonadUnliftIO.cs │ │ ├── Monad.cs │ │ ├── MonadIO.cs │ │ ├── MonadT.cs │ │ ├── MonadUnliftIO.cs │ │ ├── MonoidK.cs │ │ ├── Readable.cs │ │ ├── SemigroupK.cs │ │ ├── Stateful.cs │ │ ├── Traversable.cs │ │ └── Writable.cs │ ├── Effects/ │ │ ├── Eff/ │ │ │ ├── Eff no runtime/ │ │ │ │ ├── Eff.Module.cs │ │ │ │ ├── Eff.Monad.cs │ │ │ │ ├── Eff.cs │ │ │ │ ├── Extensions/ │ │ │ │ │ ├── Eff.Extensions.MapApply.cs │ │ │ │ │ ├── Eff.Extensions.cs │ │ │ │ │ └── Eff.Guard.cs │ │ │ │ ├── Operators/ │ │ │ │ │ ├── Eff.Operators.Applicative.cs │ │ │ │ │ ├── Eff.Operators.Choice.cs │ │ │ │ │ ├── Eff.Operators.Fallible.cs │ │ │ │ │ ├── Eff.Operators.Final.cs │ │ │ │ │ ├── Eff.Operators.Functor.cs │ │ │ │ │ ├── Eff.Operators.Monad.cs │ │ │ │ │ └── Eff.Operators.cs │ │ │ │ └── Prelude/ │ │ │ │ ├── Eff.Prelude.cs │ │ │ │ └── Eff.Prelude.mapapply.cs │ │ │ └── Eff with runtime/ │ │ │ ├── Eff.Module.cs │ │ │ ├── Eff.cs │ │ │ ├── Extensions/ │ │ │ │ ├── Eff.Extensions.MapApply.cs │ │ │ │ ├── Eff.Extensions.cs │ │ │ │ └── Eff.Guard.cs │ │ │ ├── Operators/ │ │ │ │ ├── Eff.Operators.Applicative.cs │ │ │ │ ├── Eff.Operators.Choice.cs │ │ │ │ ├── Eff.Operators.Fallible.cs │ │ │ │ ├── Eff.Operators.Final.cs │ │ │ │ ├── Eff.Operators.Functor.cs │ │ │ │ ├── Eff.Operators.Monad.cs │ │ │ │ └── Eff.Operators.cs │ │ │ └── Prelude/ │ │ │ ├── Eff.Prelude.cs │ │ │ └── Eff.Prelude.mapapply.cs │ │ ├── IO/ │ │ │ ├── DSL/ │ │ │ │ ├── IOAction.cs │ │ │ │ ├── IOActions.cs │ │ │ │ ├── IOActionsAsync.cs │ │ │ │ ├── IOApply.cs │ │ │ │ ├── IOBind.cs │ │ │ │ ├── IOBindAsync.cs │ │ │ │ ├── IOBindMap.cs │ │ │ │ ├── IOCatch.cs │ │ │ │ ├── IOCatchPop.cs │ │ │ │ ├── IOEmpty.cs │ │ │ │ ├── IOFail.cs │ │ │ │ ├── IOFinal.cs │ │ │ │ ├── IOFold.cs │ │ │ │ ├── IOFoldUntil.cs │ │ │ │ ├── IOFoldWhile.cs │ │ │ │ ├── IOLiftAsync.cs │ │ │ │ ├── IOLiftSync.cs │ │ │ │ ├── IOLocal2.cs │ │ │ │ ├── IOMap.cs │ │ │ │ ├── IOPure.cs │ │ │ │ ├── IOPureAsync.cs │ │ │ │ ├── IOTail.cs │ │ │ │ ├── IOTimeout.cs │ │ │ │ ├── IOToken.cs │ │ │ │ ├── IOUninterruptible.cs │ │ │ │ ├── IOUse.cs │ │ │ │ ├── InvokeAsync.cs │ │ │ │ ├── InvokeAsyncIO.cs │ │ │ │ ├── InvokeSync.cs │ │ │ │ └── InvokeSyncIO.cs │ │ │ ├── EnvIO.cs │ │ │ ├── Extensions/ │ │ │ │ ├── IO.Extensions.MapApply.cs │ │ │ │ ├── IO.Extensions.cs │ │ │ │ └── IO.Guard.cs │ │ │ ├── ForkIO.cs │ │ │ ├── IO.Module.cs │ │ │ ├── IO.Monad.cs │ │ │ ├── IO.cs │ │ │ ├── Operators/ │ │ │ │ ├── IO.Operators.Applicative.cs │ │ │ │ ├── IO.Operators.Choice.cs │ │ │ │ ├── IO.Operators.Fallible.cs │ │ │ │ ├── IO.Operators.Final.cs │ │ │ │ ├── IO.Operators.Functor.cs │ │ │ │ ├── IO.Operators.Monad.cs │ │ │ │ └── IO.Operators.cs │ │ │ ├── Prelude/ │ │ │ │ ├── IO.Prelude.Concurreny.cs │ │ │ │ ├── IO.Prelude.cs │ │ │ │ └── IO.Prelude.mapapply.cs │ │ │ └── Resources.cs │ │ ├── MinRT.cs │ │ ├── Prelude.cs │ │ ├── README.md │ │ └── Schedule/ │ │ ├── Duration.cs │ │ ├── Schedule.Constructors.cs │ │ ├── Schedule.DSL.cs │ │ ├── Schedule.Extensions.cs │ │ ├── Schedule.Prelude.cs │ │ ├── Schedule.cs │ │ ├── ScheduleTransformer.cs │ │ └── SingletonRandom.cs │ ├── Exceptions/ │ │ └── Exceptions.cs │ ├── Extensions/ │ │ ├── ActionObservable.cs │ │ ├── Compose.cs │ │ ├── FuncExtensions.cs │ │ ├── ObjectExt.cs │ │ ├── ObservableExt.cs │ │ ├── Query.cs │ │ ├── TryOutExt.cs │ │ └── UnsafeValueAccess.cs │ ├── Guard.cs │ ├── Immutable Collections/ │ │ ├── Arr/ │ │ │ ├── Arr.Module.cs │ │ │ ├── Arr.cs │ │ │ ├── Extensions/ │ │ │ │ ├── Arr.Extensions.MapApply.cs │ │ │ │ └── Arr.Extensions.cs │ │ │ ├── Operators/ │ │ │ │ └── Arr.Operators.cs │ │ │ ├── Prelude/ │ │ │ │ └── Arr.Prelude.mapapply.cs │ │ │ └── Trait/ │ │ │ └── Arr.TraitImpl.cs │ │ ├── BiMap/ │ │ │ └── BiMap.cs │ │ ├── HashMap/ │ │ │ ├── HashMap.Eq.cs │ │ │ ├── HashMap.Extensions.Eq.cs │ │ │ ├── HashMap.Extensions.MapApply.cs │ │ │ ├── HashMap.Extensions.cs │ │ │ ├── HashMap.Module.Eq.cs │ │ │ ├── HashMap.Module.cs │ │ │ ├── HashMap.Prelude.mapapply.cs │ │ │ ├── HashMap.Trait.Implementations.Eq.cs │ │ │ ├── HashMap.Trait.Implementations.cs │ │ │ └── HashMap.cs │ │ ├── HashSet/ │ │ │ ├── Extensions/ │ │ │ │ ├── HashSet.Extensions.MapApply.cs │ │ │ │ └── HashSet.Extensions.cs │ │ │ ├── HashSet.Eq.cs │ │ │ ├── HashSet.Module.Eq.cs │ │ │ ├── HashSet.Module.cs │ │ │ ├── HashSet.cs │ │ │ ├── Operators/ │ │ │ │ └── HashSet.Operators.cs │ │ │ ├── Prelude/ │ │ │ │ └── HashSet.Prelude.mapapply.cs │ │ │ └── Trait/ │ │ │ └── HashSet.TraitImpl.cs │ │ ├── Iterable/ │ │ │ ├── DSL/ │ │ │ │ ├── Iterable.Add.cs │ │ │ │ ├── Iterable.AsyncEnumerable.cs │ │ │ │ ├── Iterable.Cast.cs │ │ │ │ ├── Iterable.Concat.cs │ │ │ │ ├── Iterable.Enumerable.cs │ │ │ │ ├── Iterable.Nil.cs │ │ │ │ ├── Iterable.Singleton.cs │ │ │ │ ├── Iterable.SingletonIO.cs │ │ │ │ ├── Iterable.Strict.cs │ │ │ │ └── Iterable.Zip.cs │ │ │ ├── Extensions/ │ │ │ │ ├── Iterable.Extensions.MapApply.cs │ │ │ │ └── Iterable.Extensions.cs │ │ │ ├── Iterable.Module.cs │ │ │ ├── Iterable.cs │ │ │ ├── Operators/ │ │ │ │ └── Iterable.Operators.cs │ │ │ ├── Prelude/ │ │ │ │ └── Iterable.Prelude.mapapply.cs │ │ │ └── Trait/ │ │ │ └── Iterable.TraitImpl.cs │ │ ├── IterableNE/ │ │ │ ├── Extensions/ │ │ │ │ ├── IterableNE.Extensions.MapApply.cs │ │ │ │ └── IterableNE.Extensions.cs │ │ │ ├── IterableNE.Module.cs │ │ │ ├── IterableNE.cs │ │ │ ├── Operators/ │ │ │ │ └── IterableNE.Operators.cs │ │ │ └── Trait/ │ │ │ └── IterableNE.TraitImpl.cs │ │ ├── Iterator/ │ │ │ ├── DSL/ │ │ │ │ ├── Iterator.Cons.cs │ │ │ │ ├── Iterator.ConsFirst.cs │ │ │ │ ├── Iterator.ConsValue.cs │ │ │ │ ├── Iterator.ConsValueEnum.cs │ │ │ │ ├── Iterator.ConsValueLazy.cs │ │ │ │ └── Iterator.Nil.cs │ │ │ ├── Extensions/ │ │ │ │ └── Iterator.Extensions.cs │ │ │ ├── Iterator.Module.cs │ │ │ ├── Iterator.cs │ │ │ ├── Operators/ │ │ │ │ └── Iterator.Operators.cs │ │ │ ├── README.md │ │ │ └── Trait/ │ │ │ └── Iterator.TraitImpl.cs │ │ ├── IteratorAsync/ │ │ │ ├── DSL/ │ │ │ │ ├── IteratorAsync.Cons.cs │ │ │ │ ├── IteratorAsync.ConsFirst.cs │ │ │ │ ├── IteratorAsync.ConsValue.cs │ │ │ │ ├── IteratorAsync.ConsValueEnum.cs │ │ │ │ ├── IteratorAsync.ConsValueLazy.cs │ │ │ │ └── IteratorAsync.Nil.cs │ │ │ ├── Extensions/ │ │ │ │ └── IteratorAsync.Extensions.cs │ │ │ ├── IteratorAsync.Module.cs │ │ │ ├── IteratorAsync.cs │ │ │ ├── Operators/ │ │ │ │ └── IteratorAsync.Operators.cs │ │ │ ├── README.md │ │ │ └── Trait/ │ │ │ └── IteratorAsync.TraitImpl.cs │ │ ├── List/ │ │ │ ├── Extensions/ │ │ │ │ ├── Lst.Extensions.MapApply.cs │ │ │ │ └── Lst.Extensions.cs │ │ │ ├── Internal/ │ │ │ │ └── Lst.Internal.cs │ │ │ ├── ListInfo.cs │ │ │ ├── Lst.Module.cs │ │ │ ├── Lst.cs │ │ │ ├── Operators/ │ │ │ │ └── Lst.Operators.cs │ │ │ ├── Prelude/ │ │ │ │ └── Lst.Prelude.mapapply.cs │ │ │ └── Trait/ │ │ │ └── Lst.TraitImpl.cs │ │ ├── Map/ │ │ │ ├── Map.EnumeratorK.cs │ │ │ ├── Map.EnumeratorKV.cs │ │ │ ├── Map.EnumeratorV.cs │ │ │ ├── Map.Extensions.MapApply.cs │ │ │ ├── Map.Extensions.Ord.cs │ │ │ ├── Map.Extensions.cs │ │ │ ├── Map.Internal.cs │ │ │ ├── Map.Module.Ord.cs │ │ │ ├── Map.Module.cs │ │ │ ├── Map.Ord.cs │ │ │ ├── Map.Prelude.mapapply.cs │ │ │ ├── Map.Trait.Implementations.cs │ │ │ └── Map.cs │ │ ├── Prelude.Collections.cs │ │ ├── Queue/ │ │ │ ├── Que.Internal.cs │ │ │ ├── Que.cs │ │ │ └── Queue.Module.cs │ │ ├── README.md │ │ ├── Seq/ │ │ │ ├── DSL/ │ │ │ │ ├── Enum.cs │ │ │ │ ├── ISeqInternal.cs │ │ │ │ ├── SeqConcat.cs │ │ │ │ ├── SeqEmptyInternal.cs │ │ │ │ ├── SeqLazy.cs │ │ │ │ └── SeqStrict.cs │ │ │ ├── Extensions/ │ │ │ │ ├── Seq.Extensions.MapApply.cs │ │ │ │ └── Seq.Extensions.cs │ │ │ ├── Operators/ │ │ │ │ └── Seq.Operators.cs │ │ │ ├── Prelude/ │ │ │ │ └── Seq.Prelude.mapapply.cs │ │ │ ├── Seq.Module.cs │ │ │ ├── Seq.cs │ │ │ ├── SeqEmpty.cs │ │ │ ├── SeqLoan.cs │ │ │ └── Trait/ │ │ │ └── Seq.TraitImpl.cs │ │ ├── Set/ │ │ │ ├── Extensions/ │ │ │ │ ├── Set.Extensions.MapApply.cs │ │ │ │ └── Set.Extensions.cs │ │ │ ├── Internal/ │ │ │ │ └── Set.Internal.cs │ │ │ ├── Prelude/ │ │ │ │ └── Set.Prelude.mapapply.cs │ │ │ ├── Set.Module.cs │ │ │ ├── Set.Ord.Module.cs │ │ │ ├── Set.Ord.cs │ │ │ ├── Set.cs │ │ │ └── Trait/ │ │ │ └── Set.TraitImpl.cs │ │ ├── Stack/ │ │ │ ├── Stack.Extensions.cs │ │ │ ├── Stack.Module.cs │ │ │ ├── Stck.Internal.cs │ │ │ └── Stck.cs │ │ ├── TrackingHashMap/ │ │ │ ├── TrackingHashMap.Eq.cs │ │ │ ├── TrackingHashMap.Extensions.Eq.cs │ │ │ ├── TrackingHashMap.Extensions.cs │ │ │ ├── TrackingHashMap.Module.Eq.cs │ │ │ ├── TrackingHashMap.Module.cs │ │ │ └── TrackingHashMap.cs │ │ ├── TrieMap/ │ │ │ └── TrieMap.cs │ │ └── TrieSet/ │ │ └── TrieSet.cs │ ├── LanguageExt.Core.csproj │ ├── Lens/ │ │ ├── Lens.Operators.cs │ │ ├── Lens.cs │ │ ├── LensAB.cs │ │ ├── Prelude.Lens.cs │ │ └── README.md │ ├── Memo/ │ │ ├── Extensions/ │ │ │ └── Memo.Extensions.cs │ │ ├── Memo.F.cs │ │ ├── Memo.Module.cs │ │ ├── Memo.cs │ │ ├── Operators/ │ │ │ └── Memo.Operators.cs │ │ └── Prelude.Memoize.cs │ ├── Monads/ │ │ ├── Alternative Monads/ │ │ │ ├── ChronicleT/ │ │ │ │ ├── ChronicleT.Module.2.cs │ │ │ │ ├── ChronicleT.Module.cs │ │ │ │ ├── ChronicleT.cs │ │ │ │ ├── Extensions/ │ │ │ │ │ ├── ChronicleT.Extensions.cs │ │ │ │ │ └── ChronicleT.Guard.cs │ │ │ │ ├── Operators/ │ │ │ │ │ ├── ChronicleT.Operators.Applicative.cs │ │ │ │ │ ├── ChronicleT.Operators.Choice.cs │ │ │ │ │ ├── ChronicleT.Operators.Fallible.cs │ │ │ │ │ ├── ChronicleT.Operators.Final.cs │ │ │ │ │ ├── ChronicleT.Operators.Functor.cs │ │ │ │ │ ├── ChronicleT.Operators.Monad.cs │ │ │ │ │ ├── ChronicleT.Operators.SemigroupK.cs │ │ │ │ │ └── ChronicleT.Operators.cs │ │ │ │ ├── Prelude/ │ │ │ │ │ └── ChronicleT.Prelude.cs │ │ │ │ └── Trait/ │ │ │ │ ├── ChronicleT.TraitImpl.2.cs │ │ │ │ └── ChronicleT.TraitImpl.cs │ │ │ ├── Either/ │ │ │ │ ├── Either.Left.cs │ │ │ │ ├── Either.Module.cs │ │ │ │ ├── Either.Right.cs │ │ │ │ ├── Either.cs │ │ │ │ ├── Extensions/ │ │ │ │ │ ├── Either.Extensions.Apply.cs │ │ │ │ │ ├── Either.Extensions.Map.cs │ │ │ │ │ ├── Either.Extensions.cs │ │ │ │ │ └── Either.Guard.cs │ │ │ │ ├── IEither.cs │ │ │ │ ├── Operators/ │ │ │ │ │ ├── Either.Operators.Applicative.cs │ │ │ │ │ ├── Either.Operators.Choice.cs │ │ │ │ │ ├── Either.Operators.Fallible.cs │ │ │ │ │ ├── Either.Operators.Functor.cs │ │ │ │ │ ├── Either.Operators.Monad.cs │ │ │ │ │ └── Either.Operators.cs │ │ │ │ ├── Prelude/ │ │ │ │ │ ├── Either.Prelude.cs │ │ │ │ │ └── Either.Prelude.mapapply.cs │ │ │ │ ├── README.md │ │ │ │ └── Trait/ │ │ │ │ ├── Either.TraitImpl.2.cs │ │ │ │ └── Either.TraitImpl.cs │ │ │ ├── EitherT/ │ │ │ │ ├── EitherT.Module.cs │ │ │ │ ├── EitherT.cs │ │ │ │ ├── Extensions/ │ │ │ │ │ ├── EitherT.Extensions.Apply.cs │ │ │ │ │ ├── EitherT.Extensions.Map.cs │ │ │ │ │ ├── EitherT.Extensions.cs │ │ │ │ │ └── EitherT.Guard.cs │ │ │ │ ├── Operators/ │ │ │ │ │ ├── EitherT.Operators.Applicative.cs │ │ │ │ │ ├── EitherT.Operators.Choice.cs │ │ │ │ │ ├── EitherT.Operators.Fallible.cs │ │ │ │ │ ├── EitherT.Operators.Final.cs │ │ │ │ │ ├── EitherT.Operators.Functor.cs │ │ │ │ │ ├── EitherT.Operators.Monad.cs │ │ │ │ │ ├── EitherT.Operators.SemigroupK.cs │ │ │ │ │ └── EitherT.Operators.cs │ │ │ │ └── Trait/ │ │ │ │ ├── EitherT.TraitImpl.2.cs │ │ │ │ └── EitherT.TraitImpl.cs │ │ │ ├── Fin/ │ │ │ │ ├── Extensions/ │ │ │ │ │ ├── Fin.Extensions.Apply.cs │ │ │ │ │ ├── Fin.Extensions.Map.cs │ │ │ │ │ ├── Fin.Extensions.cs │ │ │ │ │ └── Fin.Guard.cs │ │ │ │ ├── Fin.Fail.cs │ │ │ │ ├── Fin.Module.cs │ │ │ │ ├── Fin.Succ.cs │ │ │ │ ├── Fin.cs │ │ │ │ ├── Operators/ │ │ │ │ │ ├── Fin.Operators.Applicative.cs │ │ │ │ │ ├── Fin.Operators.Choice.cs │ │ │ │ │ ├── Fin.Operators.Fallible.cs │ │ │ │ │ ├── Fin.Operators.Functor.cs │ │ │ │ │ ├── Fin.Operators.Monad.cs │ │ │ │ │ ├── Fin.Operators.SemigroupK.cs │ │ │ │ │ └── Fin.Operators.cs │ │ │ │ ├── README.md │ │ │ │ └── Trait/ │ │ │ │ └── Fin.TraitImpl.cs │ │ │ ├── FinT/ │ │ │ │ ├── Extensions/ │ │ │ │ │ ├── FinT.Extensions.Apply.cs │ │ │ │ │ ├── FinT.Extensions.Map.cs │ │ │ │ │ ├── FinT.Extensions.cs │ │ │ │ │ └── FinT.Guard.cs │ │ │ │ ├── FinT.Module.cs │ │ │ │ ├── FinT.cs │ │ │ │ ├── Operators/ │ │ │ │ │ ├── FinT.Operators.Applicative.cs │ │ │ │ │ ├── FinT.Operators.Choice.cs │ │ │ │ │ ├── FinT.Operators.Fallible.cs │ │ │ │ │ ├── FinT.Operators.Final.cs │ │ │ │ │ ├── FinT.Operators.Functor.cs │ │ │ │ │ ├── FinT.Operators.Monad.cs │ │ │ │ │ ├── FinT.Operators.SemigroupK.cs │ │ │ │ │ └── FinT.Operators.cs │ │ │ │ └── Trait/ │ │ │ │ └── FinT.TraitImpl.cs │ │ │ ├── Nullable/ │ │ │ │ ├── Nullable.Prelude.cs │ │ │ │ └── README.md │ │ │ ├── Option/ │ │ │ │ ├── Extensions/ │ │ │ │ │ ├── Option.Extensions.MapApply.cs │ │ │ │ │ └── Option.Extensions.cs │ │ │ │ ├── Operators/ │ │ │ │ │ ├── Option.Operators.Applicative.cs │ │ │ │ │ ├── Option.Operators.Choice.cs │ │ │ │ │ ├── Option.Operators.Fallible.cs │ │ │ │ │ ├── Option.Operators.Functor.cs │ │ │ │ │ ├── Option.Operators.Monad.cs │ │ │ │ │ └── Option.Operators.cs │ │ │ │ ├── Option.Module.cs │ │ │ │ ├── Option.cs │ │ │ │ ├── Prelude/ │ │ │ │ │ ├── Option.Prelude.cs │ │ │ │ │ └── Option.Prelude.mapapply.cs │ │ │ │ ├── README.md │ │ │ │ ├── Shared/ │ │ │ │ │ ├── IOptional.cs │ │ │ │ │ ├── SomeContext.cs │ │ │ │ │ └── SomeUnitContext.cs │ │ │ │ └── Trait/ │ │ │ │ └── Option.TraitImpl.cs │ │ │ ├── OptionT/ │ │ │ │ ├── Extensions/ │ │ │ │ │ ├── OptionT.Extensions.MapApply.cs │ │ │ │ │ └── OptionT.Extensions.cs │ │ │ │ ├── Operators/ │ │ │ │ │ ├── OptionT.Operators.Applicative.cs │ │ │ │ │ ├── OptionT.Operators.Choice.cs │ │ │ │ │ ├── OptionT.Operators.Fallible.cs │ │ │ │ │ ├── OptionT.Operators.Final.cs │ │ │ │ │ ├── OptionT.Operators.Functor.cs │ │ │ │ │ ├── OptionT.Operators.Monad.cs │ │ │ │ │ ├── OptionT.Operators.SemigroupK.cs │ │ │ │ │ └── OptionT.Operators.cs │ │ │ │ ├── OptionT.Module.cs │ │ │ │ ├── OptionT.cs │ │ │ │ ├── Prelude/ │ │ │ │ │ └── OptionT.Prelude.mapapply.cs │ │ │ │ └── Trait/ │ │ │ │ └── OptionT.TraitImpl.cs │ │ │ ├── README.md │ │ │ ├── These/ │ │ │ │ ├── Extensions/ │ │ │ │ │ └── These.Extensions.cs │ │ │ │ ├── Operators/ │ │ │ │ │ ├── These.Operators.Functor.cs │ │ │ │ │ ├── These.Operators.SemigroupK.cs │ │ │ │ │ └── These.Operators.cs │ │ │ │ ├── Prelude/ │ │ │ │ │ └── These.Prelude.cs │ │ │ │ ├── These.Both.cs │ │ │ │ ├── These.Module.cs │ │ │ │ ├── These.That.cs │ │ │ │ ├── These.This.cs │ │ │ │ ├── These.cs │ │ │ │ └── Trait/ │ │ │ │ └── These.TraitImpl.cs │ │ │ ├── Try/ │ │ │ │ ├── Extensions/ │ │ │ │ │ ├── Try.Extensions.MapApply.cs │ │ │ │ │ └── Try.Extensions.cs │ │ │ │ ├── Operators/ │ │ │ │ │ ├── Try.Operators.Applicative.cs │ │ │ │ │ ├── Try.Operators.Choice.cs │ │ │ │ │ ├── Try.Operators.Fallible.cs │ │ │ │ │ ├── Try.Operators.Final.cs │ │ │ │ │ ├── Try.Operators.Functor.cs │ │ │ │ │ ├── Try.Operators.Monad.cs │ │ │ │ │ └── Try.Operators.cs │ │ │ │ ├── Prelude/ │ │ │ │ │ └── Try.Prelude.mapapply.cs │ │ │ │ ├── Trait/ │ │ │ │ │ └── Try.TraitImpl.cs │ │ │ │ ├── Try.Module.cs │ │ │ │ └── Try.cs │ │ │ ├── TryT/ │ │ │ │ ├── Extensions/ │ │ │ │ │ ├── TryT.Extensions.MapApply.cs │ │ │ │ │ └── TryT.Extensions.cs │ │ │ │ ├── Operators/ │ │ │ │ │ ├── TryT.Operators.Applicative.cs │ │ │ │ │ ├── TryT.Operators.Choice.cs │ │ │ │ │ ├── TryT.Operators.Fallible.cs │ │ │ │ │ ├── TryT.Operators.Functor.cs │ │ │ │ │ ├── TryT.Operators.Monad.cs │ │ │ │ │ └── TryT.Operators.cs │ │ │ │ ├── Prelude/ │ │ │ │ │ └── TryT.Prelude.mapapply.cs │ │ │ │ ├── Trait/ │ │ │ │ │ └── TryT.TraitImpl.cs │ │ │ │ ├── TryT.Module.cs │ │ │ │ └── TryT.cs │ │ │ ├── Validation/ │ │ │ │ ├── Extensions/ │ │ │ │ │ ├── Validation.Extensions.Apply.cs │ │ │ │ │ ├── Validation.Extensions.Map.cs │ │ │ │ │ ├── Validation.Extensions.cs │ │ │ │ │ └── Validation.Guard.cs │ │ │ │ ├── Operators/ │ │ │ │ │ ├── Validation.Operators.Applicative.cs │ │ │ │ │ ├── Validation.Operators.Choice.cs │ │ │ │ │ ├── Validation.Operators.Combine.cs │ │ │ │ │ ├── Validation.Operators.CombineSeq.cs │ │ │ │ │ ├── Validation.Operators.Fallible.cs │ │ │ │ │ ├── Validation.Operators.Functor.cs │ │ │ │ │ ├── Validation.Operators.Monad.cs │ │ │ │ │ └── Validation.Operators.cs │ │ │ │ ├── Prelude/ │ │ │ │ │ ├── Validation.Prelude.apply.cs │ │ │ │ │ ├── Validation.Prelude.cs │ │ │ │ │ └── Validation.Prelude.map.cs │ │ │ │ ├── Trait/ │ │ │ │ │ ├── Validation.TraitImpl.2.cs │ │ │ │ │ └── Validation.TraitImpl.cs │ │ │ │ ├── Validation.Fail.cs │ │ │ │ ├── Validation.Module.cs │ │ │ │ ├── Validation.Success.cs │ │ │ │ └── Validation.cs │ │ │ └── ValidationT/ │ │ │ ├── Extensions/ │ │ │ │ ├── ValidationT.Extensions.Apply.cs │ │ │ │ ├── ValidationT.Extensions.Map.cs │ │ │ │ ├── ValidationT.Extensions.cs │ │ │ │ └── ValidationT.Guard.cs │ │ │ ├── Operators/ │ │ │ │ ├── ValidationT.Operators.Applicative.cs │ │ │ │ ├── ValidationT.Operators.Choice.cs │ │ │ │ ├── ValidationT.Operators.Combine.cs │ │ │ │ ├── ValidationT.Operators.Fallible.cs │ │ │ │ ├── ValidationT.Operators.Functor.cs │ │ │ │ ├── ValidationT.Operators.Monad.cs │ │ │ │ └── ValidationT.Operators.cs │ │ │ ├── Prelude/ │ │ │ │ └── ValidationT.Prelude.mapapply.cs │ │ │ ├── Trait/ │ │ │ │ ├── ValidationT.TraitImpl.2.cs │ │ │ │ └── ValidationT.TraitImpl.cs │ │ │ ├── ValidationT.Module.cs │ │ │ └── ValidationT.cs │ │ ├── ContT/ │ │ │ ├── ContT.Module.cs │ │ │ ├── ContT.cs │ │ │ └── README.md │ │ ├── Free/ │ │ │ ├── Extensions/ │ │ │ │ ├── Free.Extensions.MapApply.cs │ │ │ │ └── Free.Extensions.cs │ │ │ ├── Free.Module.cs │ │ │ ├── Free.cs │ │ │ ├── Operators/ │ │ │ │ ├── Free.Operators.Applicative.cs │ │ │ │ ├── Free.Operators.Functor.cs │ │ │ │ ├── Free.Operators.Monad.cs │ │ │ │ └── Free.Operators.cs │ │ │ ├── Prelude/ │ │ │ │ └── Free.Prelude.mapapply.cs │ │ │ └── Trait/ │ │ │ └── Free.TraitImpl.cs │ │ ├── Identity/ │ │ │ ├── Extensions/ │ │ │ │ └── Identity.Extensions.cs │ │ │ ├── Identity.Module.cs │ │ │ ├── Identity.cs │ │ │ ├── Operators/ │ │ │ │ └── Identity.Operators.cs │ │ │ └── Trait/ │ │ │ └── Identity.TraitImpl.cs │ │ ├── IdentityT/ │ │ │ ├── IdentityT.Extensions.cs │ │ │ ├── IdentityT.Module.cs │ │ │ ├── IdentityT.Monad.cs │ │ │ └── IdentityT.cs │ │ ├── Lifting/ │ │ │ ├── Lift.Extensions.cs │ │ │ ├── Lift.Prelude.cs │ │ │ └── Lift.cs │ │ ├── Monadic conditionals/ │ │ │ ├── Prelude.guard.cs │ │ │ ├── Prelude.guardnot.cs │ │ │ ├── Prelude.iff.cs │ │ │ ├── Prelude.unless.cs │ │ │ ├── Prelude.when.cs │ │ │ └── README.md │ │ ├── Prelude.cs │ │ ├── README.md │ │ ├── State and Environment Monads/ │ │ │ ├── RWS/ │ │ │ │ └── RWST/ │ │ │ │ ├── Extensions/ │ │ │ │ │ └── RWST.Extensions.cs │ │ │ │ ├── Operators/ │ │ │ │ │ ├── RWST.Operators.Applicative.cs │ │ │ │ │ ├── RWST.Operators.Choice.cs │ │ │ │ │ ├── RWST.Operators.Fallible.cs │ │ │ │ │ ├── RWST.Operators.Final.cs │ │ │ │ │ ├── RWST.Operators.Functor.cs │ │ │ │ │ ├── RWST.Operators.Monad.cs │ │ │ │ │ ├── RWST.Operators.SemigroupK.cs │ │ │ │ │ └── RWST.Operators.cs │ │ │ │ ├── RWST.Module.cs │ │ │ │ ├── RWST.cs │ │ │ │ └── Trait/ │ │ │ │ └── RWST.TraitImpl.cs │ │ │ ├── Reader/ │ │ │ │ ├── Ask.cs │ │ │ │ ├── Reader/ │ │ │ │ │ ├── Extensions/ │ │ │ │ │ │ └── Reader.Extensions.cs │ │ │ │ │ ├── Operators/ │ │ │ │ │ │ ├── Reader.Operators.Applicative.cs │ │ │ │ │ │ ├── Reader.Operators.Functor.cs │ │ │ │ │ │ ├── Reader.Operators.Monad.cs │ │ │ │ │ │ └── Reader.Operators.cs │ │ │ │ │ ├── Reader.Module.cs │ │ │ │ │ ├── Reader.cs │ │ │ │ │ └── Trait/ │ │ │ │ │ └── Reader.TraitImpl.cs │ │ │ │ └── ReaderT/ │ │ │ │ ├── Extensions/ │ │ │ │ │ └── ReaderT.Extensions.cs │ │ │ │ ├── Operators/ │ │ │ │ │ ├── ReaderT.Operators.Applicative.cs │ │ │ │ │ ├── ReaderT.Operators.Choice.cs │ │ │ │ │ ├── ReaderT.Operators.Fallible.cs │ │ │ │ │ ├── ReaderT.Operators.Final.cs │ │ │ │ │ ├── ReaderT.Operators.Functor.cs │ │ │ │ │ ├── ReaderT.Operators.Monad.cs │ │ │ │ │ ├── ReaderT.Operators.SemigroupK.cs │ │ │ │ │ └── ReaderT.Operators.cs │ │ │ │ ├── Prelude/ │ │ │ │ │ └── ReaderT.Prelude.mapapply.cs │ │ │ │ ├── ReaderT.Module.cs │ │ │ │ ├── ReaderT.cs │ │ │ │ └── Trait/ │ │ │ │ └── ReaderT.TraitImpl.cs │ │ │ ├── State/ │ │ │ │ ├── PutGet.cs │ │ │ │ ├── State/ │ │ │ │ │ ├── Extensions/ │ │ │ │ │ │ └── State.Extensions.cs │ │ │ │ │ ├── Operators/ │ │ │ │ │ │ ├── State.Operators.Applicative.cs │ │ │ │ │ │ ├── State.Operators.Functor.cs │ │ │ │ │ │ ├── State.Operators.Monad.cs │ │ │ │ │ │ └── State.Operators.cs │ │ │ │ │ ├── State.Module.cs │ │ │ │ │ ├── State.cs │ │ │ │ │ └── Trait/ │ │ │ │ │ └── State.TraitImpl.cs │ │ │ │ └── StateT/ │ │ │ │ ├── Extensions/ │ │ │ │ │ └── StateT.Extensions.cs │ │ │ │ ├── Operators/ │ │ │ │ │ ├── StateT.Operators.Applicative.cs │ │ │ │ │ ├── StateT.Operators.Choice.cs │ │ │ │ │ ├── StateT.Operators.Fallible.cs │ │ │ │ │ ├── StateT.Operators.Final.cs │ │ │ │ │ ├── StateT.Operators.Functor.cs │ │ │ │ │ ├── StateT.Operators.Monad.cs │ │ │ │ │ ├── StateT.Operators.SemigroupK.cs │ │ │ │ │ └── StateT.Operators.cs │ │ │ │ ├── StateT.Module.cs │ │ │ │ ├── StateT.cs │ │ │ │ └── Trait/ │ │ │ │ └── StateT.TraitImpl.cs │ │ │ └── Writer/ │ │ │ ├── Tell.cs │ │ │ ├── Writer/ │ │ │ │ ├── Extensions/ │ │ │ │ │ └── Writer.Extensions.cs │ │ │ │ ├── Operators/ │ │ │ │ │ ├── Writer.Operators.Applicative.cs │ │ │ │ │ ├── Writer.Operators.Functor.cs │ │ │ │ │ ├── Writer.Operators.Monad.cs │ │ │ │ │ └── Writer.Operators.cs │ │ │ │ ├── Prelude/ │ │ │ │ │ └── Writer.Prelude.cs │ │ │ │ ├── Trait/ │ │ │ │ │ └── Writer.TraitImpl.cs │ │ │ │ ├── Writer.Module.cs │ │ │ │ └── Writer.cs │ │ │ └── WriterT/ │ │ │ ├── Extensions/ │ │ │ │ └── WriterT.Extensions.cs │ │ │ ├── Operators/ │ │ │ │ ├── WriterT.Operators.Applicative.cs │ │ │ │ ├── WriterT.Operators.Choice.cs │ │ │ │ ├── WriterT.Operators.Fallible.cs │ │ │ │ ├── WriterT.Operators.Final.cs │ │ │ │ ├── WriterT.Operators.Functor.cs │ │ │ │ ├── WriterT.Operators.Monad.cs │ │ │ │ ├── WriterT.Operators.SemigroupK.cs │ │ │ │ └── WriterT.Operators.cs │ │ │ ├── Trait/ │ │ │ │ └── WriterT.TraitImpl.cs │ │ │ ├── WriterT.Module.cs │ │ │ └── WriterT.cs │ │ └── Trampoline/ │ │ └── Trampoline.cs │ ├── Number.cs │ ├── Obsolete and Deprecated/ │ │ ├── Change.cs │ │ └── Fin.Prelude.cs │ ├── Opt.cs │ ├── Prelude/ │ │ ├── Currying and Partial Application/ │ │ │ ├── Prelude.Curry.cs │ │ │ ├── Prelude.PartialApplication.cs │ │ │ └── Prelude.Uncurry.cs │ │ ├── Function argument flipping/ │ │ │ └── Prelude.Flip.cs │ │ ├── Hash code functions/ │ │ │ └── Prelude.Hash.cs │ │ ├── Lambda function inference/ │ │ │ └── Prelude.Func.cs │ │ ├── Prelude.cs │ │ ├── README.md │ │ ├── Random/ │ │ │ └── Prelude.Random.cs │ │ ├── Resources/ │ │ │ └── Prelude.Resources.cs │ │ ├── Timer/ │ │ │ └── Prelude.Timer.cs │ │ └── Value parsing/ │ │ └── Prelude.Parse.cs │ ├── Pretty/ │ │ ├── Doc.cs │ │ ├── DocAnn.cs │ │ ├── DocStream.cs │ │ ├── Doc_A.cs │ │ ├── FastSpace.cs │ │ ├── FittingPredicate.cs │ │ ├── FlattenResult.cs │ │ ├── Layout.cs │ │ ├── LayoutOptions.cs │ │ ├── LayoutPipeline.cs │ │ └── PageWidth.cs │ ├── Prism/ │ │ ├── Prelude_Prism.cs │ │ ├── Prism.cs │ │ ├── PrismAB.cs │ │ └── README.md │ ├── README.md │ ├── README.nuget.md │ ├── Traits/ │ │ ├── Alternative/ │ │ │ ├── Alternative.Extensions.cs │ │ │ ├── Alternative.Laws.cs │ │ │ ├── Alternative.Module.cs │ │ │ ├── Alternative.Prelude.cs │ │ │ ├── Alternative.Trait.cs │ │ │ └── README.md │ │ ├── Applicative/ │ │ │ ├── Act.cs │ │ │ ├── Applicative.Laws.cs │ │ │ ├── Applicative.Trait.cs │ │ │ ├── Extensions/ │ │ │ │ ├── Applicative.Extensions.Action.cs │ │ │ │ ├── Applicative.Extensions.Apply.cs │ │ │ │ ├── Applicative.Extensions.ApplyM.cs │ │ │ │ ├── Applicative.Extensions.Arithmetic.cs │ │ │ │ ├── Applicative.Extensions.Lift.cs │ │ │ │ ├── Applicative.Extensions.Memo.Action.cs │ │ │ │ ├── Applicative.Extensions.Memo.Apply.Tuple.cs │ │ │ │ ├── Applicative.Extensions.Memo.Apply.cs │ │ │ │ ├── Applicative.Extensions.Memo.Arithmetic.cs │ │ │ │ ├── Applicative.Extensions.Memo.Lift.cs │ │ │ │ ├── Applicative.Extensions.Memo.Zip.cs │ │ │ │ ├── Applicative.Extensions.Zip.cs │ │ │ │ └── Applicative.Extensions.cs │ │ │ ├── Module/ │ │ │ │ ├── Applicative.Module.Action.cs │ │ │ │ ├── Applicative.Module.Apply.cs │ │ │ │ ├── Applicative.Module.ApplyM.cs │ │ │ │ ├── Applicative.Module.Arithmetic.cs │ │ │ │ ├── Applicative.Module.Lift.cs │ │ │ │ ├── Applicative.Module.Memo.Action.cs │ │ │ │ ├── Applicative.Module.Memo.Apply.cs │ │ │ │ ├── Applicative.Module.Memo.ApplyM.cs │ │ │ │ ├── Applicative.Module.Memo.Arithmetic.cs │ │ │ │ ├── Applicative.Module.Memo.Lift.cs │ │ │ │ ├── Applicative.Module.Zip.cs │ │ │ │ └── Applicative.Module.cs │ │ │ ├── Operators/ │ │ │ │ ├── Applicative.Memo.Operators.cs │ │ │ │ └── Applicative.Operators.cs │ │ │ ├── Prelude/ │ │ │ │ └── Applicative.Prelude.cs │ │ │ └── README.md │ │ ├── Arithmetic/ │ │ │ ├── Arithmetic.Prelude.cs │ │ │ └── Arithmetic.cs │ │ ├── Biapplicative/ │ │ │ └── Biapplicative.Trait.cs │ │ ├── Bifunctor/ │ │ │ ├── Bifunctor.Extensions.cs │ │ │ ├── Bifunctor.Module.cs │ │ │ └── Bifunctor.Trait.cs │ │ ├── Bimonad/ │ │ │ ├── Bimonad.Extensions.cs │ │ │ ├── Bimonad.Module.cs │ │ │ └── Bimonad.Trait.cs │ │ ├── Bool/ │ │ │ └── Bool.cs │ │ ├── Choice/ │ │ │ ├── Choice.Laws.cs │ │ │ ├── Choice.Module.cs │ │ │ ├── Choice.Trait.cs │ │ │ ├── Extensions/ │ │ │ │ └── Choice.Extensions.cs │ │ │ ├── Operators/ │ │ │ │ └── Choice.Operators.cs │ │ │ ├── Prelude/ │ │ │ │ └── Choice.Prelude.cs │ │ │ └── README.md │ │ ├── Chronicaler/ │ │ │ ├── Chronicaler.Extensions.cs │ │ │ ├── Chronicaler.Module.cs │ │ │ └── Chronicaler.Trait.cs │ │ ├── Cofunctor/ │ │ │ ├── Cofunctor.Extensions.cs │ │ │ ├── Cofunctor.Module.cs │ │ │ ├── Cofunctor.Prelude.cs │ │ │ └── Cofunctor.Trait.cs │ │ ├── Const/ │ │ │ └── Const.cs │ │ ├── Coproduct/ │ │ │ ├── Coproduct.Extensions.cs │ │ │ ├── Coproduct.Module.cs │ │ │ └── Coproduct.Trait.cs │ │ ├── CoproductCons/ │ │ │ ├── CoproductCons.Module.cs │ │ │ └── CoproductCons.Trait.cs │ │ ├── CoproductK/ │ │ │ ├── CoproductK.Extensions.cs │ │ │ ├── CoproductK.Module.cs │ │ │ └── CoproductK.Trait.cs │ │ ├── Coreadable/ │ │ │ ├── Coreadable.Extensions.cs │ │ │ ├── Coreadable.Module.cs │ │ │ └── Coreadable.Trait.cs │ │ ├── Decidable/ │ │ │ ├── Decidable.Module.cs │ │ │ ├── Decidable.Prelude.cs │ │ │ └── Decidable.Trait.cs │ │ ├── Deriving/ │ │ │ └── Deriving.cs │ │ ├── Divisible/ │ │ │ ├── Divisible.Module.cs │ │ │ ├── Divisible.Prelude.cs │ │ │ └── Divisible.Trait.cs │ │ ├── Domain/ │ │ │ ├── Amount.cs │ │ │ ├── DomainType.cs │ │ │ ├── Identifier.cs │ │ │ ├── Locus.cs │ │ │ ├── README.md │ │ │ └── VectorSpace.cs │ │ ├── Eq/ │ │ │ ├── Eq.Extensions.cs │ │ │ ├── Eq.Module.cs │ │ │ ├── Eq.Prelude.cs │ │ │ └── Eq.cs │ │ ├── Fallible/ │ │ │ ├── Fallible.Extensions.Catch.E.cs │ │ │ ├── Fallible.Extensions.Catch.cs │ │ │ ├── Fallible.Extensions.Fails.E.cs │ │ │ ├── Fallible.Extensions.Fails.cs │ │ │ ├── Fallible.Extensions.Partition.E.cs │ │ │ ├── Fallible.Extensions.Partition.cs │ │ │ ├── Fallible.Extensions.Succs.E.cs │ │ │ ├── Fallible.Extensions.Succs.cs │ │ │ ├── Fallible.Guard.cs │ │ │ ├── Fallible.Interface.cs │ │ │ ├── Fallible.Module.cs │ │ │ ├── Fallible.Operators.cs │ │ │ ├── Fallible.Prelude.Catch.E.cs │ │ │ ├── Fallible.Prelude.Catch.cs │ │ │ ├── Fallible.Prelude.Fails.E.cs │ │ │ ├── Fallible.Prelude.Fails.cs │ │ │ ├── Fallible.Prelude.Partition.E.cs │ │ │ ├── Fallible.Prelude.Partition.cs │ │ │ ├── Fallible.Prelude.Succs.E.cs │ │ │ ├── Fallible.Prelude.Succs.cs │ │ │ ├── Fallible.Prelude.cs │ │ │ └── Fallible.Trait.cs │ │ ├── Final/ │ │ │ ├── Final.Extensions.cs │ │ │ ├── Final.Module.cs │ │ │ ├── Final.Operators.cs │ │ │ ├── Final.Prelude.cs │ │ │ ├── Final.Trait.cs │ │ │ └── Finally.cs │ │ ├── Floating/ │ │ │ ├── Floating.Prelude.cs │ │ │ └── Floating.cs │ │ ├── Foldable/ │ │ │ ├── Fold.Module.cs │ │ │ ├── Fold.cs │ │ │ ├── Foldable.Extensions.cs │ │ │ ├── Foldable.ExtensionsT.cs │ │ │ ├── Foldable.Module.cs │ │ │ ├── Foldable.Prelude.cs │ │ │ └── Foldable.Trait.cs │ │ ├── Fraction/ │ │ │ ├── Fraction.Prelude.cs │ │ │ └── Fraction.cs │ │ ├── Functor/ │ │ │ ├── Extensions/ │ │ │ │ ├── Functor.Extensions.cs │ │ │ │ ├── Functor.ExtensionsT.cs │ │ │ │ └── Functor.MemoExtensions.cs │ │ │ ├── Functor.Laws.cs │ │ │ ├── Functor.Module.cs │ │ │ ├── Functor.Trait.cs │ │ │ ├── Operators/ │ │ │ │ ├── Functor.MemoOperators.cs │ │ │ │ └── Functor.Operators.cs │ │ │ └── Prelude/ │ │ │ └── Functor.Prelude.cs │ │ ├── Has/ │ │ │ ├── Has.Trait.cs │ │ │ └── Has.cs │ │ ├── Hashable/ │ │ │ ├── Hashable.Module.cs │ │ │ └── Hashable.Trait.cs │ │ ├── Identifiable/ │ │ │ ├── Identifiable.Module.cs │ │ │ ├── Identifiable.Operators.cs │ │ │ ├── Identifiable.Prelude.cs │ │ │ ├── Identifiable.cs │ │ │ └── Label.cs │ │ ├── Indexable/ │ │ │ └── Indexable.cs │ │ ├── K.Extensions.cs │ │ ├── K.cs │ │ ├── Local/ │ │ │ ├── Local.Module.cs │ │ │ └── Local.Trait.cs │ │ ├── Maybe Traits/ │ │ │ ├── MonadIO/ │ │ │ │ └── MonadIO.Trait.cs │ │ │ └── MonadUnliftIO/ │ │ │ ├── MonadUnliftIO.Extensions.cs │ │ │ └── MonadUnliftIO.Trait.cs │ │ ├── Monads/ │ │ │ ├── Monad/ │ │ │ │ ├── Monad.Extensions.cs │ │ │ │ ├── Monad.ExtensionsT.cs │ │ │ │ ├── Monad.Laws.cs │ │ │ │ ├── Monad.Module.cs │ │ │ │ ├── Monad.Operators.cs │ │ │ │ └── Monad.Trait.cs │ │ │ ├── MonadIO/ │ │ │ │ ├── MonadIO.Extensions.cs │ │ │ │ ├── MonadIO.Module.cs │ │ │ │ └── MonadIO.Trait.cs │ │ │ ├── MonadT/ │ │ │ │ ├── MonadT.Extensions.cs │ │ │ │ ├── MonadT.Module.cs │ │ │ │ └── MonadT.Trait.cs │ │ │ ├── MonadUnliftIO/ │ │ │ │ ├── MonadUnliftIO.Extensions.cs │ │ │ │ ├── MonadUnliftIO.Module.cs │ │ │ │ └── MonadUnliftIO.Trait.cs │ │ │ └── README.md │ │ ├── Monoid/ │ │ │ ├── Monoid.Instance.cs │ │ │ ├── Monoid.Prelude.cs │ │ │ └── Monoid.cs │ │ ├── MonoidK/ │ │ │ ├── MonoidK.Extensions.cs │ │ │ ├── MonoidK.Module.cs │ │ │ ├── MonoidK.cs │ │ │ └── README.md │ │ ├── Mutates/ │ │ │ ├── Mutates.Module.cs │ │ │ └── Mutates.Trait.cs │ │ ├── Natural/ │ │ │ ├── CoNatural.Module.cs │ │ │ ├── CoNatural.cs │ │ │ ├── Natural.Module.cs │ │ │ ├── Natural.cs │ │ │ ├── NaturalEpi.cs │ │ │ ├── NaturalIso.cs │ │ │ ├── NaturalMono.cs │ │ │ └── README.md │ │ ├── Num/ │ │ │ ├── Num.Prelude.cs │ │ │ └── Num.cs │ │ ├── Ord/ │ │ │ ├── Ord.Module.cs │ │ │ ├── Ord.Prelude.cs │ │ │ ├── Ord.cs │ │ │ └── OrdComparer.cs │ │ ├── Predicate/ │ │ │ └── Predicate.cs │ │ ├── README.md │ │ ├── Range/ │ │ │ └── Range.Trait.cs │ │ ├── Readable/ │ │ │ ├── Readable.Extensions.cs │ │ │ ├── Readable.Module.cs │ │ │ ├── Readable.Prelude.cs │ │ │ └── Readable.Trait.cs │ │ ├── Resolve/ │ │ │ ├── EqResolver.cs │ │ │ ├── HashableResolver.cs │ │ │ ├── OrdResolver.cs │ │ │ └── Resolver.cs │ │ ├── Semigroup/ │ │ │ ├── Semigroup.Instance.cs │ │ │ ├── Semigroup.Module.cs │ │ │ ├── Semigroup.Operators.cs │ │ │ ├── Semigroup.Prelude.cs │ │ │ └── Semigroup.cs │ │ ├── SemigroupK/ │ │ │ ├── SemigroupK.Extensions.cs │ │ │ ├── SemigroupK.Module.cs │ │ │ ├── SemigroupK.Operators.cs │ │ │ ├── SemigroupK.Prelude.cs │ │ │ └── SemigroupK.cs │ │ ├── Stateful/ │ │ │ ├── Stateful.Extensions.cs │ │ │ ├── Stateful.Module.cs │ │ │ └── Stateful.Trait.cs │ │ ├── TokenStream/ │ │ │ ├── TokenStream.Module.cs │ │ │ └── TokenStream.cs │ │ ├── Trait.cs │ │ ├── TraitAttribute.cs │ │ ├── Traversable/ │ │ │ ├── README.md │ │ │ ├── Traversable.Extensions.cs │ │ │ ├── Traversable.Module.cs │ │ │ └── Traversable.Trait.cs │ │ └── Writable/ │ │ ├── Writable.Extensions.cs │ │ ├── Writable.Module.cs │ │ └── Writable.Trait.cs │ ├── Units of Measure/ │ │ ├── Accel.cs │ │ ├── Area.cs │ │ ├── Length.cs │ │ ├── Mass.cs │ │ ├── Module.cs │ │ ├── Temperature.cs │ │ ├── Time.cs │ │ ├── TimeSq.cs │ │ ├── Velocity.cs │ │ └── VelocitySq.cs │ ├── Utility/ │ │ ├── AsyncEnumerableEx.cs │ │ ├── Box.cs │ │ ├── Check.cs │ │ ├── CollectionFormat.cs │ │ ├── Disposable.cs │ │ ├── EnumerableOptimal.cs │ │ ├── Fnv.cs │ │ ├── IL.cs │ │ ├── Inter.cs │ │ ├── Pool.cs │ │ ├── ReferenceEqualityComparer.cs │ │ ├── Reflect.cs │ │ ├── SysInfo.cs │ │ ├── TaskExt.cs │ │ └── WaitAsync.cs │ └── Void.cs ├── LanguageExt.FSharp/ │ ├── LanguageExt.FSharp.csproj │ ├── Prelude.cs │ └── README.nuget.md ├── LanguageExt.Megaparsec/ │ ├── Delegates.cs │ ├── ErrorFancy/ │ │ ├── ErrorFancy.Module.cs │ │ └── ErrorFancy.cs │ ├── ErrorItem/ │ │ ├── ErrorItem.Extensions.cs │ │ ├── ErrorItem.Module.cs │ │ ├── ErrorItem.cs │ │ └── ExpectedErrors.cs │ ├── Hints/ │ │ ├── Hints.Module.cs │ │ └── Hints.cs │ ├── LanguageExt.Megaparsec.csproj │ ├── LineText.cs │ ├── ModuleT/ │ │ ├── Expr.cs │ │ ├── Failure.cs │ │ ├── Lexer.cs │ │ ├── Prim.cs │ │ ├── State.cs │ │ ├── Text.cs │ │ └── Token.cs │ ├── MonadParsecT/ │ │ ├── MonadParsecT.Module.cs │ │ └── MonadParsecT.cs │ ├── Operator.cs │ ├── PString.cs │ ├── ParseError/ │ │ ├── ParseError.Module.cs │ │ └── ParseError.cs │ ├── ParsecT/ │ │ ├── DSL/ │ │ │ ├── Apply.cs │ │ │ ├── Bind.cs │ │ │ ├── Catch.cs │ │ │ ├── Choose.cs │ │ │ ├── DSL.cs │ │ │ ├── EOF.cs │ │ │ ├── Empty.cs │ │ │ ├── Error.cs │ │ │ ├── Fail.cs │ │ │ ├── Label.cs │ │ │ ├── Lift.cs │ │ │ ├── LookAhead.cs │ │ │ ├── MTransLift.cs │ │ │ ├── Map.cs │ │ │ ├── NoneOf.cs │ │ │ ├── NotFollowedBy.cs │ │ │ ├── Observing.cs │ │ │ ├── OneOf.cs │ │ │ ├── Pure.cs │ │ │ ├── Reader.cs │ │ │ ├── State.cs │ │ │ ├── Take.cs │ │ │ ├── TakeWhile.cs │ │ │ ├── TakeWhile1.cs │ │ │ ├── Token.cs │ │ │ ├── Tokens.cs │ │ │ ├── Try.cs │ │ │ └── WithRecovery.cs │ │ ├── Extensions/ │ │ │ └── ParsecTExtensions.cs │ │ ├── ParsecT.cs │ │ └── Trait/ │ │ └── ParsecT.TraitImpl.cs │ ├── Pos.cs │ ├── PosState.cs │ ├── Reach.cs │ ├── Reply/ │ │ ├── Extensions/ │ │ │ └── Reply.Extensions.cs │ │ ├── Reply.Module.cs │ │ ├── Reply.cs │ │ └── Trait/ │ │ └── Reply.TraitImpl.cs │ ├── Result/ │ │ ├── Extensions/ │ │ │ └── Result.Extensions.cs │ │ ├── Result.Module.cs │ │ ├── Result.cs │ │ └── Trait/ │ │ └── Result.TraitImpl.cs │ ├── SourcePos.cs │ └── State.cs ├── LanguageExt.Parsec/ │ ├── Common.cs │ ├── Exceptions.cs │ ├── GenLanguageDef.cs │ ├── GenTokenParser.cs │ ├── GenTokenParser2.cs │ ├── Language.cs │ ├── LanguageExt.Parsec.csproj │ ├── OperatorIOs.cs │ ├── Operators.cs │ ├── PString.cs │ ├── PStringIO.cs │ ├── Parsec.Internal.cs │ ├── ParsecIO.Internal.cs │ ├── Parser.cs │ ├── ParserError.cs │ ├── ParserIO.cs │ ├── ParserIOs/ │ │ ├── Expr.cs │ │ ├── Indent.cs │ │ ├── Item.cs │ │ └── Prim.cs │ ├── ParserResult.cs │ ├── ParserResultIO.cs │ ├── Parsers/ │ │ ├── Char.cs │ │ ├── Expr.cs │ │ ├── Indent.cs │ │ ├── Prim.cs │ │ ├── Token.cs │ │ └── Token2.cs │ ├── Pipes.cs │ ├── Pos.cs │ ├── README.nuget.md │ ├── Reply.cs │ ├── ReplyIO.cs │ ├── Sidedness.cs │ └── StringAndCollectionExt.cs ├── LanguageExt.Rx/ │ ├── Atom.Extensions.cs │ ├── Cast.cs │ ├── Check.cs │ ├── Eff.Extensions.cs │ ├── Either.Extensions.cs │ ├── EitherUnsafe.Extensions.cs │ ├── LanguageExt.Rx.csproj │ ├── Option.Extensions.cs │ ├── PreludeRx.cs │ ├── README.nuget.md │ └── Validation.Extensions.cs ├── LanguageExt.Streaming/ │ ├── Buffer.cs │ ├── Conduit/ │ │ ├── Conduit.Extensions.cs │ │ ├── Conduit.Module.cs │ │ ├── Conduit.TraitImpl.cs │ │ ├── Conduit.cs │ │ └── Internal/ │ │ └── Conduit.ABC.cs │ ├── ConduitT/ │ │ ├── ConduitT.Extensions.cs │ │ ├── ConduitT.Module.cs │ │ ├── ConduitT.TraitImpl.cs │ │ ├── ConduitT.cs │ │ └── Internal/ │ │ └── ConduitT.ABC.cs │ ├── Event/ │ │ ├── Event.Module.cs │ │ └── Event.cs │ ├── LanguageExt.Streaming.csproj │ ├── Pipes/ │ │ ├── Consumer/ │ │ │ ├── Consumer.Extensions.cs │ │ │ ├── Consumer.Module.cs │ │ │ ├── Consumer.Monad.cs │ │ │ ├── Consumer.Operators.cs │ │ │ └── Consumer.cs │ │ ├── ConsumerT/ │ │ │ ├── ConsumerT.Extensions.cs │ │ │ ├── ConsumerT.Module.cs │ │ │ ├── ConsumerT.Monad.cs │ │ │ ├── ConsumerT.Operators.cs │ │ │ └── ConsumerT.cs │ │ ├── Effect/ │ │ │ ├── Effect.Extensions.cs │ │ │ ├── Effect.Module.cs │ │ │ ├── Effect.Monad.cs │ │ │ ├── Effect.Operators.cs │ │ │ └── Effect.cs │ │ ├── EffectT/ │ │ │ ├── EffectT.Extensions.cs │ │ │ ├── EffectT.Module.cs │ │ │ ├── EffectT.Monad.cs │ │ │ ├── EffectT.Operators.cs │ │ │ └── EffectT.cs │ │ ├── Pipe/ │ │ │ ├── Pipe.Extensions.cs │ │ │ ├── Pipe.Module.cs │ │ │ ├── Pipe.Monad.cs │ │ │ ├── Pipe.Operators.cs │ │ │ └── Pipe.cs │ │ ├── PipeT/ │ │ │ ├── PipeT.Cached.cs │ │ │ ├── PipeT.DSL.cs │ │ │ ├── PipeT.Extensions.cs │ │ │ ├── PipeT.Module.cs │ │ │ ├── PipeT.Monad.cs │ │ │ ├── PipeT.Operators.cs │ │ │ └── PipeT.cs │ │ ├── Producer/ │ │ │ ├── Producer.Extensions.cs │ │ │ ├── Producer.Module.cs │ │ │ ├── Producer.Monad.cs │ │ │ ├── Producer.Operators.cs │ │ │ └── Producer.cs │ │ ├── ProducerT/ │ │ │ ├── ProducerT.Extensions.cs │ │ │ ├── ProducerT.Module.cs │ │ │ ├── ProducerT.Monad.cs │ │ │ ├── ProducerT.Operators.cs │ │ │ └── ProducerT.cs │ │ └── README.md │ ├── README.md │ ├── README.nuget.md │ ├── Sink/ │ │ ├── DSL/ │ │ │ ├── SinkChoose.cs │ │ │ ├── SinkCombine.cs │ │ │ ├── SinkContraMap.cs │ │ │ ├── SinkContraMapT.cs │ │ │ ├── SinkEmpty.cs │ │ │ ├── SinkVoid.cs │ │ │ └── SinkWriter.cs │ │ ├── Sink.CoFunctor.cs │ │ ├── Sink.Extensions.cs │ │ ├── Sink.Module.cs │ │ └── Sink.cs │ ├── SinkT/ │ │ ├── DSL/ │ │ │ ├── SinkChoose.cs │ │ │ ├── SinkCombine.cs │ │ │ ├── SinkContraMap.cs │ │ │ ├── SinkContraMapT.cs │ │ │ ├── SinkEmpty.cs │ │ │ ├── SinkVoid.cs │ │ │ └── SinkWriter.cs │ │ ├── SinkT.CoFunctor.cs │ │ ├── SinkT.Extensions.cs │ │ ├── SinkT.Module.cs │ │ └── SinkT.cs │ ├── Source/ │ │ ├── Extensions/ │ │ │ ├── Source.Extensions.cs │ │ │ └── Source.Reducers.cs │ │ ├── Operators/ │ │ │ ├── Source.Operators.Applicative.cs │ │ │ ├── Source.Operators.Choice.cs │ │ │ ├── Source.Operators.Combine.cs │ │ │ ├── Source.Operators.Functor.cs │ │ │ ├── Source.Operators.Monad.cs │ │ │ ├── Source.Operators.Zip.cs │ │ │ └── Source.Operators.cs │ │ ├── Source.Module.cs │ │ ├── Source.cs │ │ └── Trait/ │ │ └── Source.TraitImpl.cs │ ├── SourceT/ │ │ ├── DSL/ │ │ │ ├── ApplySourceT.cs │ │ │ ├── BindSourceT.cs │ │ │ ├── ChooseSourceT.cs │ │ │ ├── CombineSourceT.cs │ │ │ ├── DoneSourceT.cs │ │ │ ├── EmptySourceT.cs │ │ │ ├── FilterSourceT.cs │ │ │ ├── FoldUntilSourceT.cs │ │ │ ├── FoldWhileSourceT.cs │ │ │ ├── FoldablePureSourceT.cs │ │ │ ├── FoldableSourceT.cs │ │ │ ├── ForeverSourceT.cs │ │ │ ├── IteratorAsyncSourceT.cs │ │ │ ├── IteratorSyncSourceT.cs │ │ │ ├── LiftSourceT.cs │ │ │ ├── MapIOSourceT.cs │ │ │ ├── MapSourceT.cs │ │ │ ├── MultiListenerPureSourceT.cs │ │ │ ├── MultiListenerSourceT.cs │ │ │ ├── ObservablePureSourceT.cs │ │ │ ├── ObservableSourceT.cs │ │ │ ├── PureSourceT.cs │ │ │ ├── Reader2SourceT.cs │ │ │ ├── Reader3SourceT.cs │ │ │ ├── Reader4SourceT.cs │ │ │ ├── SourcePureSourceT.cs │ │ │ ├── SourceSourceT.cs │ │ │ ├── TakeForSourceT.cs │ │ │ ├── TakeSourceT.cs │ │ │ ├── ToIOSourceT.cs │ │ │ ├── TransformSourceT.cs │ │ │ ├── Zip2SourceT.cs │ │ │ ├── Zip3SourceT.cs │ │ │ └── Zip4SourceT.cs │ │ ├── Extensions/ │ │ │ ├── SourceT.Combinators.cs │ │ │ ├── SourceT.Extensions.cs │ │ │ └── SourceT.Reducers.cs │ │ ├── Operators/ │ │ │ ├── SourceT.Operators.Applicative.cs │ │ │ ├── SourceT.Operators.Choice.cs │ │ │ ├── SourceT.Operators.Combine.cs │ │ │ ├── SourceT.Operators.Functor.cs │ │ │ ├── SourceT.Operators.Monad.cs │ │ │ ├── SourceT.Operators.Zip.cs │ │ │ └── SourceT.Operators.cs │ │ ├── SourceT.Module.cs │ │ ├── SourceT.cs │ │ └── Trait/ │ │ └── SourceT.TraitImpl.cs │ └── Transducers/ │ ├── Reduced.cs │ ├── ReducedM.cs │ ├── Reducer.cs │ ├── ReducerIO.cs │ ├── ReducerM.cs │ ├── Transducer/ │ │ ├── DSL/ │ │ │ ├── BindTransducer.cs │ │ │ ├── ComposeTransducer.cs │ │ │ ├── ConstTransducer.cs │ │ │ ├── FilterTransducer.cs │ │ │ ├── FoldUntilTransducer.cs │ │ │ ├── FoldWhileTransducer.cs │ │ │ ├── IdentityTransducer.cs │ │ │ ├── MapTransducer.cs │ │ │ ├── SelectManyTransducer.cs │ │ │ ├── SkipTransducer.cs │ │ │ └── TakeTransducer.cs │ │ ├── TransduceFrom.TraitImpl.cs │ │ ├── TransduceTo.TraitImpl.cs │ │ ├── Transducer.Extensions.cs │ │ ├── Transducer.Module.cs │ │ └── Transducer.cs │ └── TransducerM/ │ ├── DSL/ │ │ ├── BindTransducer.cs │ │ ├── ComposeTransducer.cs │ │ ├── ConstTransducer.cs │ │ ├── FilterTransducer.cs │ │ ├── FoldUntilTransducer.cs │ │ ├── FoldWhileTransducer.cs │ │ ├── IdentityTransducer.cs │ │ ├── MapTransducer.cs │ │ ├── SelectManyTransducer.cs │ │ ├── SkipTransducer.cs │ │ └── TakeTransducer.cs │ ├── TransduceFromM.TraitImpl.cs │ ├── TransduceToM.TraitImpl.cs │ ├── TransducerM.Extensions.cs │ ├── TransducerM.Module.cs │ └── TransducerM.cs ├── LanguageExt.Sys/ │ ├── ActivityEnv.cs │ ├── EffOpt.cs │ ├── LanguageExt.Sys.csproj │ ├── Live/ │ │ ├── Implementations/ │ │ │ ├── ActivitySourceIO.cs │ │ │ ├── ConsoleIO.cs │ │ │ ├── DirectoryIO.cs │ │ │ ├── EncodingIO.cs │ │ │ ├── EnvironmentIO.cs │ │ │ ├── FileIO.cs │ │ │ ├── TextReadIO.cs │ │ │ └── TimeIO.cs │ │ └── Runtime.cs │ ├── MemoryConsole.cs │ ├── MemorySystemEnvironment.cs │ ├── README.nuget.md │ ├── Sys/ │ │ ├── Console.Eff.cs │ │ ├── Console.cs │ │ ├── Diag/ │ │ │ ├── Activity.Eff.cs │ │ │ └── Activity.cs │ │ ├── Encoding.Eff.cs │ │ ├── Encoding.cs │ │ ├── Environment.Eff.cs │ │ ├── Environment.cs │ │ ├── IO/ │ │ │ ├── Directory.Eff.cs │ │ │ ├── Directory.cs │ │ │ ├── File.Eff.cs │ │ │ ├── File.cs │ │ │ ├── Stream.cs │ │ │ ├── TextRead.Eff.cs │ │ │ └── TextRead.cs │ │ ├── Time.Eff.cs │ │ └── Time.cs │ ├── Test/ │ │ ├── Implementations/ │ │ │ ├── ConsoleIO.cs │ │ │ ├── DirectoryIO.cs │ │ │ ├── EnvironmentIO.cs │ │ │ ├── FileIO.cs │ │ │ ├── TestTimeSpec.cs │ │ │ ├── TextReadIO.cs │ │ │ └── TimeIO.cs │ │ └── Runtime.cs │ └── Traits/ │ ├── ActivitySourceIO.cs │ ├── ConsoleIO.cs │ ├── DirectoryIO.cs │ ├── EncodingIO.cs │ ├── EnvironmentIO.cs │ ├── FileIO.cs │ ├── SysIO.cs │ ├── TextReadIO.cs │ └── TimeIO.cs ├── LanguageExt.Tests/ │ ├── AffTests.cs │ ├── ArrayTests.cs │ ├── AtomHashMapEqTests.cs │ ├── AtomHashMapTests.cs │ ├── AtomTests.cs │ ├── ChoiceTests.cs │ ├── CollectionOrderingTests.cs │ ├── CollectionToStringTests.cs │ ├── CombinatorsTests.cs │ ├── CompositionTests.cs │ ├── DefaultValueChecks/ │ │ ├── AbstractDefaultValueCheckTests.cs │ │ ├── isDefaultPreludeTests.cs │ │ └── notDefaultPreludeTests.cs │ ├── DelayTests.cs │ ├── DistinctTests.cs │ ├── Divisible.cs │ ├── EitherApply.cs │ ├── EitherCoalesceTests.cs │ ├── EitherTests.cs │ ├── EnumerableTTests.cs │ ├── EqualityTests.cs │ ├── ErrorTests.cs │ ├── FSharp/ │ │ └── FSharpTests.cs │ ├── FunTests.cs │ ├── GlobalSuppressions.cs │ ├── GlobalUsings.cs │ ├── HashMapTests.cs │ ├── HashSetTests.cs │ ├── IOTests/ │ │ ├── ApplyTests.cs │ │ ├── FoldTests.cs │ │ ├── GeneralTests.cs │ │ ├── MapFailTests.cs │ │ ├── RepeatTests.cs │ │ ├── RetryTests.cs │ │ └── TailRecursionTests.cs │ ├── IssuesTests.cs │ ├── IteratorTests/ │ │ └── IteratorTests.cs │ ├── LanguageExt.Tests.csproj │ ├── LensTests.cs │ ├── LinqTests.cs │ ├── ListMatchingTests.cs │ ├── ListTests.cs │ ├── MapTests.cs │ ├── MemoImplTests.cs │ ├── MemoryConsoleTests.cs │ ├── MemoryFSTests.cs │ ├── MonadTests.cs │ ├── Multiplicable.cs │ ├── NullChecks/ │ │ ├── AbstractNullCheckTests.cs │ │ ├── IsNullExtensionTests.cs │ │ ├── isnullPreludeTests.cs │ │ └── notnullPreludeTests.cs │ ├── OptionApply.cs │ ├── OptionCoalesceTests.cs │ ├── OptionLazy.cs │ ├── OptionTTests.cs │ ├── OptionTests.cs │ ├── OptionUnsafeApply.cs │ ├── ParsecIOTests.cs │ ├── ParsecTests.cs │ ├── Parsing/ │ │ ├── AbstractParseTPrecisionIntervalTests.cs │ │ ├── AbstractParseTSignedPrecisionIntervalTests.cs │ │ ├── AbstractParseTTests.cs │ │ ├── AbstractParseTUnsignedPrecisionIntervalTests.cs │ │ ├── parseBoolTests.cs │ │ ├── parseByteTests.cs │ │ ├── parseCharTests.cs │ │ ├── parseDateTimeOffsetTests.cs │ │ ├── parseDateTimeTests.cs │ │ ├── parseDecimalTests.cs │ │ ├── parseDoubleTests.cs │ │ ├── parseEnumTests.cs │ │ ├── parseFloatTests.cs │ │ ├── parseGuidTests.cs │ │ ├── parseIntTests.cs │ │ ├── parseLongTests.cs │ │ ├── parseSByteTests.cs │ │ ├── parseShortTests.cs │ │ ├── parseTimeSpanTests.cs │ │ ├── parseUIntTests.cs │ │ ├── parseULongTests.cs │ │ └── parseUShortTests.cs │ ├── PartialAndCurryingTests.cs │ ├── PatchTests.cs │ ├── PipesTests.cs │ ├── PrismTests.cs │ ├── QueryTests.cs │ ├── QueueTests.cs │ ├── RangeTests.cs │ ├── Read-Me.cs │ ├── RecordIgnoreBaseTests.cs │ ├── RecordTests.cs │ ├── RecordTypesTest.cs │ ├── RefTest.cs │ ├── ReflectTests.cs │ ├── ScheduleTest/ │ │ ├── EffTests1.cs │ │ ├── PositiveDurationTests.cs │ │ └── ScheduleTests.cs │ ├── SeqTypes/ │ │ ├── Seq.Arr.Tests.cs │ │ ├── Seq.Cons.Tests.cs │ │ ├── Seq.Enumerable.Tests.cs │ │ ├── Seq.IList.Tests.cs │ │ ├── Seq.Lazy.Tests.cs │ │ ├── Seq.Lst.Tests.cs │ │ ├── Seq.Module.Tests.cs │ │ ├── SeqListTests.cs │ │ └── SeqTests.cs │ ├── SerialisationTests.cs │ ├── SetTests.cs │ ├── StackTests.cs │ ├── Streaming/ │ │ ├── Source.Tests.cs │ │ └── SourceT.Tests.cs │ ├── Sys/ │ │ ├── Diag/ │ │ │ └── ActivityTests.cs │ │ └── IO/ │ │ └── DirectoryTests.cs │ ├── TESTING.cs │ ├── TrackingHashMapTests.cs │ ├── TraitTests/ │ │ ├── AlternativeLawTests.cs │ │ ├── ApplicativeLawTests.cs │ │ ├── ChoiceLawTests.cs │ │ ├── FoldableDefaultsTests.cs │ │ ├── FunctorLawTests.cs │ │ └── MonadLawsTest.cs │ ├── Transformer/ │ │ └── Traverse/ │ │ ├── Arr/ │ │ │ ├── Collections/ │ │ │ │ ├── Arr.cs │ │ │ │ ├── HashSet.cs │ │ │ │ ├── IEnumerable.cs │ │ │ │ ├── Lst.cs │ │ │ │ ├── Seq.cs │ │ │ │ └── Set.cs │ │ │ └── Sync/ │ │ │ ├── Either.cs │ │ │ ├── Identity.cs │ │ │ ├── Option.cs │ │ │ └── Validation.cs │ │ ├── Either/ │ │ │ ├── Collections/ │ │ │ │ ├── Arr.cs │ │ │ │ ├── HashSet.cs │ │ │ │ ├── IEnumerable.cs │ │ │ │ ├── Lst.cs │ │ │ │ ├── Seq.cs │ │ │ │ └── Set.cs │ │ │ └── Sync/ │ │ │ ├── Either.cs │ │ │ ├── Identity.cs │ │ │ ├── Option.cs │ │ │ └── ValidationSeq.cs │ │ ├── HashSet/ │ │ │ ├── Collections/ │ │ │ │ ├── Arr.cs │ │ │ │ ├── HashSet.cs │ │ │ │ ├── Lst.cs │ │ │ │ ├── Seq.cs │ │ │ │ └── Set.cs │ │ │ └── Sync/ │ │ │ ├── Either.cs │ │ │ ├── Identity.cs │ │ │ ├── Option.cs │ │ │ └── ValidationSeq.cs │ │ ├── IEnumerable/ │ │ │ ├── Collections/ │ │ │ │ ├── Arr.cs │ │ │ │ └── HashSet.cs │ │ │ └── Sync/ │ │ │ ├── Either.cs │ │ │ ├── Identity.cs │ │ │ ├── Option.cs │ │ │ └── ValidationSeq.cs │ │ ├── Identity/ │ │ │ ├── Collections/ │ │ │ │ ├── Arr.cs │ │ │ │ ├── HashSet.cs │ │ │ │ ├── IEnumerable.cs │ │ │ │ ├── Lst.cs │ │ │ │ ├── Seq.cs │ │ │ │ └── Set.cs │ │ │ └── Sync/ │ │ │ ├── Either.cs │ │ │ ├── Identity.cs │ │ │ ├── Option.cs │ │ │ └── ValidationSeq.cs │ │ ├── Lst/ │ │ │ ├── Collections/ │ │ │ │ ├── Arr.cs │ │ │ │ ├── HashSet.cs │ │ │ │ ├── IEnumerable.cs │ │ │ │ ├── Lst.cs │ │ │ │ ├── Seq.cs │ │ │ │ └── Set.cs │ │ │ └── Sync/ │ │ │ ├── Either.cs │ │ │ ├── Identity.cs │ │ │ ├── Option.cs │ │ │ └── ValidationSeq.cs │ │ ├── Option/ │ │ │ ├── Collections/ │ │ │ │ ├── Arr.cs │ │ │ │ ├── HashSet.cs │ │ │ │ ├── IEnumerable.cs │ │ │ │ ├── Lst.cs │ │ │ │ ├── Seq.cs │ │ │ │ └── Set.cs │ │ │ └── Sync/ │ │ │ ├── Either.cs │ │ │ ├── Identity.cs │ │ │ ├── Option.cs │ │ │ └── ValidationSeq.cs │ │ ├── SeqT/ │ │ │ ├── Collections/ │ │ │ │ ├── Arr.cs │ │ │ │ ├── HashSet.cs │ │ │ │ ├── IEnumerable.cs │ │ │ │ ├── Lst.cs │ │ │ │ ├── Seq.cs │ │ │ │ └── Set.cs │ │ │ └── Sync/ │ │ │ ├── Either.cs │ │ │ ├── Identity.cs │ │ │ ├── Option.cs │ │ │ ├── Validation.cs │ │ │ └── ValidationWithMonoid.cs │ │ ├── SetT/ │ │ │ ├── Collections/ │ │ │ │ └── Arr.cs │ │ │ └── Sync/ │ │ │ ├── Either.cs │ │ │ ├── Identity.cs │ │ │ ├── Option.cs │ │ │ └── ValidationSeq.cs │ │ └── Validation/ │ │ ├── Collections/ │ │ │ ├── Arr.cs │ │ │ ├── HashSet.cs │ │ │ ├── IEnumerable.cs │ │ │ ├── Lst.cs │ │ │ ├── Seq.cs │ │ │ └── Set.cs │ │ └── Sync/ │ │ ├── Either.cs │ │ ├── Identity.cs │ │ ├── Option.cs │ │ └── ValidationSeq.cs │ ├── TupleTests.cs │ ├── UnionCustomJsonSerializerTests.cs │ ├── UnionJsonSerializerTests.cs │ ├── UnionTests.cs │ ├── UnitsOfMeasureTests.cs │ ├── ValidationTests.cs │ ├── VectorClockTests.cs │ ├── VersionHashMapTests.cs │ └── WithTests.cs ├── LanguageExt.XUnitExt/ │ ├── EffExtensions.cs │ ├── EitherExtensions.cs │ ├── ErrorExtensions.cs │ ├── FinExtensions.cs │ ├── IOExtensions.cs │ ├── LanguageExt.XUnitExt.csproj │ ├── OptionExtensions.cs │ ├── README.nuget.md │ └── SourceExtensions.cs ├── Major Version Release Notes/ │ ├── Version 2/ │ │ ├── README.md │ │ └── version-2-migration-notes.md │ └── Version 5/ │ ├── Migration War Stories/ │ │ └── README.md │ └── README.md ├── Performance.md ├── README.md ├── Samples/ │ ├── BlazorApp/ │ │ ├── BlazorApp.csproj │ │ ├── Components/ │ │ │ ├── App.razor │ │ │ ├── Layout/ │ │ │ │ ├── MainLayout.razor │ │ │ │ ├── MainLayout.razor.css │ │ │ │ ├── NavMenu.razor │ │ │ │ └── NavMenu.razor.css │ │ │ ├── Pages/ │ │ │ │ ├── Counter.razor │ │ │ │ ├── Error.razor │ │ │ │ ├── Home.razor │ │ │ │ ├── Page.cs │ │ │ │ └── Weather.razor │ │ │ ├── Routes.razor │ │ │ └── _Imports.razor │ │ ├── Control/ │ │ │ └── Weather.cs │ │ ├── Data/ │ │ │ └── WeatherForecast.cs │ │ ├── Effects/ │ │ │ ├── AppRuntime.cs │ │ │ ├── Impl/ │ │ │ │ └── RndImpl.cs │ │ │ ├── Interfaces/ │ │ │ │ └── RndIO.cs │ │ │ ├── Rnd.cs │ │ │ ├── Runtime.cs │ │ │ └── SafeErrorExtensions.cs │ │ ├── Program.cs │ │ ├── Properties/ │ │ │ └── launchSettings.json │ │ ├── appsettings.Development.json │ │ ├── appsettings.json │ │ └── wwwroot/ │ │ └── app.css │ ├── CardGame/ │ │ ├── Card.cs │ │ ├── CardGame.csproj │ │ ├── Console.cs │ │ ├── Deck.cs │ │ ├── Display.cs │ │ ├── Game.Monad/ │ │ │ ├── Game.Extensions.cs │ │ │ ├── Game.Module.cs │ │ │ ├── Game.Monad.cs │ │ │ └── Game.cs │ │ ├── Game.cs │ │ ├── GameState.cs │ │ ├── Player.cs │ │ ├── PlayerState.cs │ │ ├── Players.cs │ │ └── Program.cs │ ├── CreditCardValidation/ │ │ ├── Control/ │ │ │ └── CreditCard.cs │ │ ├── CreditCardValidation.csproj │ │ ├── Data/ │ │ │ ├── Base12.cs │ │ │ ├── CVV.cs │ │ │ ├── CardNumber.cs │ │ │ ├── CreditCardDetails.cs │ │ │ ├── Expiry.cs │ │ │ └── MonthSpan.cs │ │ └── Program.cs │ ├── DomainTypesExamples/ │ │ ├── Dimension.cs │ │ ├── DomainTypesExamples.csproj │ │ ├── Program.cs │ │ ├── Time.cs │ │ ├── TimeSpan.cs │ │ └── Vector.cs │ ├── EffectsExamples/ │ │ ├── EffectsExamples.csproj │ │ ├── Examples/ │ │ │ ├── CancelExample.cs │ │ │ ├── ErrorAndGuardExample.cs │ │ │ ├── FoldTest.cs │ │ │ ├── ForkCancelExample.cs │ │ │ ├── QueueExample.cs │ │ │ ├── RetryExample.cs │ │ │ ├── TextFileChunkStreamExample.cs │ │ │ ├── TextFileLineStreamExample.cs │ │ │ ├── TimeExample.cs │ │ │ └── TimeoutExample.cs │ │ ├── Menu.cs │ │ └── Program.cs │ ├── IOExmples/ │ │ ├── IOExmples.csproj │ │ └── Program.cs │ ├── Newsletter/ │ │ ├── Newsletter/ │ │ │ ├── Command/ │ │ │ │ ├── Email.cs │ │ │ │ ├── Members.cs │ │ │ │ ├── Newsletter.cs │ │ │ │ ├── Posts.cs │ │ │ │ ├── Send.cs │ │ │ │ └── Templates.cs │ │ │ ├── Data/ │ │ │ │ ├── Letter.cs │ │ │ │ ├── Members.cs │ │ │ │ ├── Posts.cs │ │ │ │ └── Templates.cs │ │ │ ├── Effects/ │ │ │ │ ├── Config.cs │ │ │ │ ├── Email.cs │ │ │ │ ├── Image.cs │ │ │ │ ├── Impl/ │ │ │ │ │ ├── Email.cs │ │ │ │ │ ├── Image.cs │ │ │ │ │ ├── Json.cs │ │ │ │ │ └── Web.cs │ │ │ │ ├── Json.cs │ │ │ │ ├── Runtime.cs │ │ │ │ ├── RuntimeEnv.cs │ │ │ │ ├── Traits/ │ │ │ │ │ ├── EmailIO.cs │ │ │ │ │ ├── ImageIO.cs │ │ │ │ │ ├── JsonIO.cs │ │ │ │ │ └── WebIO.cs │ │ │ │ └── Web.cs │ │ │ ├── GlobalUsings.cs │ │ │ ├── JsonExtensions.cs │ │ │ ├── Newsletter.csproj │ │ │ ├── Newsletter.sln │ │ │ ├── Program.cs │ │ │ └── UI/ │ │ │ └── Display.cs │ │ ├── README.md │ │ ├── members-test/ │ │ │ └── members.2024-07-29.csv │ │ └── templates/ │ │ ├── email.html │ │ ├── email.txt │ │ ├── recent-item.html │ │ └── recent-item.txt │ ├── PipesExamples/ │ │ ├── PipesExamples.csproj │ │ └── Program.cs │ ├── Streams/ │ │ ├── Console.cs │ │ ├── CountForever.cs │ │ ├── CountForeverAsync.cs │ │ ├── Folding.cs │ │ ├── Grouping.cs │ │ ├── HeadsAndTails.cs │ │ ├── Menu.cs │ │ ├── Merging.cs │ │ ├── OptionalItems.cs │ │ ├── Program.cs │ │ ├── RecursionIO.cs │ │ ├── SourceStream.cs │ │ ├── Streams.csproj │ │ ├── SumOfSquares.cs │ │ └── Zipping.cs │ ├── TestBed/ │ │ ├── ApplicativeTest.cs │ │ ├── AtomHashMapTests.cs │ │ ├── AwaitAnyTest.cs │ │ ├── BracketTest.cs │ │ ├── FreeTests.cs │ │ ├── Issues/ │ │ │ ├── Discussion1527.cs │ │ │ ├── Issue1497.cs │ │ │ └── IssueTests.cs │ │ ├── PipesTest.cs │ │ ├── Program.cs │ │ ├── RWSTTests.cs │ │ ├── RecurTests.cs │ │ ├── ResourcesDiscussion1366.cs │ │ ├── RobotExample.cs │ │ ├── ScheduleTests.cs │ │ ├── SeqConstructTests.cs │ │ ├── SequenceParallelTest.cs │ │ ├── SourceTTests.cs │ │ ├── StateAsync.cs │ │ ├── StateEff.cs │ │ ├── TestBed.csproj │ │ ├── TestCodeGen.cs │ │ └── UseTest.cs │ ├── TestBed.WPF/ │ │ ├── App.xaml │ │ ├── App.xaml.cs │ │ ├── AssemblyInfo.cs │ │ ├── AtomIO.cs │ │ ├── MainWindow.xaml │ │ ├── MainWindow.xaml.cs │ │ ├── TestBed.WPF.csproj │ │ ├── WindowIO.cs │ │ └── WindowRT.cs │ ├── TestBed.Web/ │ │ ├── Program.cs │ │ └── TestBed.Web.csproj │ └── TestBed.Web.Runner/ │ ├── Program.cs │ └── TestBed.Web.Runner.csproj ├── clean.sh ├── docs.sh ├── inc.bat ├── language-ext.sln ├── language-ext.sln.DotSettings ├── pack.bat ├── pack.sh └── pjv/ └── LanguageExt.Core.3.1.9-beta.nuspec ================================================ FILE CONTENTS ================================================ ================================================ FILE: .editorconfig ================================================ root = true [*.cs] # http://kent-boogaart.com/blog/editorconfig-reference-for-c-developers#csharp_indent_block_contents csharp_indent_block_contents = true # http://kent-boogaart.com/blog/editorconfig-reference-for-c-developers#csharp_indent_braces csharp_indent_braces = false # http://kent-boogaart.com/blog/editorconfig-reference-for-c-developers#csharp_indent_case_contents csharp_indent_case_contents = true # http://kent-boogaart.com/blog/editorconfig-reference-for-c-developers#csharp_indent_switch_labels csharp_indent_switch_labels = true # http://kent-boogaart.com/blog/editorconfig-reference-for-c-developers#csharp_new_line_before_catch csharp_new_line_before_catch = true # http://kent-boogaart.com/blog/editorconfig-reference-for-c-developers#csharp_new_line_before_else csharp_new_line_before_else = true # http://kent-boogaart.com/blog/editorconfig-reference-for-c-developers#csharp_new_line_before_finally csharp_new_line_before_finally = true # http://kent-boogaart.com/blog/editorconfig-reference-for-c-developers#csharp_new_line_before_members_in_anonymous_types csharp_new_line_before_members_in_anonymous_types = true # http://kent-boogaart.com/blog/editorconfig-reference-for-c-developers#csharp_new_line_before_members_in_object_initializers csharp_new_line_before_members_in_object_initializers = true # http://kent-boogaart.com/blog/editorconfig-reference-for-c-developers#csharp_new_line_before_open_brace csharp_new_line_before_open_brace = all # http://kent-boogaart.com/blog/editorconfig-reference-for-c-developers#csharp_space_after_cast csharp_space_after_cast = false # http://kent-boogaart.com/blog/editorconfig-reference-for-c-developers#csharp_space_after_colon_in_inheritance_clause csharp_space_after_colon_in_inheritance_clause = true # http://kent-boogaart.com/blog/editorconfig-reference-for-c-developers#csharp_space_after_comma csharp_space_after_comma = true # http://kent-boogaart.com/blog/editorconfig-reference-for-c-developers#csharp_space_after_dot csharp_space_after_dot = false # http://kent-boogaart.com/blog/editorconfig-reference-for-c-developers#csharp_space_after_keywords_in_control_flow_statements csharp_space_after_keywords_in_control_flow_statements = true # http://kent-boogaart.com/blog/editorconfig-reference-for-c-developers#csharp_space_after_semicolon_in_for_statement csharp_space_after_semicolon_in_for_statement = true # http://kent-boogaart.com/blog/editorconfig-reference-for-c-developers#csharp_space_around_binary_operators csharp_space_around_binary_operators = before_and_after # http://kent-boogaart.com/blog/editorconfig-reference-for-c-developers#csharp_space_before_colon_in_inheritance_clause csharp_space_before_colon_in_inheritance_clause = true # http://kent-boogaart.com/blog/editorconfig-reference-for-c-developers#csharp_space_before_comma csharp_space_before_comma = false # http://kent-boogaart.com/blog/editorconfig-reference-for-c-developers#csharp_space_before_dot csharp_space_before_dot = false # http://kent-boogaart.com/blog/editorconfig-reference-for-c-developers#csharp_space_before_open_square_brackets csharp_space_before_open_square_brackets = false # http://kent-boogaart.com/blog/editorconfig-reference-for-c-developers#csharp_space_before_semicolon_in_for_statement csharp_space_before_semicolon_in_for_statement = false # http://kent-boogaart.com/blog/editorconfig-reference-for-c-developers#csharp_space_between_empty_square_brackets csharp_space_between_empty_square_brackets = false # http://kent-boogaart.com/blog/editorconfig-reference-for-c-developers#csharp_space_between_method_call_empty_parameter_list_parentheses csharp_space_between_method_call_empty_parameter_list_parentheses = false # http://kent-boogaart.com/blog/editorconfig-reference-for-c-developers#csharp_space_between_method_call_name_and_opening_parenthesis csharp_space_between_method_call_name_and_opening_parenthesis = false # http://kent-boogaart.com/blog/editorconfig-reference-for-c-developers#csharp_space_between_method_call_parameter_list_parentheses csharp_space_between_method_call_parameter_list_parentheses = false # http://kent-boogaart.com/blog/editorconfig-reference-for-c-developers#csharp_space_between_method_declaration_empty_parameter_list_parentheses csharp_space_between_method_declaration_empty_parameter_list_parentheses = false # http://kent-boogaart.com/blog/editorconfig-reference-for-c-developers#csharp_space_between_method_declaration_name_and_open_parenthesis csharp_space_between_method_declaration_name_and_open_parenthesis = false # http://kent-boogaart.com/blog/editorconfig-reference-for-c-developers#csharp_space_between_method_declaration_parameter_list_parentheses csharp_space_between_method_declaration_parameter_list_parentheses = false # http://kent-boogaart.com/blog/editorconfig-reference-for-c-developers#csharp_space_between_parentheses csharp_space_between_parentheses = none # http://kent-boogaart.com/blog/editorconfig-reference-for-c-developers#csharp_space_between_square_brackets csharp_space_between_square_brackets = false # http://kent-boogaart.com/blog/editorconfig-reference-for-c-developers#csharp_style_expression_bodied_accessors csharp_style_expression_bodied_accessors = true:suggestion # http://kent-boogaart.com/blog/editorconfig-reference-for-c-developers#csharp_style_expression_bodied_constructors csharp_style_expression_bodied_constructors = true:suggestion # http://kent-boogaart.com/blog/editorconfig-reference-for-c-developers#csharp_style_expression_bodied_indexers csharp_style_expression_bodied_indexers = true:suggestion # http://kent-boogaart.com/blog/editorconfig-reference-for-c-developers#csharp_style_expression_bodied_methods csharp_style_expression_bodied_methods = true:suggestion # http://kent-boogaart.com/blog/editorconfig-reference-for-c-developers#csharp_style_expression_bodied_operators csharp_style_expression_bodied_operators = true:suggestion # http://kent-boogaart.com/blog/editorconfig-reference-for-c-developers#csharp_style_expression_bodied_properties csharp_style_expression_bodied_properties = true:suggestion # http://kent-boogaart.com/blog/editorconfig-reference-for-c-developers#csharp_style_inlined_variable_declaration csharp_style_inlined_variable_declaration = true:suggestion # http://kent-boogaart.com/blog/editorconfig-reference-for-c-developers#csharp_style_pattern_matching_over_as_with_null_check csharp_style_pattern_matching_over_as_with_null_check = true:suggestion # http://kent-boogaart.com/blog/editorconfig-reference-for-c-developers#csharp_style_pattern_matching_over_is_with_cast_check csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion # http://kent-boogaart.com/blog/editorconfig-reference-for-c-developers#csharp_style_throw_expression csharp_style_throw_expression = true:suggestion # http://kent-boogaart.com/blog/editorconfig-reference-for-c-developers#csharp_style_var_elsewhere csharp_style_var_elsewhere = true:suggestion # http://kent-boogaart.com/blog/editorconfig-reference-for-c-developers#csharp_style_var_for_built_in_types csharp_style_var_for_built_in_types = true:suggestion # http://kent-boogaart.com/blog/editorconfig-reference-for-c-developers#csharp_style_var_when_type_is_apparent csharp_style_var_when_type_is_apparent = true:suggestion # http://kent-boogaart.com/blog/editorconfig-reference-for-c-developers#dotnet_sort_system_directives_first dotnet_sort_system_directives_first = true # http://kent-boogaart.com/blog/editorconfig-reference-for-c-developers#dotnet_style_coalesce_expression dotnet_style_coalesce_expression = true:suggestion # http://kent-boogaart.com/blog/editorconfig-reference-for-c-developers#dotnet_style_collection_initializer dotnet_style_collection_initializer = true:suggestion # http://kent-boogaart.com/blog/editorconfig-reference-for-c-developers#dotnet_style_explicit_tuple_names dotnet_style_explicit_tuple_names = true:suggestion # http://kent-boogaart.com/blog/editorconfig-reference-for-c-developers#dotnet_style_null_propagation dotnet_style_null_propagation = true:suggestion # http://kent-boogaart.com/blog/editorconfig-reference-for-c-developers#dotnet_style_object_initializer dotnet_style_object_initializer = true:suggestion # http://kent-boogaart.com/blog/editorconfig-reference-for-c-developers#dotnet_style_predefined_type_for_locals_parameters_members dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion # http://kent-boogaart.com/blog/editorconfig-reference-for-c-developers#dotnet_style_predefined_type_for_member_access dotnet_style_predefined_type_for_member_access = true:suggestion # http://kent-boogaart.com/blog/editorconfig-reference-for-c-developers#dotnet_style_qualification_for_event dotnet_style_qualification_for_event = false:suggestion # http://kent-boogaart.com/blog/editorconfig-reference-for-c-developers#dotnet_style_qualification_for_field dotnet_style_qualification_for_field = false:suggestion # http://kent-boogaart.com/blog/editorconfig-reference-for-c-developers#dotnet_style_qualification_for_method dotnet_style_qualification_for_method = false:suggestion # http://kent-boogaart.com/blog/editorconfig-reference-for-c-developers#dotnet_style_qualification_for_property dotnet_style_qualification_for_property = false:suggestion # http://kent-boogaart.com/blog/editorconfig-reference-for-c-developers#end_of_line end_of_line = lf # http://kent-boogaart.com/blog/editorconfig-reference-for-c-developers#indent_size indent_size = 4 # http://kent-boogaart.com/blog/editorconfig-reference-for-c-developers#indent_style indent_style = space # http://kent-boogaart.com/blog/editorconfig-reference-for-c-developers#insert_final_newline insert_final_newline = true ================================================ FILE: .gitattributes ================================================ ############################################################################### # Set default behavior to automatically normalize line endings. ############################################################################### * text=auto ############################################################################### # Set default behavior for command prompt diff. # # This is need for earlier builds of msysgit that does not have it on by # default for csharp files. # Note: This is only used by command line ############################################################################### #*.cs diff=csharp ############################################################################### # Set the merge driver for project and solution files # # Merging from the command prompt will add diff markers to the files if there # are conflicts (Merging from VS is not affected by the settings below, in VS # the diff markers are never inserted). Diff markers may cause the following # file extensions to fail to load in VS. An alternative would be to treat # these files as binary and thus will always conflict and require user # intervention with every merge. To do so, just uncomment the entries below ############################################################################### #*.sln merge=binary #*.csproj merge=binary #*.vbproj merge=binary #*.vcxproj merge=binary #*.vcproj merge=binary #*.dbproj merge=binary #*.fsproj merge=binary #*.lsproj merge=binary #*.wixproj merge=binary #*.modelproj merge=binary #*.sqlproj merge=binary #*.wwaproj merge=binary ############################################################################### # behavior for image files # # image files are treated as binary by default. ############################################################################### #*.jpg binary #*.png binary #*.gif binary ############################################################################### # diff behavior for common document formats # # Convert binary document formats to text before diffing them. This feature # is only available from the command line. Turn it on by uncommenting the # entries below. ############################################################################### #*.doc diff=astextplain #*.DOC diff=astextplain #*.docx diff=astextplain #*.DOCX diff=astextplain #*.dot diff=astextplain #*.DOT diff=astextplain #*.pdf diff=astextplain #*.PDF diff=astextplain #*.rtf diff=astextplain #*.RTF diff=astextplain ================================================ FILE: .github/FUNDING.yml ================================================ # These are supported funding model platforms. github: [louthy] patreon: louthy ko_fi: louthy ================================================ FILE: .gitignore ================================================ ## Ignore Visual Studio temporary files, build results, and ## files generated by popular Visual Studio add-ons. # User-specific files *.suo *.user *.sln.docstates **.idea *.iml # Build results [Dd]ebug/ [Dd]ebugPublic/ [Rr]elease/ x64/ build/ bld/ [Bb]in/ [Oo]bj/ lib/ .vs/ # Roslyn cache directories *.ide/ # MSTest test Results [Tt]est[Rr]esult*/ [Bb]uild[Ll]og.* #NUNIT *.VisualState.xml TestResult.xml # Build Results of an ATL Project [Dd]ebugPS/ [Rr]eleasePS/ dlldata.c *_i.c *_p.c *_i.h *.ilk *.meta *.obj *.pch *.pdb *.pgc *.pgd *.rsp *.sbr *.tlb *.tli *.tlh *.tmp *.tmp_proj *.log *.vspscc *.vssscc .builds *.pidb *.svclog *.scc *.lock.json # Chutzpah Test files _Chutzpah* # Visual C++ cache files ipch/ *.aps *.ncb *.opensdf *.sdf *.cachefile # Visual Studio profiler *.psess *.vsp *.vspx # TFS 2012 Local Workspace $tf/ # Guidance Automation Toolkit *.gpState # ReSharper is a .NET coding add-in _ReSharper*/ *.[Rr]e[Ss]harper *.DotSettings.user # JustCode is a .NET coding addin-in .JustCode # TeamCity is a build add-in _TeamCity* # DotCover is a Code Coverage Tool *.dotCover # NCrunch _NCrunch_* .*crunch*.local.xml # MightyMoose *.mm.* AutoTest.Net/ # Web workbench (sass) .sass-cache/ # Installshield output folder [Ee]xpress/ # DocProject is a documentation generator add-in DocProject/buildhelp/ DocProject/Help/*.HxT DocProject/Help/*.HxC DocProject/Help/*.hhc DocProject/Help/*.hhk DocProject/Help/*.hhp DocProject/Help/Html2 DocProject/Help/html # Click-Once directory publish/ # Publish Web Output *.[Pp]ublish.xml *.azurePubxml ## TODO: Comment the next line if you want to checkin your ## web deploy settings but do note that will include unencrypted ## passwords #*.pubxml # NuGet Packages Directory packages/* ## TODO: If the tool you use requires repositories.config ## uncomment the next line #!packages/repositories.config # Enable "build/" folder in the NuGet Packages folder since # NuGet packages use it for MSBuild targets. # This line needs to be after the ignore of the build folder # (and the packages folder if the line above has been uncommented) !packages/build/ # Windows Azure Build Output csx/ *.build.csdef # Windows Store app package directory AppPackages/ # Others sql/ *.Cache ClientBin/ [Ss]tyle[Cc]op.* ~$* *~ *.dbmdl *.dbproj.schemaview *.pfx *.publishsettings node_modules/ bower_components/ # RIA/Silverlight projects Generated_Code/ # Backup & report files from converting an old project file # to a newer Visual Studio version. Backup files are not needed, # because we have git ;-) _UpgradeReport_Files/ Backup*/ UpgradeLog*.XML UpgradeLog*.htm # SQL Server files *.mdf *.ldf # Business Intelligence projects *.rdl.data *.bim.layout *.bim_*.settings # Microsoft Fakes FakesAssemblies/ # LightSwitch generated files GeneratedArtifacts/ _Pvt_Extensions/ ModelManifest.xml /SettingsParser.playlist /Parsec Tests.playlist /LanguageExt.Tests/LanguageExt.Tests.nuget.props /.idea/.idea.language-ext # Benchmark artifacts BenchmarkDotNet.Artifacts/ ================================================ FILE: CODE_OF_CONDUCT.md ================================================ # Contributor Covenant Code of Conduct ## Our Pledge In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. ## Our Standards Examples of behavior that contributes to creating a positive environment include: * Using welcoming and inclusive language * Being respectful of differing viewpoints and experiences * Gracefully accepting constructive criticism * Focusing on what is best for the community * Showing empathy towards other community members Examples of unacceptable behavior by participants include: * The use of sexualized language or imagery and unwelcome sexual attention or advances * Trolling, insulting/derogatory comments, and personal or political attacks * Public or private harassment * Publishing others' private information, such as a physical or electronic address, without explicit permission * Other conduct which could reasonably be considered inappropriate in a professional setting ## Our Responsibilities Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. ## Scope This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at p.louth@gmail.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] [homepage]: http://contributor-covenant.org [version]: http://contributor-covenant.org/version/1/4/ ================================================ FILE: CONTRIBUTING.md ================================================ ## How to contribute to language-ext #### **Did you find a bug?** * **Ensure the bug was not already reported** by searching on GitHub under [Issues](https://github.com/louthy/language-ext/issues). * If you're unable to find an open issue addressing the problem, [open a new one](https://github.com/louthy/language-ext/issues/new). Be sure to include a **title and clear description**, as much relevant information as possible, and a **code sample** or an **executable test case** demonstrating the expected behavior that is not occurring. #### **Did you write a patch that fixes a bug?** * Open a new GitHub pull request with the patch. * Ensure the PR description clearly describes the problem and solution. Include the relevant issue number if applicable. #### **Do you intend to add a new feature or change an existing one?** * First suggest your change in the [language-ext issues page](https://github.com/louthy/language-ext/issues). * There are no fixed rules on what should and shouldn't be in this library, but some features are more valuable than others, and some require long-term maintenance that outweighs the value of the feature. So please get sign-off from the project leader (Paul Louth) before putting in an excessive amount of work. #### **Do you have questions about the source code?** * Ask any question about how to use language-ext in the [language-ext issues page](https://github.com/louthy/language-ext/issues). #### **Do you want to contribute to the language-ext documentation?** This is very welcome: * The APi documentation is generated from the source code - please make sure you use back-tick markdown code-blocks within `...` and other XmlDoc sections for variable, method, or type-names and code-examples. If unclear, ask on the [language-ext issues page](https://github.com/louthy/language-ext/issues) * The [language-ext wiki](https://github.com/louthy/language-ext/wiki) is always crying out for general usage examples. If you want to talk about a larger subject that is enabled by language-ext (like parsing for example), then clear it first on the [language-ext issues page](https://github.com/louthy/language-ext/issues). Thanks! :heart: :heart: :heart: Paul Louth ================================================ FILE: LICENSE.md ================================================ The MIT License (MIT) Copyright (c) 2014-2025 Paul Louth Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: LanguageExt.Benchmarks/HashMapAddBenchmarks.cs ================================================ using System.Collections.Generic; using System.Collections.Immutable; using BenchmarkDotNet.Attributes; using LanguageExt.ClassInstances; using LanguageExt.Traits; using Sasa.Collections; using static LanguageExt.Prelude; namespace LanguageExt.Benchmarks { [RPlotExporter, RankColumn] [GenericTypeArguments(typeof(int), typeof(OrdInt))] [GenericTypeArguments(typeof(string), typeof(OrdString))] public class HashMapAddBenchmarks where TOrd : Ord { [Params(100, 1000, 10000, 100000)] public int N; Dictionary values; [GlobalSetup] public void Setup() { values = ValuesGenerator.Default.GenerateDictionary(N); } [Benchmark] public ImmutableDictionary SysColImmutableDictionary() { var map = ImmutableDictionary.Create(); foreach (var kvp in values) { map = map.Add(kvp.Key, kvp.Value); } return map; } [Benchmark] public ImmutableSortedDictionary SysColImmutableSortedDictionary() { var map = ImmutableSortedDictionary.Create(); foreach (var kvp in values) { map = map.Add(kvp.Key, kvp.Value); } return map; } [Benchmark] public Trie SasaTrie() { var map = Trie.Empty; foreach (var kvp in values) { map = map.Add(kvp.Key, kvp.Value); } return map; } [Benchmark] public Dictionary SysColDictionary() { var map = new Dictionary(); foreach (var kvp in values) { map.Add(kvp.Key, kvp.Value); } return map; } [Benchmark] public HashMap LangExtHashMap() { var map = HashMap(); foreach (var kvp in values) { map = map.Add(kvp.Key, kvp.Value); } return map; } [Benchmark] public Map LangExtMap() { var map = Map(); foreach (var kvp in values) { map = map.Add(kvp.Key, kvp.Value); } return map; } } } ================================================ FILE: LanguageExt.Benchmarks/HashMapContainsKeyBenchmarks.cs ================================================ using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; using BenchmarkDotNet.Attributes; using LanguageExt.ClassInstances; using LanguageExt.Traits; using Sasa.Collections; namespace LanguageExt.Benchmarks { [RPlotExporter, RankColumn] [GenericTypeArguments(typeof(int), typeof(OrdInt))] [GenericTypeArguments(typeof(string), typeof(OrdString))] public class HashMapContainsKeyBenchmarks where TOrd : Ord { [Params(100, 1000, 10000, 100000)] public int N; T[] keys; ImmutableDictionary immutableMap; ImmutableSortedDictionary immutableSortedMap; Dictionary dictionary; HashMap hashMap; Map map; Trie sasaTrie; [GlobalSetup] public void Setup() { var values = ValuesGenerator.Default.GenerateDictionary(N); keys = values.Keys.ToArray(); sasaTrie = ValuesGenerator.SasaTrieSetup(values); immutableMap = ValuesGenerator.SysColImmutableDictionarySetup(values); immutableSortedMap = ValuesGenerator.SysColImmutableSortedDictionarySetup(values); dictionary = ValuesGenerator.SysColDictionarySetup(values); hashMap = ValuesGenerator.LangExtHashMapSetup(values); map = ValuesGenerator.LangExtMapSetup(values); } [Benchmark] public bool SysColImmutableDictionary() { var result = true; foreach (var key in keys) { result &= immutableMap.ContainsKey(key); } return result; } [Benchmark] public bool SasaTrie() { var result = true; foreach (var key in keys) { result &= sasaTrie.ContainsKey(key); } return result; } [Benchmark] public bool SysColImmutableSortedDictionary() { var result = true; foreach (var key in keys) { result &= immutableSortedMap.ContainsKey(key); } return result; } [Benchmark] public bool SysColDictionary() { var result = true; foreach (var key in keys) { result &= dictionary.ContainsKey(key); } return result; } [Benchmark] public bool LangExtHashMap() { var result = true; foreach (var key in keys) { result &= hashMap.ContainsKey(key); } return result; } [Benchmark] public bool LangExtMap() { var result = true; foreach (var key in keys) { result &= map.ContainsKey(key); } return result; } } } ================================================ FILE: LanguageExt.Benchmarks/HashMapIterateBenchmarks.cs ================================================ using System.Collections.Generic; using System.Collections.Immutable; using BenchmarkDotNet.Attributes; using LanguageExt.ClassInstances; using LanguageExt.Traits; using Sasa.Collections; namespace LanguageExt.Benchmarks { [RPlotExporter, RankColumn] [GenericTypeArguments(typeof(int), typeof(OrdInt))] [GenericTypeArguments(typeof(string), typeof(OrdString))] public class HashMapIterateBenchmarks where TOrd : Ord { [Params(100, 1000, 10000, 100000)] public int N; ImmutableDictionary immutableMap; ImmutableSortedDictionary immutableSortedMap; Dictionary dictionary; HashMap hashMap; Map map; Trie sasaTrie; [GlobalSetup] public void Setup() { var values = ValuesGenerator.Default.GenerateDictionary(N); sasaTrie = ValuesGenerator.SasaTrieSetup(values); immutableMap = ValuesGenerator.SysColImmutableDictionarySetup(values); immutableSortedMap = ValuesGenerator.SysColImmutableSortedDictionarySetup(values); dictionary = ValuesGenerator.SysColDictionarySetup(values); hashMap = ValuesGenerator.LangExtHashMapSetup(values); map = ValuesGenerator.LangExtMapSetup(values); } [Benchmark] public T SysColImmutableDictionary() { T result = default; var collection = immutableMap; foreach (var item in collection) { result = item.Value; } return result; } [Benchmark] public T SysColImmutableSortedDictionary() { T result = default; var collection = immutableSortedMap; foreach (var item in collection) { result = item.Value; } return result; } [Benchmark] public T SasaTrie() { T result = default; var collection = sasaTrie; foreach (var item in collection) { result = item.Value; } return result; } [Benchmark] public T SysColDictionary() { T result = default; var collection = dictionary; foreach (var item in collection) { result = item.Value; } return result; } [Benchmark] public T LangExtHashMap() { T result = default; var collection = hashMap; foreach (var item in collection) { result = item.Value; } return result; } [Benchmark] public T LangExtMap() { T result = default; var collection = map; foreach (var item in collection) { result = item.Value; } return result; } } } ================================================ FILE: LanguageExt.Benchmarks/HashMapRandomRemovalBenchmarks.cs ================================================ using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; using BenchmarkDotNet.Attributes; using LanguageExt.ClassInstances; using LanguageExt.Traits; using Sasa.Collections; namespace LanguageExt.Benchmarks { [RPlotExporter, RankColumn] [GenericTypeArguments(typeof(int), typeof(OrdInt))] [GenericTypeArguments(typeof(string), typeof(OrdString))] public class HashMapRandomRemovalBenchmarks where TOrd : Ord { [Params(100, 1000, 10000, 100000)] public int N; T[] keys; ImmutableDictionary immutableMap; ImmutableSortedDictionary immutableSortedMap; Dictionary dictionary; HashMap hashMap; Map map; Trie sasaTrie; [GlobalSetup] public void Setup() { var values = ValuesGenerator.Default.GenerateDictionary(N); keys = values.Keys.ToArray(); sasaTrie = ValuesGenerator.SasaTrieSetup(values); immutableMap = ValuesGenerator.SysColImmutableDictionarySetup(values); immutableSortedMap = ValuesGenerator.SysColImmutableSortedDictionarySetup(values); dictionary = ValuesGenerator.SysColDictionarySetup(values); hashMap = ValuesGenerator.LangExtHashMapSetup(values); map = ValuesGenerator.LangExtMapSetup(values); } [Benchmark] public bool SysColImmutableDictionary() { var localImmutableMap = immutableMap; foreach (var key in keys) { localImmutableMap = localImmutableMap.Remove(key); } return localImmutableMap.IsEmpty; } [Benchmark] public bool SysColImmutableSortedDictionary() { var localSortedMap = immutableSortedMap; foreach (var key in keys) { localSortedMap = localSortedMap.Remove(key); } return localSortedMap.IsEmpty; } [Benchmark] public bool SasaTrie() { var localImmutableMap = sasaTrie; foreach (var key in keys) { localImmutableMap = localImmutableMap.Remove(key); } return localImmutableMap.IsEmpty(); } [Benchmark] public bool SysColDictionary() { // NB! no local variable - mutating field instance foreach (var key in keys) { dictionary.Remove(key); } return dictionary.Count == 0; } [Benchmark] public bool LangExtHashMap() { var localHashMap = hashMap; foreach (var key in keys) { localHashMap = localHashMap.Remove(key); } return localHashMap.IsEmpty; } [Benchmark] public bool LangExtMap() { var localMap = map; foreach (var key in keys) { localMap = localMap.Remove(key); } return localMap.IsEmpty; } } } ================================================ FILE: LanguageExt.Benchmarks/HashSetAddBenchmarks.cs ================================================ using System.Collections.Immutable; using BenchmarkDotNet.Attributes; using LanguageExt.ClassInstances; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt.Benchmarks { [RPlotExporter, RankColumn] [GenericTypeArguments(typeof(int), typeof(OrdInt))] [GenericTypeArguments(typeof(string), typeof(OrdString))] public class HashSetAddBenchmarks where TOrd : Ord { [Params(100, 1000, 10000, 100000)] public int N; T[] values; [GlobalSetup] public void Setup() { values = ValuesGenerator.Default.GenerateUniqueValues(N); } [Benchmark] public ImmutableHashSet SysColImmutableHashSet() { var set = ImmutableHashSet.Create(); foreach (var value in values) { set = set.Add(value); } return set; } [Benchmark] public ImmutableSortedSet SysColImmutableSortedSet() { var set = ImmutableSortedSet.Create(); foreach (var value in values) { set = set.Add(value); } return set; } [Benchmark] public System.Collections.Generic.HashSet SysColHashSet() { var set = new System.Collections.Generic.HashSet(); foreach (var value in values) { set.Add(value); } return set; } [Benchmark] public HashSet LangExtHashSet() { var set = HashSet(); foreach (var value in values) { set = set.Add(value); } return set; } [Benchmark] public Set LangExtSet() { var set = Set(); foreach (var value in values) { set = set.Add(value); } return set; } } } ================================================ FILE: LanguageExt.Benchmarks/HashSetContainsBenchmarks.cs ================================================ using System.Collections.Immutable; using BenchmarkDotNet.Attributes; using LanguageExt.ClassInstances; using LanguageExt.Traits; namespace LanguageExt.Benchmarks { [RPlotExporter, RankColumn] [GenericTypeArguments(typeof(int), typeof(OrdInt))] [GenericTypeArguments(typeof(string), typeof(OrdString))] public class HashSetContainsBenchmarks where TOrd : Ord { [Params(100, 1000, 10000, 100000)] public int N; T[] values; ImmutableHashSet immutableHashSet; ImmutableSortedSet immutableSortedSet; System.Collections.Generic.HashSet sysHashSet; HashSet hashSet; Set set; [GlobalSetup] public void Setup() { values = ValuesGenerator.Default.GenerateUniqueValues(N); immutableHashSet = ValuesGenerator.SysColImmutableHashSetSetup(values); immutableSortedSet = ValuesGenerator.SysColImmutableSortedSetSetup(values); sysHashSet = ValuesGenerator.SysColHashSetSetup(values); hashSet = ValuesGenerator.LangExtHashSetSetup(values); set = ValuesGenerator.LangExtSetSetup(values); } [Benchmark] public bool SysColImmutableHashSet() { var result = true; foreach (var value in values) { result &= immutableHashSet.Contains(value); } return result; } [Benchmark] public bool SysColImmutableSortedSet() { var result = true; foreach (var value in values) { result &= immutableSortedSet.Contains(value); } return result; } [Benchmark] public bool SysColHashSet() { var result = true; foreach (var value in values) { result &= sysHashSet.Contains(value); } return result; } [Benchmark] public bool LangExtHashSet() { var result = true; foreach (var value in values) { result &= hashSet.Contains(value); } return result; } [Benchmark] public bool LangExtSet() { var result = true; foreach (var value in values) { result &= set.Contains(value); } return result; } } } ================================================ FILE: LanguageExt.Benchmarks/HashSetIterationBenchmarks.cs ================================================ using System.Collections.Immutable; using BenchmarkDotNet.Attributes; using LanguageExt.ClassInstances; using LanguageExt.Traits; namespace LanguageExt.Benchmarks { [RPlotExporter, RankColumn] [GenericTypeArguments(typeof(int), typeof(OrdInt))] [GenericTypeArguments(typeof(string), typeof(OrdString))] public class HashSetIterationBenchmarks where TOrd : Ord { [Params(100, 1000, 10000, 100000)] public int N; T[] values; ImmutableHashSet immutableHashSet; ImmutableSortedSet immutableSortedSet; System.Collections.Generic.HashSet sysHashSet; HashSet hashSet; Set set; [GlobalSetup] public void Setup() { values = ValuesGenerator.Default.GenerateUniqueValues(N); immutableHashSet = ValuesGenerator.SysColImmutableHashSetSetup(values); immutableSortedSet = ValuesGenerator.SysColImmutableSortedSetSetup(values); sysHashSet = ValuesGenerator.SysColHashSetSetup(values); hashSet = ValuesGenerator.LangExtHashSetSetup(values); set = ValuesGenerator.LangExtSetSetup(values); } [Benchmark] public T SysColImmutableHashSet() { T result = default; var collection = immutableHashSet; foreach (var item in collection) { result = item; } return result; } [Benchmark] public T SysColImmutableSortedSet() { T result = default; var collection = immutableSortedSet; foreach (var item in collection) { result = item; } return result; } [Benchmark] public T SysColHashSet() { T result = default; var collection = sysHashSet; foreach (var item in collection) { result = item; } return result; } [Benchmark] public T LangExtHashSet() { T result = default; var collection = hashSet; foreach (var item in collection) { result = item; } return result; } [Benchmark] public T LangExtSet() { T result = default; var collection = set; foreach (var item in collection) { result = item; } return result; } } } ================================================ FILE: LanguageExt.Benchmarks/HashSetRandomRemovalBenchmarks.cs ================================================ using System.Collections.Immutable; using BenchmarkDotNet.Attributes; using LanguageExt.ClassInstances; using LanguageExt.Traits; namespace LanguageExt.Benchmarks { [RPlotExporter, RankColumn] [GenericTypeArguments(typeof(int), typeof(OrdInt))] [GenericTypeArguments(typeof(string), typeof(OrdString))] public class HashSetRandomRemovalBenchmarks where TOrd : Ord { [Params(100, 1000, 10000, 100000)] public int N; T[] values; ImmutableHashSet immutableSet; ImmutableSortedSet immutableSortedSet; System.Collections.Generic.HashSet sysHashSet; HashSet hashSet; Set set; [GlobalSetup] public void Setup() { values = ValuesGenerator.Default.GenerateUniqueValues(N); immutableSet = ValuesGenerator.SysColImmutableHashSetSetup(values); immutableSortedSet = ValuesGenerator.SysColImmutableSortedSetSetup(values); sysHashSet = ValuesGenerator.SysColHashSetSetup(values); hashSet = ValuesGenerator.LangExtHashSetSetup(values); set = ValuesGenerator.LangExtSetSetup(values); } [Benchmark] public bool SysColImmutableHashSet() { var localImmutableSet = immutableSet; foreach (var value in values) { localImmutableSet = localImmutableSet.Remove(value); } return localImmutableSet.IsEmpty; } [Benchmark] public bool SysColImmutableSortedSet() { var localImmutableSortedSet = immutableSortedSet; foreach (var value in values) { localImmutableSortedSet = localImmutableSortedSet.Remove(value); } return localImmutableSortedSet.IsEmpty; } [Benchmark] public bool SysColHashSet() { // NB! no local variable - mutating field instance foreach (var value in values) { sysHashSet.Remove(value); } return sysHashSet.Count == 0; } [Benchmark] public bool LangExtHashSet() { var localHashSet = hashSet; foreach (var value in values) { localHashSet = localHashSet.Remove(value); } return localHashSet.IsEmpty; } [Benchmark] public bool LangExtSet() { var localSet = set; foreach (var value in values) { localSet = localSet.Remove(value); } return localSet.IsEmpty; } } } ================================================ FILE: LanguageExt.Benchmarks/LanguageExt.Benchmarks.csproj ================================================ Exe net10.0 ================================================ FILE: LanguageExt.Benchmarks/ListAddBenchmarks.cs ================================================ using System.Collections.Generic; using System.Collections.Immutable; using BenchmarkDotNet.Attributes; using LanguageExt.ClassInstances; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt.Benchmarks { [RPlotExporter, RankColumn] [GenericTypeArguments(typeof(int), typeof(OrdInt))] [GenericTypeArguments(typeof(string), typeof(OrdString))] public class ListAddBenchmarks where TOrd : Ord { [Params(100, 1000, 10000, 100000)] public int N; T[] values; [GlobalSetup] public void Setup() { values = ValuesGenerator.Default.GenerateUniqueValues(N); } [Benchmark] public ImmutableList SysColImmutableList() { var collection = ImmutableList.Create(); foreach (var value in values) { collection = collection.Add(value); } return collection; } [Benchmark] public Lst LangExtLst() { var collection = List(); foreach (var value in values) { collection = collection.Add(value); } return collection; } [Benchmark] public Seq LangExtSeq() { var collection = Seq(); foreach (var value in values) { collection = collection.Add(value); } return collection; } } } ================================================ FILE: LanguageExt.Benchmarks/ListIterationBenchmarks.cs ================================================ using System.Collections.Immutable; using BenchmarkDotNet.Attributes; using LanguageExt.ClassInstances; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt.Benchmarks { [RPlotExporter, RankColumn] [GenericTypeArguments(typeof(int), typeof(OrdInt))] [GenericTypeArguments(typeof(string), typeof(OrdString))] public class ListIterationBenchmarks where TOrd : Ord { [Params(100, 1000, 10000, 100000)] public int N; T[] values; ImmutableList immutableList; Lst lst; Seq seq; [GlobalSetup] public void Setup() { values = ValuesGenerator.Default.GenerateUniqueValues(N); immutableList = ImmutableList.CreateRange(ValuesGenerator.Default.GenerateUniqueValues(N)); lst = ValuesGenerator.Default.GenerateUniqueValues(N).AsIterable().ToLst(); seq = toSeq(ValuesGenerator.Default.GenerateUniqueValues(N)).Strict(); } [Benchmark] public T SysColImmutableList() { T result = default; var collection = immutableList; foreach (var item in collection) { result = item; } return result; } [Benchmark] public T LangExtLst() { T result = default; var collection = lst; foreach (var item in collection) { result = item; } return result; } [Benchmark] public T LangExtSeq() { T result = default; var collection = seq; foreach (var item in collection) { result = item; } return result; } } } ================================================ FILE: LanguageExt.Benchmarks/Program.cs ================================================ using BenchmarkDotNet.Running; namespace LanguageExt.Benchmarks { class Program { static void Main(string[] args) => BenchmarkSwitcher .FromAssembly(typeof(Program).Assembly) .Run(args); } } ================================================ FILE: LanguageExt.Benchmarks/ValuesGenerator.MapSetup.cs ================================================ using System.Collections.Generic; using System.Collections.Immutable; using LanguageExt.Traits; using Sasa.Collections; using static LanguageExt.Prelude; namespace LanguageExt.Benchmarks { internal partial class ValuesGenerator { public static Trie SasaTrieSetup(Dictionary values) { var trie = Trie.Empty; foreach (var kvp in values) { trie = trie.Add(kvp.Key, kvp.Value); } return trie; } public static ImmutableDictionary SysColImmutableDictionarySetup(Dictionary values) { var immutableMap = ImmutableDictionary.Create(); foreach (var kvp in values) { immutableMap = immutableMap.Add(kvp.Key, kvp.Value); } return immutableMap; } public static ImmutableSortedDictionary SysColImmutableSortedDictionarySetup(Dictionary values) { var immutableMap = ImmutableSortedDictionary.Create(); foreach (var kvp in values) { immutableMap = immutableMap.Add(kvp.Key, kvp.Value); } return immutableMap; } public static Dictionary SysColDictionarySetup(Dictionary values) { var dictionary = new Dictionary(); foreach (var kvp in values) { dictionary.Add(kvp.Key, kvp.Value); } return dictionary; } public static HashMap LangExtHashMapSetup(Dictionary values) where TEq : Eq { var hashMap = HashMap(); foreach (var kvp in values) { hashMap = hashMap.Add(kvp.Key, kvp.Value); } return hashMap; } public static Map LangExtMapSetup(Dictionary values) where TOrd : Ord { var hashMap = Map(); foreach (var kvp in values) { hashMap = hashMap.Add(kvp.Key, kvp.Value); } return hashMap; } } } ================================================ FILE: LanguageExt.Benchmarks/ValuesGenerator.SetSetup.cs ================================================ using System.Collections.Immutable; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt.Benchmarks { internal partial class ValuesGenerator { public static ImmutableHashSet SysColImmutableHashSetSetup(T[] values) { var immutableSet = ImmutableHashSet.Create(); foreach (var value in values) { immutableSet = immutableSet.Add(value); } return immutableSet; } public static ImmutableSortedSet SysColImmutableSortedSetSetup(T[] values) { var immutableSet = ImmutableSortedSet.Create(); foreach (var value in values) { immutableSet = immutableSet.Add(value); } return immutableSet; } public static System.Collections.Generic.HashSet SysColHashSetSetup(T[] values) { var hashSet = new System.Collections.Generic.HashSet(); foreach (var value in values) { hashSet.Add(value); } return hashSet; } public static HashSet LangExtHashSetSetup(T[] values) where TEq : Eq { var hashSet = HashSet(); foreach (var value in values) { hashSet = hashSet.Add(value); } return hashSet; } public static Set LangExtSetSetup(T[] values) where TOrd : Ord { var set = Set(); foreach (var value in values) { set = set.Add(value); } return set; } } } ================================================ FILE: LanguageExt.Benchmarks/ValuesGenerator.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace LanguageExt.Benchmarks { internal partial class ValuesGenerator { public static readonly ValuesGenerator Default = new ValuesGenerator(12345); readonly int randSeed; public ValuesGenerator(int seed) { randSeed = seed; } public Dictionary GenerateDictionary(int count) { var rand = new Random(randSeed); var dict = new Dictionary(count); while (dict.Count < count) { dict[GenerateValue(rand)] = GenerateValue(rand); } return dict; } public T[] GenerateUniqueValues(int count) { var rand = new Random(randSeed); var set = new System.Collections.Generic.HashSet(count); while (set.Count < count) { set.Add(GenerateValue(rand)); } return set.ToArray(); } private T GenerateValue(Random rand) { if (typeof(T) == typeof(int)) { return (T)(object)rand.Next(); } if (typeof(T) == typeof(string)) { return (T)(object)GenerateString(rand, 1, 50); } throw new NotSupportedException($"Generating value of type {typeof(T)} is not supported"); } private string GenerateString(Random rand, int minLength, int maxLength) { var length = rand.Next(minLength, maxLength); var sb = new StringBuilder(length); for (var i = 0; i < length; i++) { switch (rand.Next(0, 3)) { case 0: sb.Append((char)rand.Next('0', '9')); break; case 1: sb.Append((char)rand.Next('A', 'Z')); break; default: sb.Append((char)rand.Next('a', 'z')); break; } } return sb.ToString(); } } } ================================================ FILE: LanguageExt.Core/Catch.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; /// /// Used by `@catch`, `@exceptional`, `@expected` to represent the catching of errors /// public readonly record struct CatchM(Func Match, Func> Action) where M : Fallible { public K Run(E error, K otherwise) => Match(error) ? Action(error) : otherwise; } public static class CatchMExtensions { extension(CatchM self) where M : Functor, Fallible { public CatchM Map(Func f) => new(self.Match, e => self.Action(e).Map(f)); } } ================================================ FILE: LanguageExt.Core/Class Instances/Eq/EqArr.cs ================================================ using LanguageExt.Traits; using System.Diagnostics.Contracts; using static LanguageExt.Prelude; namespace LanguageExt.ClassInstances; /// /// Array equality /// public struct EqArr : Eq> where EqA : Eq { /// /// Equality test /// /// The left hand side of the equality operation /// The right hand side of the equality operation /// True if x and y are equal [Pure] public static bool Equals(Arr x, Arr y) { if (x.Count != y.Count) return false; var xiter = x.GetEnumerator(); var yiter = y.GetEnumerator(); while (xiter.MoveNext() && yiter.MoveNext()) { if (!equals(xiter.Current, yiter.Current)) return false; } return true; } /// /// Get hash code of the value /// /// Value to get the hash code of /// The hash code of x [Pure] public static int GetHashCode(Arr x) => HashableArr.GetHashCode(x); } /// /// Array equality /// public struct EqArr : Eq> { /// /// Equality test /// /// The left hand side of the equality operation /// The right hand side of the equality operation /// True if x and y are equal [Pure] public static bool Equals(Arr x, Arr y) => EqArr, A>.Equals(x, y); /// /// Get hash code of the value /// /// Value to get the hash code of /// The hash code of x [Pure] public static int GetHashCode(Arr x) => HashableArr, A>.GetHashCode(x); } ================================================ FILE: LanguageExt.Core/Class Instances/Eq/EqArray.cs ================================================ using LanguageExt.Traits; using System.Diagnostics.Contracts; using static LanguageExt.Prelude; namespace LanguageExt.ClassInstances; /// /// Array equality /// public struct EqArray : Eq where EqA : Eq { /// /// Equality test /// /// The left hand side of the equality operation /// The right hand side of the equality operation /// True if x and y are equal [Pure] public static bool Equals(A[] x, A[] y) { if (x.Length != y.Length) return false; for (var i = 0; i < x.Length; i++) { if (!equals(x[i], y[i])) return false; } return true; } /// /// Get hash code of the value /// /// Value to get the hash code of /// The hash code of x [Pure] public static int GetHashCode(A[] x) => HashableArray.GetHashCode(x); } /// /// Array equality /// public struct EqArray : Eq { /// /// Equality test /// /// The left hand side of the equality operation /// The right hand side of the equality operation /// True if x and y are equal [Pure] public static bool Equals(A[] x, A[] y) => EqArray, A>.Equals(x, y); /// /// Get hash code of the value /// /// Value to get the hash code of /// The hash code of x [Pure] public static int GetHashCode(A[] x) => HashableArray, A>.GetHashCode(x); } ================================================ FILE: LanguageExt.Core/Class Instances/Eq/EqBigInt.cs ================================================ using LanguageExt.Traits; using System.Diagnostics.Contracts; namespace LanguageExt.ClassInstances; /// /// Integer equality /// public struct EqBigInt : Eq { /// /// Equality test /// /// The left hand side of the equality operation /// The right hand side of the equality operation /// True if x and y are equal [Pure] public static bool Equals(bigint a, bigint b) => a == b; /// /// Get hash code of the value /// /// Value to get the hash code of /// The hash code of x [Pure] public static int GetHashCode(bigint x) => HashableBigInt.GetHashCode(x); } ================================================ FILE: LanguageExt.Core/Class Instances/Eq/EqBool.cs ================================================ using LanguageExt.Traits; using System.Diagnostics.Contracts; namespace LanguageExt.ClassInstances; /// /// Boolean equality /// public struct EqBool : Eq { /// /// Equality test /// /// The left hand side of the equality operation /// The right hand side of the equality operation /// True if x and y are equal [Pure] public static bool Equals(bool a, bool b) => a == b; /// /// Get hash code of the value /// /// Value to get the hash code of /// The hash code of x [Pure] public static int GetHashCode(bool x) => HashableBool.GetHashCode(x); } ================================================ FILE: LanguageExt.Core/Class Instances/Eq/EqChar.cs ================================================ using LanguageExt.Traits; using System.Diagnostics.Contracts; namespace LanguageExt.ClassInstances; /// /// Char equality /// public struct EqChar : Eq { /// /// Equality test /// /// The left hand side of the equality operation /// The right hand side of the equality operation /// True if x and y are equal [Pure] public static bool Equals(char a, char b) => a == b; /// /// Get hash code of the value /// /// Value to get the hash code of /// The hash code of x [Pure] public static int GetHashCode(char x) => HashableChar.GetHashCode(x); } /// /// Char equality (ordinal, ignore case) /// public struct EqCharOrdinalIgnoreCase : Eq { /// /// Equality test /// /// The left hand side of the equality operation /// The right hand side of the equality operation /// True if a and b are equal [Pure] public static bool Equals(char a, char b) { if (a >= 'a' && a <= 'z') a = (char)(a - 0x20); if (b >= 'a' && b <= 'z') b = (char)(b - 0x20); return a == b; } /// /// Get hash code of the value /// /// Value to get the hash code of /// The hash code of x [Pure] public static int GetHashCode(char x) => HashableCharOrdinalIgnoreCase.GetHashCode(x); } ================================================ FILE: LanguageExt.Core/Class Instances/Eq/EqCompositions.cs ================================================ using System.Diagnostics.Contracts; using LanguageExt.Traits; namespace LanguageExt.ClassInstances; public struct EqCompositions : Eq> where A : Monoid { [Pure] public static bool Equals(Compositions x, Compositions y) => x == y; [Pure] public static int GetHashCode(Compositions x) => HashableCompositions.GetHashCode(x); } ================================================ FILE: LanguageExt.Core/Class Instances/Eq/EqDateTime.cs ================================================ using LanguageExt.Traits; using System; using System.Diagnostics.Contracts; namespace LanguageExt.ClassInstances; /// /// DateTime equality /// public struct EqDateTime : Eq { /// /// Equality test /// /// The left hand side of the equality operation /// The right hand side of the equality operation /// True if x and y are equal [Pure] public static bool Equals(DateTime a, DateTime b) => a == b; /// /// Get hash code of the value /// /// Value to get the hash code of /// The hash code of x [Pure] public static int GetHashCode(DateTime x) => HashableDateTime.GetHashCode(x); } ================================================ FILE: LanguageExt.Core/Class Instances/Eq/EqDecimal.cs ================================================ using LanguageExt.Traits; using System.Diagnostics.Contracts; using System.Threading.Tasks; namespace LanguageExt.ClassInstances; /// /// Floating point equality /// public struct EqDecimal : Eq { /// /// Equality test /// /// The left hand side of the equality operation /// The right hand side of the equality operation /// True if x and y are equal [Pure] public static bool Equals(decimal a, decimal b) => a == b; /// /// Get hash code of the value /// /// Value to get the hash code of /// The hash code of x [Pure] public static int GetHashCode(decimal x) => HashableDecimal.GetHashCode(x); [Pure] public static Task EqualsAsync(decimal x, decimal y) => Equals(x, y).AsTask(); [Pure] public static Task GetHashCodeAsync(decimal x) => GetHashCode(x).AsTask(); } ================================================ FILE: LanguageExt.Core/Class Instances/Eq/EqDefault.cs ================================================ using LanguageExt.Traits; using static LanguageExt.Prelude; using System; using System.Diagnostics.Contracts; using LanguageExt.Traits.Resolve; namespace LanguageExt.ClassInstances; /// /// Finds an appropriate Eq from the loaded assemblies, if one can't be found then it /// falls back to the standard .NET EqualityComparer〈A〉.Default.Equals(a,b) method to /// provide equality testing. /// public readonly struct EqDefault : Eq { /// /// Equality test /// /// The left hand side of the equality operation /// The right hand side of the equality operation /// True if x and y are equal [Pure] public static bool Equals(A a, A b) { if (a is null) return b is null; if (b is null) return false; if (ReferenceEquals(a, b)) return true; return EqResolve.Equals(a, b); } /// /// Get hash code of the value /// /// Value to get the hash code of /// The hash code of x [Pure] public static int GetHashCode(A x) => HashableDefault.GetHashCode(x); } /// /// This is a utility type for when two generic types are used, but it's not clear if they /// have the same underlying type. We'd like to do structural equality if they are, and /// return false if they're not. /// public static class EqDefault { static readonly Func Eq; static EqDefault() { Eq = typeof(A).FullName == typeof(B).FullName ? (x, y) => y is A y1 && EqDefault.Equals(x, y1) : (_, _) => false; } /// /// Equality test /// /// The left hand side of the equality operation /// The right hand side of the equality operation /// True if x and y are equal [Pure] public static bool Equals(A a, B b) { if (isnull(a)) return isnull(b); if (isnull(b)) return false; if (ReferenceEquals(a, b)) return true; return Eq(a, b); } } ================================================ FILE: LanguageExt.Core/Class Instances/Eq/EqDouble.cs ================================================ using LanguageExt.Traits; using System.Diagnostics.Contracts; namespace LanguageExt.ClassInstances { /// /// Floating point equality /// public struct EqDouble : Eq { /// /// Equality test /// /// The left hand side of the equality operation /// The right hand side of the equality operation /// True if x and y are equal [Pure] public static bool Equals(double a, double b) => a.Equals(b); /// /// Get hash code of the value /// /// Value to get the hash code of /// The hash code of x [Pure] public static int GetHashCode(double x) => HashableDouble.GetHashCode(x); } } ================================================ FILE: LanguageExt.Core/Class Instances/Eq/EqEdit.cs ================================================ using System.Diagnostics.Contracts; using LanguageExt.Traits; namespace LanguageExt.ClassInstances { /// /// Equality instance for `Patch` `Edit` /// public struct EqEdit : Eq> where EqA : Eq { [Pure] public static bool Equals(Edit x, Edit y) => x == y; [Pure] public static int GetHashCode(Edit x) => HashableEdit.GetHashCode(x); } } ================================================ FILE: LanguageExt.Core/Class Instances/Eq/EqEither.cs ================================================ using System.Diagnostics.Contracts; using System.Threading.Tasks; using LanguageExt.Traits; namespace LanguageExt.ClassInstances { /// /// Either type hashing /// public struct EqEither : Eq> where EqL : Eq where EqR : Eq { /// /// Equality test /// [Pure] public static bool Equals(Either x, Either y) => x.Equals(y); /// /// Get hash code of the value /// /// Value to get the hash code of /// The hash code of x [Pure] public static int GetHashCode(Either x) => HashableEither.GetHashCode(x); /// /// Get hash code of the value /// /// Value to get the hash code of /// The hash code of x [Pure] public static Task GetHashCodeAsync(Either x) => GetHashCode(x).AsTask(); /// /// Equality test /// [Pure] public static Task EqualsAsync(Either x, Either y) => Equals(x, y).AsTask(); } /// /// Either type hashing /// public struct EqEither : Eq> { /// /// Equality test /// [Pure] public static bool Equals(Either x, Either y) => EqEither, EqDefault, L, R>.Equals(x, y); /// /// Get hash code of the value /// /// Value to get the hash code of /// The hash code of x [Pure] public static int GetHashCode(Either x) => HashableEither, HashableDefault, L, R>.GetHashCode(x); } } ================================================ FILE: LanguageExt.Core/Class Instances/Eq/EqEnumerable.cs ================================================ using LanguageExt.Traits; using System.Collections.Generic; using System.Diagnostics.Contracts; namespace LanguageExt.ClassInstances; /// /// Equality test /// /// The left hand side of the equality operation /// The right hand side of the equality operation /// True if x and y are equal public struct EqEnumerable : Eq> where EQ : Eq { /// /// Equality check /// [Pure] public static bool Equals(IEnumerable x, IEnumerable y) { using var enumx = x.GetEnumerator(); using var enumy = y.GetEnumerator(); while (true) { var a = enumx.MoveNext(); var b = enumy.MoveNext(); if (a != b) return false; if (!a && !b) return true; if (!EQ.Equals(enumx.Current, enumy.Current)) return false; } } /// /// Get hash code of the value /// /// Value to get the hash code of /// The hash code of x [Pure] public static int GetHashCode(IEnumerable x) => HashableEnumerable.GetHashCode(x); } /// /// Equality test /// /// The left hand side of the equality operation /// The right hand side of the equality operation /// True if x and y are equal public struct EqEnumerable : Eq> { /// /// Equality check /// [Pure] public static bool Equals(IEnumerable x, IEnumerable y) => EqEnumerable, A>.Equals(x, y); /// /// Get hash code of the value /// /// Value to get the hash code of /// The hash code of x [Pure] public static int GetHashCode(IEnumerable x) => HashableEnumerable.GetHashCode(x); } ================================================ FILE: LanguageExt.Core/Class Instances/Eq/EqException.cs ================================================ using System; using System.Diagnostics.Contracts; using LanguageExt.Traits; namespace LanguageExt.ClassInstances; public struct EqException : Eq { [Pure] public static int GetHashCode(Exception x) => HashableException.GetHashCode(x); [Pure] public static bool Equals(Exception x, Exception y) => x.GetType() == y.GetType(); } ================================================ FILE: LanguageExt.Core/Class Instances/Eq/EqFloat.cs ================================================ using LanguageExt.Traits; using System.Diagnostics.Contracts; namespace LanguageExt.ClassInstances; /// /// Floating point equality /// public struct EqFloat : Eq { /// /// Equality test /// /// The left hand side of the equality operation /// The right hand side of the equality operation /// True if x and y are equal [Pure] public static bool Equals(float a, float b) => a.Equals(b); /// /// Get hash code of the value /// /// Value to get the hash code of /// The hash code of x [Pure] public static int GetHashCode(float x) => HashableFloat.GetHashCode(x); } ================================================ FILE: LanguageExt.Core/Class Instances/Eq/EqGuid.cs ================================================ using System; using LanguageExt.Traits; using System.Diagnostics.Contracts; namespace LanguageExt.ClassInstances; /// /// Guid equality /// public struct EqGuid : Eq { /// /// Equality test /// /// The left hand side of the equality operation /// The right hand side of the equality operation /// True if x and y are equal [Pure] public static bool Equals(Guid a, Guid b) => a == b; /// /// Get hash code of the value /// /// Value to get the hash code of /// The hash code of x [Pure] public static int GetHashCode(Guid x) => HashableGuid.GetHashCode(x); } ================================================ FILE: LanguageExt.Core/Class Instances/Eq/EqHashSet.cs ================================================ using LanguageExt.Traits; using System.Diagnostics.Contracts; namespace LanguageExt.ClassInstances; /// /// HashSet equality /// public struct EqHashSet : Eq> where EQ : Eq { /// /// Equality test /// /// The left hand side of the equality operation /// The right hand side of the equality operation /// True if x and y are equal [Pure] public static bool Equals(HashSet x, HashSet y) { if (x.Count != y.Count) return false; using var enumx = x.GetEnumerator(); using var enumy = y.GetEnumerator(); for (var i = 0; i < x.Count; i++) { enumx.MoveNext(); enumy.MoveNext(); if (!EQ.Equals(enumx.Current, enumy.Current)) return false; } return true; } /// /// Get hash code of the value /// /// Value to get the hash code of /// The hash code of x [Pure] public static int GetHashCode(HashSet x) => HashableHashSet.GetHashCode(x); } /// /// HashSet equality /// public struct EqHashSet : Eq> { /// /// Equality test /// /// The left hand side of the equality operation /// The right hand side of the equality operation /// True if x and y are equal [Pure] public static bool Equals(HashSet x, HashSet y) => EqHashSet, A>.Equals(x, y); /// /// Get hash code of the value /// /// Value to get the hash code of /// The hash code of x [Pure] public static int GetHashCode(HashSet x) => HashableHashSet.GetHashCode(x); } ================================================ FILE: LanguageExt.Core/Class Instances/Eq/EqIdentity.cs ================================================ using LanguageExt.Traits; using System.Diagnostics.Contracts; namespace LanguageExt.ClassInstances; /// /// Identity equality /// public struct EqIdentity : Eq> { /// /// Equality test /// /// The left hand side of the equality operation /// The right hand side of the equality operation /// True if x and y are equal [Pure] public static bool Equals(Identity a, Identity b) => a == b; /// /// Get hash code of the value /// /// Value to get the hash code of /// The hash code of x [Pure] public static int GetHashCode(Identity x) => HashableIdentity.GetHashCode(x); } ================================================ FILE: LanguageExt.Core/Class Instances/Eq/EqInt.cs ================================================ using LanguageExt.Traits; using System.Diagnostics.Contracts; namespace LanguageExt.ClassInstances; /// /// Integer equality /// public struct EqInt : Eq { /// /// Equality test /// /// The left hand side of the equality operation /// The right hand side of the equality operation /// True if x and y are equal [Pure] public static bool Equals(int a, int b) => a == b; /// /// Get hash code of the value /// /// Value to get the hash code of /// The hash code of x [Pure] public static int GetHashCode(int x) => HashableInt.GetHashCode(x); } ================================================ FILE: LanguageExt.Core/Class Instances/Eq/EqIterable.cs ================================================ using LanguageExt.Traits; using System.Collections.Generic; using System.Diagnostics.Contracts; namespace LanguageExt.ClassInstances; /// /// Equality test /// /// The left hand side of the equality operation /// The right hand side of the equality operation /// True if x and y are equal public struct EqIterable : Eq> where EQ : Eq { /// /// Equality check /// [Pure] public static bool Equals(Iterable x, Iterable y) { using var enumx = x.GetEnumerator(); using var enumy = y.GetEnumerator(); while (true) { var a = enumx.MoveNext(); var b = enumy.MoveNext(); if (a != b) return false; if (!a && !b) return true; if (!EQ.Equals(enumx.Current, enumy.Current)) return false; } } /// /// Get hash code of the value /// /// Value to get the hash code of /// The hash code of x [Pure] public static int GetHashCode(Iterable x) => HashableIterable.GetHashCode(x); } /// /// Equality test /// /// The left hand side of the equality operation /// The right hand side of the equality operation /// True if x and y are equal public struct EqIterable : Eq> { /// /// Equality check /// [Pure] public static bool Equals(Iterable x, Iterable y) => EqIterable, A>.Equals(x, y); /// /// Get hash code of the value /// /// Value to get the hash code of /// The hash code of x [Pure] public static int GetHashCode(Iterable x) => HashableIterable.GetHashCode(x); } ================================================ FILE: LanguageExt.Core/Class Instances/Eq/EqLong.cs ================================================ using LanguageExt.Traits; using System.Diagnostics.Contracts; namespace LanguageExt.ClassInstances; /// /// Integer equality /// public struct EqLong : Eq { /// /// Equality test /// /// The left hand side of the equality operation /// The right hand side of the equality operation /// True if x and y are equal [Pure] public static bool Equals(long a, long b) => a == b; /// /// Get hash code of the value /// /// Value to get the hash code of /// The hash code of x [Pure] public static int GetHashCode(long x) => HashableLong.GetHashCode(x); } ================================================ FILE: LanguageExt.Core/Class Instances/Eq/EqLst.cs ================================================ using LanguageExt.Traits; using System.Diagnostics.Contracts; namespace LanguageExt.ClassInstances; /// /// Equality test /// /// The left hand side of the equality operation /// The right hand side of the equality operation /// True if x and y are equal public struct EqLst : Eq> where EQ : Eq { [Pure] public static bool Equals(Lst x, Lst y) { if (x.Count != y.Count) return false; using var enumx = x.GetEnumerator(); using var enumy = y.GetEnumerator(); var count = x.Count; for (var i = 0; i < count; i++) { enumx.MoveNext(); enumy.MoveNext(); if (!EQ.Equals(enumx.Current, enumy.Current)) return false; } return true; } /// /// Get hash code of the value /// /// Value to get the hash code of /// The hash code of x [Pure] public static int GetHashCode(Lst x) => HashableLst.GetHashCode(x); } /// /// Equality test /// /// The left hand side of the equality operation /// The right hand side of the equality operation /// True if x and y are equal public struct EqLst : Eq> { [Pure] public static bool Equals(Lst x, Lst y) => EqLst, A>.Equals(x, y); /// /// Get hash code of the value /// /// Value to get the hash code of /// The hash code of x [Pure] public static int GetHashCode(Lst x) => HashableLst.GetHashCode(x); } ================================================ FILE: LanguageExt.Core/Class Instances/Eq/EqMap.cs ================================================ using LanguageExt.Traits; using System.Diagnostics.Contracts; namespace LanguageExt.ClassInstances; public struct EqMap : Eq> { /// /// Equality test /// /// The left hand side of the equality operation /// The right hand side of the equality operation /// True if `x` and `y` are equal [Pure] public static bool Equals(Map x, Map y) => x.Equals(y); /// /// Get the hash-code of the provided value /// /// Hash code of `x` [Pure] public static int GetHashCode(Map x) => HashableMap.GetHashCode(x); } public struct EqMap : Eq> where OrdK : Ord { /// /// Equality test /// /// The left hand side of the equality operation /// The right hand side of the equality operation /// True if `x` and `y` are equal [Pure] public static bool Equals(Map x, Map y) => x.Equals(y); /// /// Get the hash-code of the provided value /// /// Hash code of `x` [Pure] public static int GetHashCode(Map x) => HashableMap.GetHashCode(x); } ================================================ FILE: LanguageExt.Core/Class Instances/Eq/EqOption.cs ================================================ using System.Diagnostics.Contracts; using LanguageExt.Traits; namespace LanguageExt.ClassInstances; /// /// Option type equality /// public struct EqOption : Eq> { [Pure] public static bool Equals(Option x, Option y) => x.Equals(y); [Pure] public static int GetHashCode(Option x) => HashableOption.GetHashCode(x); } ================================================ FILE: LanguageExt.Core/Class Instances/Eq/EqPatch.cs ================================================ using System.Diagnostics.Contracts; using LanguageExt.Traits; namespace LanguageExt.ClassInstances; /// /// Equality instance for `Patch` /// public struct EqPatch : Eq> where EqA : Eq { [Pure] public static bool Equals(Patch x, Patch y) => x == y; [Pure] public static int GetHashCode(Patch x) => HashablePatch.GetHashCode(x); } ================================================ FILE: LanguageExt.Core/Class Instances/Eq/EqQue.cs ================================================ using LanguageExt.Traits; using System.Diagnostics.Contracts; using System.Threading.Tasks; namespace LanguageExt.ClassInstances; /// /// Equality test /// /// The left hand side of the equality operation /// The right hand side of the equality operation /// True if x and y are equal public struct EqQue : Eq> where EQ : Eq { [Pure] public static bool Equals(Que x, Que y) { if (x.Count != y.Count) return false; using var enumx = x.GetEnumerator(); using var enumy = y.GetEnumerator(); var count = x.Count; for (var i = 0; i < count; i++) { enumx.MoveNext(); enumy.MoveNext(); if (!EQ.Equals(enumx.Current, enumy.Current)) return false; } return true; } /// /// Get hash code of the value /// /// Value to get the hash code of /// The hash code of x [Pure] public static int GetHashCode(Que x) => HashableQue.GetHashCode(x); } /// /// Equality test /// /// The left hand side of the equality operation /// The right hand side of the equality operation /// True if x and y are equal public struct EqQue : Eq> { [Pure] public static bool Equals(Que x, Que y) => EqQue, A>.Equals(x, y); /// /// Get hash code of the value /// /// Value to get the hash code of /// The hash code of x [Pure] public static int GetHashCode(Que x) => HashableQue.GetHashCode(x); } ================================================ FILE: LanguageExt.Core/Class Instances/Eq/EqRecord.cs ================================================ using System.Diagnostics.Contracts; using LanguageExt.Traits; namespace LanguageExt.ClassInstances; /// /// Equality class instance for all record types /// /// Record type public struct EqRecord : Eq where A : Record { [Pure] public static bool Equals(A x, A y) => RecordType.EqualityTyped(x, y); [Pure] public static int GetHashCode(A x) => HashableRecord.GetHashCode(x); } ================================================ FILE: LanguageExt.Core/Class Instances/Eq/EqSeq.cs ================================================ using LanguageExt.Traits; using System.Diagnostics.Contracts; namespace LanguageExt.ClassInstances; /// /// Equality test /// /// The left hand side of the equality operation /// The right hand side of the equality operation /// True if x and y are equal public struct EqSeq : Eq> where EqA : Eq { /// /// Equality check /// [Pure] public static bool Equals(Seq x, Seq y) { if (x.Count != y.Count) return false; while (true) { var a = x.IsEmpty; var b = y.IsEmpty; if (a != b) return false; if (a && b) return true; if (!EqA.Equals((A)x.Head,(A)y.Head)) return false; x = x.Tail; y = y.Tail; } } /// /// Get hash code of the value /// /// Value to get the hash code of /// The hash code of x [Pure] public static int GetHashCode(Seq x) => HashableSeq.GetHashCode(x); } /// /// Equality test /// /// The left hand side of the equality operation /// The right hand side of the equality operation /// True if x and y are equal public struct EqSeq : Eq> { /// /// Equality check /// [Pure] public static bool Equals(Seq x, Seq y) => EqSeq, A>.Equals(x, y); /// /// Get hash code of the value /// /// Value to get the hash code of /// The hash code of x [Pure] public static int GetHashCode(Seq x) => HashableSeq.GetHashCode(x); } ================================================ FILE: LanguageExt.Core/Class Instances/Eq/EqSet.cs ================================================ using LanguageExt.Traits; using System.Diagnostics.Contracts; namespace LanguageExt.ClassInstances; /// /// Set〈T〉 equality /// public struct EqSet : Eq> where EQ : Eq { /// /// Equality test /// /// The left hand side of the equality operation /// The right hand side of the equality operation /// True if x and y are equal [Pure] public static bool Equals(Set x, Set y) { if (x.Count != y.Count) return false; using var enumx = x.GetEnumerator(); using var enumy = y.GetEnumerator(); for (var i = 0; i < x.Count; i++) { enumx.MoveNext(); enumy.MoveNext(); if (!EQ.Equals(enumx.Current, enumy.Current)) return false; } return true; } /// /// Get hash code of the value /// /// Value to get the hash code of /// The hash code of x [Pure] public static int GetHashCode(Set x) => HashableSet.GetHashCode(x); } /// /// Set〈T〉 equality /// public struct EqSet : Eq> { /// /// Equality test /// /// The left hand side of the equality operation /// The right hand side of the equality operation /// True if x and y are equal [Pure] public static bool Equals(Set x, Set y) => EqSet, A>.Equals(x, y); /// /// Get hash code of the value /// /// Value to get the hash code of /// The hash code of x [Pure] public static int GetHashCode(Set x) => HashableSet.GetHashCode(x); } ================================================ FILE: LanguageExt.Core/Class Instances/Eq/EqShort.cs ================================================ using LanguageExt.Traits; using System.Diagnostics.Contracts; namespace LanguageExt.ClassInstances; /// /// Integer equality /// public struct EqShort : Eq { /// /// Equality test /// /// The left hand side of the equality operation /// The right hand side of the equality operation /// True if x and y are equal [Pure] public static bool Equals(short a, short b) => a == b; /// /// Get hash code of the value /// /// Value to get the hash code of /// The hash code of x [Pure] public static int GetHashCode(short x) => HashableShort.GetHashCode(x); } ================================================ FILE: LanguageExt.Core/Class Instances/Eq/EqStck.cs ================================================ using LanguageExt.Traits; using System.Diagnostics.Contracts; namespace LanguageExt.ClassInstances; /// /// Equality test /// /// The left hand side of the equality operation /// The right hand side of the equality operation /// True if x and y are equal public struct EqStck : Eq> where EQ : Eq { [Pure] public static bool Equals(Stck x, Stck y) { if (x.Count != y.Count) return false; using var enumx = x.GetEnumerator(); using var enumy = y.GetEnumerator(); var count = x.Count; for (var i = 0; i < count; i++) { enumx.MoveNext(); enumy.MoveNext(); if (!EQ.Equals(enumx.Current, enumy.Current)) return false; } return true; } /// /// Get hash code of the value /// /// Value to get the hash code of /// The hash code of x [Pure] public static int GetHashCode(Stck x) => HashableStck.GetHashCode(x); } /// /// Equality test /// /// The left hand side of the equality operation /// The right hand side of the equality operation /// True if x and y are equal public struct EqStck : Eq> { [Pure] public static bool Equals(Stck x, Stck y) => EqStck, A>.Equals(x, y); /// /// Get hash code of the value /// /// Value to get the hash code of /// The hash code of x [Pure] public static int GetHashCode(Stck x) => HashableStck.GetHashCode(x); } ================================================ FILE: LanguageExt.Core/Class Instances/Eq/EqString.cs ================================================ using System; using LanguageExt.Traits; using System.Diagnostics.Contracts; namespace LanguageExt.ClassInstances; /// /// String equality /// public struct EqString : Eq { /// /// Equality test /// /// The left hand side of the equality operation /// The right hand side of the equality operation /// True if a and b are equal [Pure] public static bool Equals(string a, string b) => a == b; /// /// Get hash code of the value /// /// Value to get the hash code of /// The hash code of x [Pure] public static int GetHashCode(string x) => HashableString.GetHashCode(x); } /// /// String equality (invariant culture) /// public struct EqStringInvariantCulture : Eq { /// /// Equality test /// /// The left hand side of the equality operation /// The right hand side of the equality operation /// True if a and b are equal [Pure] public static bool Equals(string a, string b) => StringComparer.InvariantCulture.Equals(a, b); /// /// Get hash code of the value /// /// Value to get the hash code of /// The hash code of x [Pure] public static int GetHashCode(string x) => HashableStringInvariantCulture.GetHashCode(x); } /// /// String equality (invariant culture, ignore case) /// public struct EqStringInvariantCultureIgnoreCase : Eq { /// /// Equality test /// /// The left hand side of the equality operation /// The right hand side of the equality operation /// True if a and b are equal [Pure] public static bool Equals(string a, string b) => StringComparer.InvariantCultureIgnoreCase.Equals(a, b); /// /// Get hash code of the value /// /// Value to get the hash code of /// The hash code of x [Pure] public static int GetHashCode(string x) => HashableStringInvariantCultureIgnoreCase.GetHashCode(x); } /// /// String equality (ordinal, ignore case) /// public struct EqStringOrdinalIgnoreCase : Eq { /// /// Equality test /// /// The left hand side of the equality operation /// The right hand side of the equality operation /// True if a and b are equal [Pure] public static bool Equals(string a, string b) => StringComparer.OrdinalIgnoreCase.Equals(a, b); /// /// Get hash code of the value /// /// Value to get the hash code of /// The hash code of x [Pure] public static int GetHashCode(string x) => HashableStringOrdinalIgnoreCase.GetHashCode(x); } /// /// String equality (ordinal) /// public struct EqStringOrdinal : Eq { public static readonly EqStringOrdinal Inst = default; /// /// Equality test /// /// The left hand side of the equality operation /// The right hand side of the equality operation /// True if a and b are equal [Pure] public static bool Equals(string a, string b) => StringComparer.Ordinal.Equals(a, b); /// /// Get hash code of the value /// /// Value to get the hash code of /// The hash code of x [Pure] public static int GetHashCode(string x) => HashableStringOrdinal.GetHashCode(x); } /// /// String equality (current culture, ignore case) /// public struct EqStringCurrentCultureIgnoreCase : Eq { /// /// Equality test /// /// The left hand side of the equality operation /// The right hand side of the equality operation /// True if a and b are equal [Pure] public static bool Equals(string a, string b) => StringComparer.CurrentCultureIgnoreCase.Equals(a, b); /// /// Get hash code of the value /// /// Value to get the hash code of /// The hash code of x [Pure] public static int GetHashCode(string x) => HashableStringCurrentCultureIgnoreCase.GetHashCode(x); } /// /// String equality (current culture) /// public struct EqStringCurrentCulture : Eq { public static readonly EqStringCurrentCulture Inst = default; /// /// Equality test /// /// The left hand side of the equality operation /// The right hand side of the equality operation /// True if a and b are equal [Pure] public static bool Equals(string a, string b) => StringComparer.CurrentCulture.Equals(a, b); /// /// Get hash code of the value /// /// Value to get the hash code of /// The hash code of x [Pure] public static int GetHashCode(string x) => HashableStringCurrentCulture.GetHashCode(x); } ================================================ FILE: LanguageExt.Core/Class Instances/Eq/EqTask.cs ================================================ using System; using System.Diagnostics.Contracts; using LanguageExt.Traits; using System.Threading.Tasks; namespace LanguageExt.ClassInstances; public struct EqTask : Eq> { [Pure] public static bool Equals(Task x, Task y) => x.Id == y.Id; [Pure] public static int GetHashCode(Task x) => HashableTask.GetHashCode(x); } ================================================ FILE: LanguageExt.Core/Class Instances/Eq/EqTrue.cs ================================================ using System.Diagnostics.Contracts; using LanguageExt.Traits; namespace LanguageExt.ClassInstances; /// /// Always returns true for equality checks /// public struct EqTrue : Eq { [Pure] public static bool Equals(A x, A y) => true; [Pure] public static int GetHashCode(A x) => EqDefault.GetHashCode(x); } ================================================ FILE: LanguageExt.Core/Class Instances/Eq/EqTuple2.cs ================================================ using LanguageExt.Traits; namespace LanguageExt.ClassInstances; public struct EqTuple2 : Eq<(A, B)> where EqA : Eq where EqB : Eq { public static int GetHashCode((A, B) pair) => FNV32.Next(EqA.GetHashCode(pair.Item1), EqB.GetHashCode(pair.Item2)); public static bool Equals((A, B) x, (A, B) y) => EqA.Equals(x.Item1, y.Item1) && EqB.Equals(x.Item2, y.Item2); } ================================================ FILE: LanguageExt.Core/Class Instances/Eq/EqTypeInfo.cs ================================================ using System.Diagnostics.Contracts; using LanguageExt.Traits; using System.Reflection; namespace LanguageExt.ClassInstances; public struct EqTypeInfo : Eq { [Pure] public static bool Equals(TypeInfo x, TypeInfo y) => x.Equals(y); [Pure] public static int GetHashCode(TypeInfo x) => HashableTypeInfo.GetHashCode(x); } ================================================ FILE: LanguageExt.Core/Class Instances/Hashable/HashableArr.cs ================================================ using System.Diagnostics.Contracts; using static LanguageExt.Prelude; namespace LanguageExt.ClassInstances; /// /// Array hash /// public struct HashableArr : Hashable> where HashA : Hashable { /// /// Get hash code of the value /// /// Value to get the hash code of /// The hash code of x [Pure] public static int GetHashCode(Arr x) => hash(x); } /// /// Array hash /// public struct HashableArr : Hashable> { /// /// Get hash code of the value /// /// Value to get the hash code of /// The hash code of x [Pure] public static int GetHashCode(Arr x) => HashableArr, A>.GetHashCode(x); } ================================================ FILE: LanguageExt.Core/Class Instances/Hashable/HashableArray.cs ================================================ using System.Diagnostics.Contracts; using static LanguageExt.Prelude; namespace LanguageExt.ClassInstances; /// /// Array hash /// public struct HashableArray : Hashable where HashA : Hashable { /// /// Get hash code of the value /// /// Value to get the hash code of /// The hash code of x [Pure] public static int GetHashCode(A[] x) => hash(x); } /// /// Array hash /// public struct HashableArray : Hashable { /// /// Get hash code of the value /// /// Value to get the hash code of /// The hash code of x [Pure] public static int GetHashCode(A[] x) => HashableArray, A>.GetHashCode(x); } ================================================ FILE: LanguageExt.Core/Class Instances/Hashable/HashableBigInt.cs ================================================ using System.Diagnostics.Contracts; namespace LanguageExt.ClassInstances; /// /// bigint hash /// public struct HashableBigInt : Hashable { /// /// Get hash code of the value /// /// Value to get the hash code of /// The hash code of x [Pure] public static int GetHashCode(bigint x) => x.GetHashCode(); } ================================================ FILE: LanguageExt.Core/Class Instances/Hashable/HashableBool.cs ================================================ using System.Diagnostics.Contracts; namespace LanguageExt.ClassInstances; /// /// Boolean hash /// public struct HashableBool : Hashable { /// /// Get hash code of the value /// /// Value to get the hash code of /// The hash code of x [Pure] public static int GetHashCode(bool x) => x.GetHashCode(); } ================================================ FILE: LanguageExt.Core/Class Instances/Hashable/HashableChar.cs ================================================ using System.Diagnostics.Contracts; using System.Threading.Tasks; namespace LanguageExt.ClassInstances; /// /// Char hash /// public struct HashableChar : Hashable { /// /// Get hash code of the value /// /// Value to get the hash code of /// The hash code of x [Pure] public static int GetHashCode(char x) => x.GetHashCode(); } /// /// Char hash (ordinal, ignore case) /// public struct HashableCharOrdinalIgnoreCase : Hashable { /// /// Get hash code of the value /// /// Value to get the hash code of /// The hash code of x [Pure] public static int GetHashCode(char x) => x is >= 'a' and <= 'z' ? x - 0x20 : x; } ================================================ FILE: LanguageExt.Core/Class Instances/Hashable/HashableCompositions.cs ================================================ using System.Diagnostics.Contracts; using LanguageExt.Traits; namespace LanguageExt.ClassInstances; public struct HashableCompositions : Hashable> where A : Monoid { [Pure] public static int GetHashCode(Compositions x) => x.GetHashCode(); } ================================================ FILE: LanguageExt.Core/Class Instances/Hashable/HashableDateTime.cs ================================================ using System; using System.Diagnostics.Contracts; namespace LanguageExt.ClassInstances; /// /// DateTime hash /// public struct HashableDateTime : Hashable { /// /// Get hash code of the value /// /// Value to get the hash code of /// The hash code of x [Pure] public static int GetHashCode(DateTime x) => x.GetHashCode(); } ================================================ FILE: LanguageExt.Core/Class Instances/Hashable/HashableDecimal.cs ================================================ using System.Diagnostics.Contracts; namespace LanguageExt.ClassInstances; /// /// Floating point hash /// public struct HashableDecimal : Hashable { /// /// Get hash code of the value /// /// Value to get the hash code of /// The hash code of x [Pure] public static int GetHashCode(decimal x) => x.GetHashCode(); } ================================================ FILE: LanguageExt.Core/Class Instances/Hashable/HashableDefault.cs ================================================ using System.Diagnostics.Contracts; using LanguageExt.Traits.Resolve; namespace LanguageExt.ClassInstances; /// /// Finds an appropriate Hashable from the loaded assemblies, if one can't be found then it /// falls back to the standard .NET Object.GetHashCode() method to provide a hash-code. /// public struct HashableDefault : Hashable { /// /// Get hash code of the value /// /// Value to get the hash code of /// The hash code of x [Pure] public static int GetHashCode(A x) => HashableResolve.GetHashCode(x); } ================================================ FILE: LanguageExt.Core/Class Instances/Hashable/HashableDouble.cs ================================================ using System.Diagnostics.Contracts; namespace LanguageExt.ClassInstances; /// /// Floating point hash /// public struct HashableDouble : Hashable { /// /// Get hash code of the value /// /// Value to get the hash code of /// The hash code of x [Pure] public static int GetHashCode(double x) => x.GetHashCode(); } ================================================ FILE: LanguageExt.Core/Class Instances/Hashable/HashableEdit.cs ================================================ using System.Diagnostics.Contracts; using LanguageExt.Traits; namespace LanguageExt.ClassInstances; /// /// Hash for `Patch` `Edit` /// public struct HashableEdit : Hashable> where EqA : Eq { [Pure] public static int GetHashCode(Edit x) => x.GetHashCode(); } ================================================ FILE: LanguageExt.Core/Class Instances/Hashable/HashableEither.cs ================================================ using System.Diagnostics.Contracts; namespace LanguageExt.ClassInstances; /// /// Either type hashing /// public readonly struct HashableEither : Hashable> where HashableL : Hashable where HashableR : Hashable { /// /// Get hash code of the value /// /// Value to get the hash code of /// The hash code of x [Pure] public static int GetHashCode(Either x) => x switch { Either.Right => HashableR.GetHashCode(x.RightValue), Either.Left => HashableL.GetHashCode(x.LeftValue), _ => 0 }; } /// /// Either type hashing /// public readonly struct HashableEither : Hashable> { /// /// Get hash code of the value /// /// Value to get the hash code of /// The hash code of x [Pure] public static int GetHashCode(Either x) => HashableEither, HashableDefault, L, R>.GetHashCode(x); } ================================================ FILE: LanguageExt.Core/Class Instances/Hashable/HashableEnumerable.cs ================================================ using static LanguageExt.Prelude; using System.Collections.Generic; using System.Diagnostics.Contracts; namespace LanguageExt.ClassInstances; /// /// Enumerable hashing /// public struct HashableEnumerable : Hashable> where HashA : Hashable { /// /// Get hash code of the value /// /// Value to get the hash code of /// The hash code of x [Pure] public static int GetHashCode(IEnumerable x) => hash(x); } /// /// Enumerable hashing /// public struct HashableEnumerable : Hashable> { /// /// Get hash code of the value /// /// Value to get the hash code of /// The hash code of x [Pure] public static int GetHashCode(IEnumerable x) => HashableEnumerable, A>.GetHashCode(x); } ================================================ FILE: LanguageExt.Core/Class Instances/Hashable/HashableException.cs ================================================ using System; using System.Diagnostics.Contracts; namespace LanguageExt.ClassInstances; public struct HashableException : Hashable { [Pure] public static int GetHashCode(Exception x) => x.GetType().GetHashCode(); } ================================================ FILE: LanguageExt.Core/Class Instances/Hashable/HashableFloat.cs ================================================ using System.Diagnostics.Contracts; namespace LanguageExt.ClassInstances; /// /// Floating point hash /// public struct HashableFloat : Hashable { /// /// Get hash code of the value /// /// Value to get the hash code of /// The hash code of x [Pure] public static int GetHashCode(float x) => x.GetHashCode(); } ================================================ FILE: LanguageExt.Core/Class Instances/Hashable/HashableGuid.cs ================================================ using System; using System.Diagnostics.Contracts; namespace LanguageExt.ClassInstances; /// /// Guid hash /// public struct HashableGuid : Hashable { /// /// Get hash code of the value /// /// Value to get the hash code of /// The hash code of x [Pure] public static int GetHashCode(Guid x) => x.GetHashCode(); } ================================================ FILE: LanguageExt.Core/Class Instances/Hashable/HashableHashSet.cs ================================================ using System.Diagnostics.Contracts; using static LanguageExt.Prelude; namespace LanguageExt.ClassInstances; /// /// HashSet hash /// public struct HashableHashSet : Hashable> where HashA : Hashable { /// /// Get hash code of the value /// /// Value to get the hash code of /// The hash code of x [Pure] public static int GetHashCode(HashSet x) => hash(x); } /// /// HashSet hash /// public struct HashableHashSet : Hashable> { /// /// Get hash code of the value /// /// Value to get the hash code of /// The hash code of x [Pure] public static int GetHashCode(HashSet x) => HashableHashSet, A>.GetHashCode(x); } ================================================ FILE: LanguageExt.Core/Class Instances/Hashable/HashableIdentity.cs ================================================ using System.Diagnostics.Contracts; namespace LanguageExt.ClassInstances; /// /// Identity hashing /// public struct HashableIdentity : Hashable> { /// /// Get hash code of the value /// /// Value to get the hash code of /// The hash code of x [Pure] public static int GetHashCode(Identity x) => x.GetHashCode(); } ================================================ FILE: LanguageExt.Core/Class Instances/Hashable/HashableInt.cs ================================================ using System.Diagnostics.Contracts; namespace LanguageExt.ClassInstances; /// /// Integer hash /// public struct HashableInt : Hashable { /// /// Get hash code of the value /// /// Value to get the hash code of /// The hash code of x [Pure] public static int GetHashCode(int x) => x.GetHashCode(); } ================================================ FILE: LanguageExt.Core/Class Instances/Hashable/HashableIterable.cs ================================================ using static LanguageExt.Prelude; using System.Collections.Generic; using System.Diagnostics.Contracts; namespace LanguageExt.ClassInstances; /// /// Iterable hashing /// public struct HashableIterable : Hashable> where HashA : Hashable { /// /// Get hash code of the value /// /// Value to get the hash code of /// The hash code of x [Pure] public static int GetHashCode(Iterable x) => hash(x); } /// /// Iterable hashing /// public struct HashableIterable : Hashable> { /// /// Get hash code of the value /// /// Value to get the hash code of /// The hash code of x [Pure] public static int GetHashCode(Iterable x) => HashableIterable, A>.GetHashCode(x); } ================================================ FILE: LanguageExt.Core/Class Instances/Hashable/HashableLong.cs ================================================ using System.Diagnostics.Contracts; namespace LanguageExt.ClassInstances; /// /// Integer hash /// public struct HashableLong : Hashable { /// /// Get hash code of the value /// /// Value to get the hash code of /// The hash code of x [Pure] public static int GetHashCode(long x) => x.GetHashCode(); } ================================================ FILE: LanguageExt.Core/Class Instances/Hashable/HashableLst.cs ================================================ using static LanguageExt.Prelude; using System.Diagnostics.Contracts; namespace LanguageExt.ClassInstances; /// /// Lst hash /// public struct HashableLst : Hashable> where HashA : Hashable { /// /// Get hash code of the value /// /// Value to get the hash code of /// The hash code of x [Pure] public static int GetHashCode(Lst x) => hash(x); } /// /// Lst hash /// public struct HashableLst : Hashable> { /// /// Get hash code of the value /// /// Value to get the hash code of /// The hash code of x [Pure] public static int GetHashCode(Lst x) => HashableLst, A>.GetHashCode(x); } ================================================ FILE: LanguageExt.Core/Class Instances/Hashable/HashableMap.cs ================================================ using LanguageExt.Traits; using System.Diagnostics.Contracts; namespace LanguageExt.ClassInstances; public struct HashableMap : Hashable> { /// /// Get the hash-code of the provided value /// /// Hash code of `x` [Pure] public static int GetHashCode(Map x) => x.GetHashCode(); } public struct HashableMap : Hashable> where OrdK : Ord { /// /// Get the hash-code of the provided value /// /// Hash code of `x` [Pure] public static int GetHashCode(Map x) => x.GetHashCode(); } ================================================ FILE: LanguageExt.Core/Class Instances/Hashable/HashableOption.cs ================================================ namespace LanguageExt.ClassInstances; /// /// Option type equality /// public struct HashableOption : Hashable> { /// /// Get hash code of the value /// /// Value to get the hash code of /// The hash code of x public static int GetHashCode(Option x) => x.GetHashCode(); } ================================================ FILE: LanguageExt.Core/Class Instances/Hashable/HashablePair.cs ================================================ using System.Diagnostics.Contracts; using System.Runtime.CompilerServices; namespace LanguageExt; public struct HashablePair : Hashable<(A, B)> where HashA : Hashable where HashB : Hashable { [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int GetHashCode((A, B) pair) => (HashB.GetHashCode(pair.Item2) ^ (HashA.GetHashCode(pair.Item1) ^ FNV32.OffsetBasis) * FNV32.Prime) * FNV32.Prime; } ================================================ FILE: LanguageExt.Core/Class Instances/Hashable/HashablePatch.cs ================================================ using System.Diagnostics.Contracts; using System.Runtime.CompilerServices; using LanguageExt.Traits; namespace LanguageExt.ClassInstances; /// /// Hash for `Patch` /// public struct HashablePatch : Hashable> where EqA : Eq { /// /// Get hash code of the value /// /// Value to get the hash code of /// The hash code of x [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int GetHashCode(Patch x) => x.GetHashCode(); } ================================================ FILE: LanguageExt.Core/Class Instances/Hashable/HashableQue.cs ================================================ using System.Diagnostics.Contracts; namespace LanguageExt.ClassInstances; /// /// Queue hashing /// public struct HashableQue : Hashable> where HashA : Hashable { /// /// Get hash code of the value /// /// Value to get the hash code of /// The hash code of x [Pure] public static int GetHashCode(Que x) => Prelude.hash(x); } /// /// Queue hashing /// public struct HashableQue : Hashable> { /// /// Get hash code of the value /// /// Value to get the hash code of /// The hash code of x [Pure] public static int GetHashCode(Que x) => HashableQue, A>.GetHashCode(x); } ================================================ FILE: LanguageExt.Core/Class Instances/Hashable/HashableRecord.cs ================================================ using System.Diagnostics.Contracts; using System.Runtime.CompilerServices; namespace LanguageExt.ClassInstances; /// /// Hash for all record types /// /// Record type public struct HashableRecord : Hashable where A : Record { /// /// Get hash code of the value /// /// Value to get the hash code of /// The hash code of x [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int GetHashCode(A x) => RecordType.Hash(x); } ================================================ FILE: LanguageExt.Core/Class Instances/Hashable/HashableSeq.cs ================================================ using static LanguageExt.Prelude; using System.Diagnostics.Contracts; namespace LanguageExt.ClassInstances; /// /// Seq hashing /// public struct HashableSeq : Hashable> where HashA : Hashable { /// /// Get hash code of the value /// /// Value to get the hash code of /// The hash code of x [Pure] public static int GetHashCode(Seq x) => hash(x); } /// /// Seq hashing /// public struct HashableSeq : Hashable> { /// /// Get hash code of the value /// /// Value to get the hash code of /// The hash code of x [Pure] public static int GetHashCode(Seq x) => HashableSeq, A>.GetHashCode(x); } ================================================ FILE: LanguageExt.Core/Class Instances/Hashable/HashableSet.cs ================================================ using System.Diagnostics.Contracts; namespace LanguageExt.ClassInstances; /// /// Set〈T〉 hashing /// public struct HashableSet : Hashable> where HashA : Hashable { /// /// Get hash code of the value /// /// Value to get the hash code of /// The hash code of x [Pure] public static int GetHashCode(Set x) => Prelude.hash(x); } /// /// Set〈T〉 hashing /// public struct HashableSet : Hashable> { /// /// Get hash code of the value /// /// Value to get the hash code of /// The hash code of x [Pure] public static int GetHashCode(Set x) => HashableSet, A>.GetHashCode(x); } ================================================ FILE: LanguageExt.Core/Class Instances/Hashable/HashableShort.cs ================================================ using System.Diagnostics.Contracts; using System.Runtime.CompilerServices; namespace LanguageExt.ClassInstances; /// /// Integer hashing /// public struct HashableShort : Hashable { /// /// Get hash code of the value /// /// Value to get the hash code of /// The hash code of x [Pure] public static int GetHashCode(short x) => x.GetHashCode(); } ================================================ FILE: LanguageExt.Core/Class Instances/Hashable/HashableStck.cs ================================================ using System.Diagnostics.Contracts; using System.Runtime.CompilerServices; namespace LanguageExt.ClassInstances; /// /// Stack hashing /// public struct HashableStck : Hashable> where HashA : Hashable { /// /// Get hash code of the value /// /// Value to get the hash code of /// The hash code of x [Pure] public static int GetHashCode(Stck x) => Prelude.hash(x); } /// /// Stack hashing /// public struct HashableStck : Hashable> { /// /// Get hash code of the value /// /// Value to get the hash code of /// The hash code of x [Pure] public static int GetHashCode(Stck x) => HashableStck, A>.GetHashCode(x); } ================================================ FILE: LanguageExt.Core/Class Instances/Hashable/HashableString.cs ================================================ using System; using System.Diagnostics.Contracts; using System.Runtime.CompilerServices; namespace LanguageExt.ClassInstances; /// /// String hashing /// public struct HashableString : Hashable { /// /// Get hash code of the value /// /// Value to get the hash code of /// The hash code of x [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int GetHashCode(string x) => x == null ? 0 : x.GetHashCode(); } /// /// String hashing (invariant culture) /// public struct HashableStringInvariantCulture : Hashable { /// /// Get hash code of the value /// /// Value to get the hash code of /// The hash code of x [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int GetHashCode(string x) => StringComparer.InvariantCulture.GetHashCode(x); } /// /// String hashing (invariant culture, ignore case) /// public struct HashableStringInvariantCultureIgnoreCase : Hashable { /// /// Get hash code of the value /// /// Value to get the hash code of /// The hash code of x [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int GetHashCode(string x) => x.IsNull() ? 0 : StringComparer.InvariantCultureIgnoreCase.GetHashCode(x); } /// /// String equality (ordinal, ignore case) /// public struct HashableStringOrdinalIgnoreCase : Hashable { /// /// Get hash code of the value /// /// Value to get the hash code of /// The hash code of x [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int GetHashCode(string x) => x.IsNull() ? 0 : StringComparer.OrdinalIgnoreCase.GetHashCode(x); } /// /// String equality (ordinal) /// public struct HashableStringOrdinal : Hashable { /// /// Get hash code of the value /// /// Value to get the hash code of /// The hash code of x [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int GetHashCode(string x) => x.IsNull() ? 0 : StringComparer.Ordinal.GetHashCode(x); } /// /// String equality (current culture, ignore case) /// public struct HashableStringCurrentCultureIgnoreCase : Hashable { /// /// Get hash code of the value /// /// Value to get the hash code of /// The hash code of x [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int GetHashCode(string x) => x.IsNull() ? 0 : StringComparer.CurrentCultureIgnoreCase.GetHashCode(x); } /// /// String equality (current culture) /// public struct HashableStringCurrentCulture : Hashable { /// /// Get hash code of the value /// /// Value to get the hash code of /// The hash code of x [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int GetHashCode(string x) => x.IsNull() ? 0 : StringComparer.CurrentCulture.GetHashCode(x); } ================================================ FILE: LanguageExt.Core/Class Instances/Hashable/HashableTask.cs ================================================ using System.Diagnostics.Contracts; using System.Runtime.CompilerServices; using System.Threading.Tasks; namespace LanguageExt.ClassInstances; public struct HashableTask : Hashable> { /// /// Get hash code of the value /// /// Value to get the hash code of /// The hash code of x [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int GetHashCode(Task x) => x.Id.GetHashCode(); } ================================================ FILE: LanguageExt.Core/Class Instances/Hashable/HashableTuple.cs ================================================ namespace LanguageExt.ClassInstances; public struct HashableTuple : Hashable<(A, B)> where HashA : Hashable where HashB : Hashable { public static int GetHashCode((A, B) pair) => FNV32.Next(HashA.GetHashCode(pair.Item1), HashB.GetHashCode(pair.Item2)); } ================================================ FILE: LanguageExt.Core/Class Instances/Hashable/HashableTypeInfo.cs ================================================ using System.Diagnostics.Contracts; using System.Reflection; using System.Runtime.CompilerServices; namespace LanguageExt.ClassInstances; public struct HashableTypeInfo : Hashable { /// /// Get hash code of the value /// /// Value to get the hash code of /// The hash code of x [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int GetHashCode(TypeInfo x) => x.GetHashCode(); } ================================================ FILE: LanguageExt.Core/Class Instances/Monoid/Addition.cs ================================================ /* using LanguageExt.Traits; using System.Diagnostics.Contracts; using static LanguageExt.Trait; namespace LanguageExt.ClassInstances; /// /// Numbers form a monoid under addition. /// /// The type of the number being added. public struct Addition : Monoid where NUM : Num { [Pure] public static A Append(A x, A y) => plus(x, y); [Pure] public static A Empty => fromInteger(0); } */ ================================================ FILE: LanguageExt.Core/Class Instances/Monoid/All.cs ================================================ /* using LanguageExt.Traits; using System.Diagnostics.Contracts; namespace LanguageExt.ClassInstances; /// /// Booleans form a monoid under disjunctions. /// public struct All : Monoid, Bool { [Pure] public static bool Append(bool x, bool y) => x && y; [Pure] public static bool Empty => true; [Pure] public static bool And(bool a, bool b) => TBool.And(a, b); [Pure] public static bool BiCondition(bool a, bool b) => TBool.BiCondition(a, b); [Pure] public static bool False() => false; [Pure] public static bool Implies(bool a, bool b) => TBool.Implies(a, b); [Pure] public static bool Not(bool a) => TBool.Not(a); [Pure] public static bool Or(bool a, bool b) => TBool.Or(a, b); [Pure] public static bool True() => true; [Pure] public static bool XOr(bool a, bool b) => TBool.XOr(a, b); } */ ================================================ FILE: LanguageExt.Core/Class Instances/Monoid/Any.cs ================================================ /* using LanguageExt.Traits; using System.Diagnostics.Contracts; namespace LanguageExt.ClassInstances; /// /// Booleans form a monoid under conjunction. /// public struct Any : Monoid, Bool { [Pure] public static bool Append(bool x, bool y) => x || y; [Pure] public static bool Empty => false; [Pure] public static bool And(bool a, bool b) => TBool.And(a, b); [Pure] public static bool BiCondition(bool a, bool b) => TBool.BiCondition(a, b); [Pure] public static bool False() => false; [Pure] public static bool Implies(bool a, bool b) => TBool.Implies(a, b); [Pure] public static bool Not(bool a) => TBool.Not(a); [Pure] public static bool Or(bool a, bool b) => TBool.Or(a, b); [Pure] public static bool True() => true; [Pure] public static bool XOr(bool a, bool b) => TBool.XOr(a, b); } */ ================================================ FILE: LanguageExt.Core/Class Instances/Monoid/MError.cs ================================================ /* using LanguageExt.Traits; using System.Diagnostics.Contracts; using LanguageExt.Common; namespace LanguageExt.ClassInstances; /// /// Error monoid /// public readonly struct MError : Monoid { [Pure] public static Error Append(Error x, Error y) => x + y; [Pure] public static Error Empty => Errors.None; } */ ================================================ FILE: LanguageExt.Core/Class Instances/Monoid/MUnit.cs ================================================ /* using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt.ClassInstances; public struct MUnit : Monoid { public static Unit Append(Unit x, Unit y) => unit; public static Unit Empty => unit; } */ ================================================ FILE: LanguageExt.Core/Class Instances/Monoid/Product.cs ================================================ /* using LanguageExt.Traits; using System.Diagnostics.Contracts; using static LanguageExt.Trait; namespace LanguageExt.ClassInstances; /// /// Numbers form a monoid under addition. /// /// The type of the number being added. public struct Product : Monoid where NUM : Num { public static readonly Product Inst = default; [Pure] public static A Append(A x, A y) => product(x, y); [Pure] public static A Empty => fromInteger(1); } */ ================================================ FILE: LanguageExt.Core/Class Instances/Ord/OrdArr.cs ================================================ using LanguageExt.Traits; using System.Diagnostics.Contracts; namespace LanguageExt.ClassInstances; /// /// Equality and ordering /// public struct OrdArr : Ord> where OrdA : Ord { /// /// Equality test /// /// The left hand side of the equality operation /// The right hand side of the equality operation /// True if x and y are equal [Pure] public static bool Equals(Arr x, Arr y) => EqArr.Equals(x, y); /// /// Compare two values /// /// Left hand side of the compare operation /// Right hand side of the compare operation /// /// if x greater than y : 1 /// if x less than y : -1 /// if x equals y : 0 /// [Pure] public static int Compare(Arr mx, Arr my) { var cmp = mx.Count.CompareTo(my.Count); if (cmp == 0) { var xiter = mx.GetEnumerator(); var yiter = my.GetEnumerator(); while(xiter.MoveNext() && yiter.MoveNext()) { cmp = OrdA.Compare(xiter.Current, yiter.Current); if (cmp != 0) return cmp; } return 0; } else { return cmp; } } /// /// Get the hash-code of the provided value /// /// Hash code of x [Pure] public static int GetHashCode(Arr x) => x.GetHashCode(); } /// /// Equality and ordering /// public struct OrdArr : Ord> { /// /// Equality test /// /// The left hand side of the equality operation /// The right hand side of the equality operation /// True if x and y are equal [Pure] public static bool Equals(Arr x, Arr y) => EqArr.Equals(x, y); /// /// Compare two values /// /// Left hand side of the compare operation /// Right hand side of the compare operation /// /// if x greater than y : 1 /// if x less than y : -1 /// if x equals y : 0 /// [Pure] public static int Compare(Arr mx, Arr my) => OrdArr, A>.Compare(mx, my); /// /// Get the hash-code of the provided value /// /// Hash code of x [Pure] public static int GetHashCode(Arr x) => OrdArr, A>.GetHashCode(x); } ================================================ FILE: LanguageExt.Core/Class Instances/Ord/OrdArray.cs ================================================ using LanguageExt.Traits; using System.Diagnostics.Contracts; using static LanguageExt.Prelude; namespace LanguageExt.ClassInstances; /// /// Equality and ordering /// public struct OrdArray : Ord where OrdA : Ord { /// /// Equality test /// /// The left hand side of the equality operation /// The right hand side of the equality operation /// True if x and y are equal [Pure] public static bool Equals(A[] x, A[] y) => EqArray.Equals(x, y); /// /// Compare two values /// /// Left hand side of the compare operation /// Right hand side of the compare operation /// /// if x greater than y : 1 /// if x less than y : -1 /// if x equals y : 0 /// [Pure] public static int Compare(A[] mx, A[] my) { if (ReferenceEquals(mx, my)) return 0; if (ReferenceEquals(mx, null)) return -1; if (ReferenceEquals(my, null)) return 1; var cmp = mx.Length.CompareTo(my.Length); if (cmp == 0) { for(var i = 0; i < mx.Length; i++) { cmp = OrdA.Compare(mx[i], my[i]); if (cmp != 0) return cmp; } return 0; } else { return cmp; } } /// /// Get the hash-code of the provided value /// /// Hash code of x [Pure] public static int GetHashCode(A[] x) => hash(x); } /// /// Equality and ordering /// public struct OrdArray : Ord { /// /// Equality test /// /// The left hand side of the equality operation /// The right hand side of the equality operation /// True if x and y are equal [Pure] public static bool Equals(A[] x, A[] y) => EqArray.Equals(x, y); /// /// Compare two values /// /// Left hand side of the compare operation /// Right hand side of the compare operation /// /// if x greater than y : 1 /// if x less than y : -1 /// if x equals y : 0 /// [Pure] public static int Compare(A[] mx, A[] my) => OrdArray, A>.Compare(mx, my); /// /// Get the hash-code of the provided value /// /// Hash code of x [Pure] public static int GetHashCode(A[] x) => OrdArray, A>.GetHashCode(x); } ================================================ FILE: LanguageExt.Core/Class Instances/Ord/OrdBigInt.cs ================================================ using LanguageExt.Traits; using System.Diagnostics.Contracts; namespace LanguageExt.ClassInstances; /// /// Equality and ordering /// public struct OrdBigInt : Ord { /// /// Equality test /// /// The left hand side of the equality operation /// The right hand side of the equality operation /// True if x and y are equal [Pure] public static bool Equals(bigint x, bigint y) => x == y; /// /// Compare two values /// /// Left hand side of the compare operation /// Right hand side of the compare operation /// /// if x greater than y : 1 /// /// if x less than y : -1 /// /// if x equals y : 0 /// [Pure] public static int Compare(bigint x, bigint y) => x.CompareTo(y); /// /// Get the hash-code of the provided value /// /// Hash code of x [Pure] public static int GetHashCode(bigint x) => x.GetHashCode(); } ================================================ FILE: LanguageExt.Core/Class Instances/Ord/OrdBool.cs ================================================ using LanguageExt.Traits; using System.Diagnostics.Contracts; namespace LanguageExt.ClassInstances; /// /// Equality and ordering /// public struct OrdBool : Ord { /// /// Equality test /// /// The left hand side of the equality operation /// The right hand side of the equality operation /// True if x and y are equal [Pure] public static bool Equals(bool x, bool y) => x == y; /// /// Compare two values /// /// Left hand side of the compare operation /// Right hand side of the compare operation /// /// if x greater than y : 1 /// /// if x less than y : -1 /// /// if x equals y : 0 /// [Pure] public static int Compare(bool x, bool y) => x.CompareTo(y); /// /// Get the hash-code of the provided value /// /// Hash code of x [Pure] public static int GetHashCode(bool x) => x.GetHashCode(); } ================================================ FILE: LanguageExt.Core/Class Instances/Ord/OrdChar.cs ================================================ using LanguageExt.Traits; using System.Diagnostics.Contracts; namespace LanguageExt.ClassInstances; /// /// Equality and ordering /// public struct OrdChar : Ord { public static readonly OrdChar Inst = default(OrdChar); /// /// Equality test /// /// The left hand side of the equality operation /// The right hand side of the equality operation /// True if x and y are equal [Pure] public static bool Equals(char x, char y) => x == y; /// /// Compare two values /// /// Left hand side of the compare operation /// Right hand side of the compare operation /// /// if x greater than y : 1 /// /// if x less than y : -1 /// /// if x equals y : 0 /// [Pure] public static int Compare(char x, char y) => x.CompareTo(y); /// /// Get the hash-code of the provided value /// /// Hash code of x [Pure] public static int GetHashCode(char x) => x.GetHashCode(); } ================================================ FILE: LanguageExt.Core/Class Instances/Ord/OrdDateTime.cs ================================================ using System; using LanguageExt.Traits; using System.Diagnostics.Contracts; namespace LanguageExt.ClassInstances; /// /// Equality and ordering /// public struct OrdDateTime : Ord { /// /// Equality test /// /// The left hand side of the equality operation /// The right hand side of the equality operation /// True if x and y are equal [Pure] public static bool Equals(DateTime x, DateTime y) => x == y; /// /// Compare two values /// /// Left hand side of the compare operation /// Right hand side of the compare operation /// /// if x greater than y : 1 /// /// if x less than y : -1 /// /// if x equals y : 0 /// [Pure] public static int Compare(DateTime x, DateTime y) => x.CompareTo(y); /// /// Get the hash-code of the provided value /// /// Hash code of x [Pure] public static int GetHashCode(DateTime x) => x.GetHashCode(); } ================================================ FILE: LanguageExt.Core/Class Instances/Ord/OrdDecimal.cs ================================================ using LanguageExt.Traits; using System.Diagnostics.Contracts; namespace LanguageExt.ClassInstances; /// /// Equality and ordering /// public struct OrdDecimal : Ord { /// /// Equality test /// /// The left hand side of the equality operation /// The right hand side of the equality operation /// True if x and y are equal [Pure] public static bool Equals(decimal x, decimal y) => x == y; /// /// Compare two values /// /// Left hand side of the compare operation /// Right hand side of the compare operation /// /// if x greater than y : 1 /// /// if x less than y : -1 /// /// if x equals y : 0 /// [Pure] public static int Compare(decimal x, decimal y) => x.CompareTo(y); /// /// Get the hash-code of the provided value /// /// Hash code of x [Pure] public static int GetHashCode(decimal x) => x.GetHashCode(); } ================================================ FILE: LanguageExt.Core/Class Instances/Ord/OrdDefault.cs ================================================ using LanguageExt.Traits; using System.Diagnostics.Contracts; using LanguageExt.Traits.Resolve; namespace LanguageExt.ClassInstances; /// /// Uses the standard .NET Comparer〈A〉.Default.Compare(a,b) method to /// provide equality testing. /// public struct OrdDefault : Ord { /// /// Equality test /// /// The left hand side of the equality operation /// The right hand side of the equality operation /// True if x and y are equal [Pure] public static int Compare(A x, A y) => OrdResolve.Compare(x, y); [Pure] public static bool Equals(A x, A y) => EqDefault.Equals(x, y); /// /// Get the hash-code of the provided value /// /// Hash code of x [Pure] public static int GetHashCode(A x) => EqDefault.GetHashCode(x); } ================================================ FILE: LanguageExt.Core/Class Instances/Ord/OrdDouble.cs ================================================ using LanguageExt.Traits; using System.Diagnostics.Contracts; namespace LanguageExt.ClassInstances; /// /// Equality and ordering /// public struct OrdDouble : Ord { /// /// Equality test /// /// The left hand side of the equality operation /// The right hand side of the equality operation /// True if x and y are equal [Pure] public static bool Equals(double x, double y) => x.Equals(y); /// /// Compare two values /// /// Left hand side of the compare operation /// Right hand side of the compare operation /// /// if x greater than y : 1 /// /// if x less than y : -1 /// /// if x equals y : 0 /// [Pure] public static int Compare(double x, double y) => x.CompareTo(y); /// /// Get the hash-code of the provided value /// /// Hash code of x [Pure] public static int GetHashCode(double x) => x.GetHashCode(); } ================================================ FILE: LanguageExt.Core/Class Instances/Ord/OrdEither.cs ================================================ using System.Diagnostics.Contracts; using LanguageExt.Traits; namespace LanguageExt.ClassInstances; /// /// Either type hashing /// public struct OrdEither : Ord> where OrdL : Ord where OrdR : Ord { /// /// Ordering test /// [Pure] public static int Compare(Either x, Either y) => x.CompareTo(y); /// /// Equality test /// [Pure] public static bool Equals(Either x, Either y) => EqEither.Equals(x, y); /// /// Get hash code of the value /// /// Value to get the hash code of /// The hash code of x [Pure] public static int GetHashCode(Either x) => HashableEither.GetHashCode(x); } /// /// Either type hashing /// public struct OrdEither : Ord> { /// /// Ordering test /// [Pure] public static int Compare(Either x, Either y) => OrdEither, OrdDefault, L, R>.Compare(x, y); /// /// Equality test /// [Pure] public static bool Equals(Either x, Either y) => EqEither, EqDefault, L, R>.Equals(x, y); /// /// Get hash code of the value /// /// Value to get the hash code of /// The hash code of x [Pure] public static int GetHashCode(Either x) => HashableEither, HashableDefault, L, R>.GetHashCode(x); } ================================================ FILE: LanguageExt.Core/Class Instances/Ord/OrdEnumerable.cs ================================================ using LanguageExt.Traits; using System.Collections.Generic; using System.Diagnostics.Contracts; using System.Runtime.CompilerServices; using static LanguageExt.Prelude; namespace LanguageExt.ClassInstances; /// /// Equality and ordering /// public struct OrdEnumerable : Ord> where OrdA : Ord { public static readonly OrdEnumerable Inst = default(OrdEnumerable); /// /// Equality test /// /// The left hand side of the equality operation /// The right hand side of the equality operation /// True if x and y are equal [Pure] public static bool Equals(IEnumerable x, IEnumerable y) => EqEnumerable.Equals(x, y); /// /// Compare two values /// /// Left hand side of the compare operation /// Right hand side of the compare operation /// /// if x greater than y : 1 /// if x less than y : -1 /// if x equals y : 0 /// [Pure] public static int Compare(IEnumerable x, IEnumerable y) { if (ReferenceEquals(x, y)) return 0; if (ReferenceEquals(x, null)) return -1; if (ReferenceEquals(y, null)) return 1; using var enumx = x.GetEnumerator(); using var enumy = y.GetEnumerator(); while (true) { bool r1 = enumx.MoveNext(); bool r2 = enumy.MoveNext(); if (!r1 && !r2) return 0; if (!r1) return -1; if (!r2) return 1; var cmp = OrdA.Compare(enumx.Current, enumy.Current); if (cmp != 0) return cmp; } } /// /// Get the hash-code of the provided value /// /// Hash code of x [Pure] public static int GetHashCode(IEnumerable x) => hash(x); } /// /// Equality and ordering /// public struct OrdEnumerable : Ord> { /// /// Equality test /// /// The left hand side of the equality operation /// The right hand side of the equality operation /// True if x and y are equal [Pure] public static bool Equals(IEnumerable x, IEnumerable y) => OrdEnumerable, A>.Equals(x, y); /// /// Compare two values /// /// Left hand side of the compare operation /// Right hand side of the compare operation /// /// if x greater than y : 1 /// if x less than y : -1 /// if x equals y : 0 /// [Pure] public static int Compare(IEnumerable x, IEnumerable y) => OrdEnumerable, A>.Compare(x, y); /// /// Get the hash-code of the provided value /// /// Hash code of x [Pure] public static int GetHashCode(IEnumerable x) => OrdEnumerable, A>.GetHashCode(x); } ================================================ FILE: LanguageExt.Core/Class Instances/Ord/OrdException.cs ================================================ using System; using System.Diagnostics.Contracts; using System.Runtime.CompilerServices; using LanguageExt.Traits; namespace LanguageExt.ClassInstances; public struct OrdException : Ord { [Pure] public static int GetHashCode(Exception x) => HashableException.GetHashCode(x); [Pure] public static bool Equals(Exception x, Exception y) => EqException.Equals(x, y); [Pure] public static int Compare(Exception x, Exception y) { if (ReferenceEquals(x, y)) return 0; if (ReferenceEquals(x, null)) return -1; if (ReferenceEquals(y, null)) return 1; return string.Compare(x.GetType().FullName, y.Message.GetType().FullName, StringComparison.Ordinal); } } ================================================ FILE: LanguageExt.Core/Class Instances/Ord/OrdFloat.cs ================================================ using LanguageExt.Traits; using System.Diagnostics.Contracts; namespace LanguageExt.ClassInstances; /// /// Equality and ordering /// public struct OrdFloat : Ord { /// /// Equality test /// /// The left hand side of the equality operation /// The right hand side of the equality operation /// True if x and y are equal [Pure] public static bool Equals(float x, float y) => x.Equals(y); /// /// Compare two values /// /// Left hand side of the compare operation /// Right hand side of the compare operation /// /// if x greater than y : 1 /// /// if x less than y : -1 /// /// if x equals y : 0 /// [Pure] public static int Compare(float x, float y) => x.CompareTo(y); /// /// Get the hash-code of the provided value /// /// Hash code of x [Pure] public static int GetHashCode(float x) => x.GetHashCode(); } ================================================ FILE: LanguageExt.Core/Class Instances/Ord/OrdGuid.cs ================================================ using System; using LanguageExt.Traits; using System.Diagnostics.Contracts; namespace LanguageExt.ClassInstances; /// /// Equality and ordering /// public struct OrdGuid : Ord { /// /// Equality test /// /// The left hand side of the equality operation /// The right hand side of the equality operation /// True if x and y are equal [Pure] public static bool Equals(Guid x, Guid y) => x == y; /// /// Compare two values /// /// Left hand side of the compare operation /// Right hand side of the compare operation /// /// if x greater than y : 1 /// /// if x less than y : -1 /// /// if x equals y : 0 /// [Pure] public static int Compare(Guid x, Guid y) => x.CompareTo(y); /// /// Get the hash-code of the provided value /// /// Hash code of x [Pure] public static int GetHashCode(Guid x) => x.GetHashCode(); } ================================================ FILE: LanguageExt.Core/Class Instances/Ord/OrdHashSet.cs ================================================ using LanguageExt.Traits; using System.Diagnostics.Contracts; using static LanguageExt.Prelude; namespace LanguageExt.ClassInstances; /// /// Equality and ordering /// public struct OrdHashSet : Ord> where OrdA : Ord { /// /// Equality test /// /// The left hand side of the equality operation /// The right hand side of the equality operation /// True if x and y are equal [Pure] public static bool Equals(HashSet x, HashSet y) => EqHashSet.Equals(x, y); /// /// Compare two values /// /// Left hand side of the compare operation /// Right hand side of the compare operation /// /// if x greater than y : 1 /// if x less than y : -1 /// if x equals y : 0 /// [Pure] public static int Compare(HashSet x, HashSet y) { if (x.Count > y.Count) return 1; if (x.Count < y.Count) return -1; var sa = toSet(x); var sb = toSet(y); using var iterA = sa.GetEnumerator(); using var iterB = sb.GetEnumerator(); while (iterA.MoveNext() && iterB.MoveNext()) { var cmp = OrdA.Compare(iterA.Current, iterB.Current); if (cmp != 0) return cmp; } return 0; } /// /// Get the hash-code of the provided value /// /// Hash code of x [Pure] public static int GetHashCode(HashSet x) => x.GetHashCode(); } /// /// Equality and ordering /// public struct OrdHashSet : Ord> { /// /// Equality test /// /// The left hand side of the equality operation /// The right hand side of the equality operation /// True if x and y are equal [Pure] public static bool Equals(HashSet x, HashSet y) => OrdHashSet, A>.Equals(x, y); /// /// Compare two values /// /// Left hand side of the compare operation /// Right hand side of the compare operation /// /// if x greater than y : 1 /// if x less than y : -1 /// if x equals y : 0 /// [Pure] public static int Compare(HashSet x, HashSet y) => OrdHashSet, A>.Compare(x, y); /// /// Get the hash-code of the provided value /// /// Hash code of x [Pure] public static int GetHashCode(HashSet x) => OrdHashSet, A>.GetHashCode(x); } ================================================ FILE: LanguageExt.Core/Class Instances/Ord/OrdInt.cs ================================================ using LanguageExt.Traits; using System.Diagnostics.Contracts; using System.Runtime.CompilerServices; namespace LanguageExt.ClassInstances; /// /// Equality and ordering /// public struct OrdInt : Ord { /// /// Equality test /// /// The left hand side of the equality operation /// The right hand side of the equality operation /// True if x and y are equal [Pure] public static bool Equals(int x, int y) => x == y; /// /// Compare two values /// /// Left hand side of the compare operation /// Right hand side of the compare operation /// /// if x greater than y : 1 /// /// if x less than y : -1 /// /// if x equals y : 0 /// [Pure] public static int Compare(int x, int y) => x.CompareTo(y); /// /// Get the hash-code of the provided value /// /// Hash code of x [Pure] public static int GetHashCode(int x) => x.GetHashCode(); } ================================================ FILE: LanguageExt.Core/Class Instances/Ord/OrdIterable.cs ================================================ using LanguageExt.Traits; using System.Collections.Generic; using System.Diagnostics.Contracts; using System.Runtime.CompilerServices; using static LanguageExt.Prelude; namespace LanguageExt.ClassInstances; /// /// Equality and ordering /// public struct OrdIterable : Ord> where OrdA : Ord { /// /// Equality test /// /// The left hand side of the equality operation /// The right hand side of the equality operation /// True if x and y are equal [Pure] public static bool Equals(Iterable x, Iterable y) => EqIterable.Equals(x, y); /// /// Compare two values /// /// Left hand side of the compare operation /// Right hand side of the compare operation /// /// if x greater than y : 1 /// if x less than y : -1 /// if x equals y : 0 /// [Pure] public static int Compare(Iterable x, Iterable y) { if (ReferenceEquals(x, y)) return 0; if (ReferenceEquals(x, null)) return -1; if (ReferenceEquals(y, null)) return 1; using var enumx = x.GetEnumerator(); using var enumy = y.GetEnumerator(); while (true) { bool r1 = enumx.MoveNext(); bool r2 = enumy.MoveNext(); if (!r1 && !r2) return 0; if (!r1) return -1; if (!r2) return 1; var cmp = OrdA.Compare(enumx.Current, enumy.Current); if (cmp != 0) return cmp; } } /// /// Get the hash-code of the provided value /// /// Hash code of x [Pure] public static int GetHashCode(Iterable x) => hash(x); } /// /// Equality and ordering /// public struct OrdIterable : Ord> { /// /// Equality test /// /// The left hand side of the equality operation /// The right hand side of the equality operation /// True if x and y are equal [Pure] public static bool Equals(Iterable x, Iterable y) => OrdIterable, A>.Equals(x, y); /// /// Compare two values /// /// Left hand side of the compare operation /// Right hand side of the compare operation /// /// if x greater than y : 1 /// if x less than y : -1 /// if x equals y : 0 /// [Pure] public static int Compare(Iterable x, Iterable y) => OrdIterable, A>.Compare(x, y); /// /// Get the hash-code of the provided value /// /// Hash code of x [Pure] public static int GetHashCode(Iterable x) => OrdIterable, A>.GetHashCode(x); } ================================================ FILE: LanguageExt.Core/Class Instances/Ord/OrdLong.cs ================================================ using LanguageExt.Traits; using System.Diagnostics.Contracts; using System.Runtime.CompilerServices; namespace LanguageExt.ClassInstances; /// /// Equality and ordering /// public struct OrdLong : Ord { /// /// Equality test /// /// The left hand side of the equality operation /// The right hand side of the equality operation /// True if x and y are equal [Pure] public static bool Equals(long x, long y) => x == y; /// /// Compare two values /// /// Left hand side of the compare operation /// Right hand side of the compare operation /// /// if x greater than y : 1 /// /// if x less than y : -1 /// /// if x equals y : 0 /// [Pure] public static int Compare(long x, long y) => x.CompareTo(y); /// /// Get the hash-code of the provided value /// /// Hash code of x [Pure] public static int GetHashCode(long x) => x.GetHashCode(); } ================================================ FILE: LanguageExt.Core/Class Instances/Ord/OrdLst.cs ================================================ using LanguageExt.Traits; using System.Diagnostics.Contracts; using System.Runtime.CompilerServices; namespace LanguageExt.ClassInstances; /// /// Equality and ordering /// public struct OrdLst : Ord> where OrdA : Ord { /// /// Equality test /// /// The left hand side of the equality operation /// The right hand side of the equality operation /// True if x and y are equal [Pure] public static bool Equals(Lst x, Lst y) => EqLst.Equals(x, y); /// /// Compare two values /// /// Left hand side of the compare operation /// Right hand side of the compare operation /// /// if x greater than y : 1 /// if x less than y : -1 /// if x equals y : 0 /// [Pure] public static int Compare(Lst mx, Lst my) { var cmp = mx.Count.CompareTo(my.Count); if (cmp == 0) { using var xiter = mx.GetEnumerator(); using var yiter = my.GetEnumerator(); while (xiter.MoveNext() && yiter.MoveNext()) { cmp = OrdA.Compare(xiter.Current, yiter.Current); if (cmp != 0) return cmp; } return 0; } else { return cmp; } } /// /// Get the hash-code of the provided value /// /// Hash code of x [Pure] public static int GetHashCode(Lst x) => x.GetHashCode(); } /// /// Equality and ordering /// public struct OrdLst : Ord> { /// /// Equality test /// /// The left hand side of the equality operation /// The right hand side of the equality operation /// True if x and y are equal [Pure] public static bool Equals(Lst x, Lst y) => OrdLst, A>.Equals(x, y); /// /// Compare two values /// /// Left hand side of the compare operation /// Right hand side of the compare operation /// /// if x greater than y : 1 /// if x less than y : -1 /// if x equals y : 0 /// [Pure] public static int Compare(Lst x, Lst y) => OrdLst, A>.Compare(x, y); /// /// Get the hash-code of the provided value /// /// Hash code of x [Pure] public static int GetHashCode(Lst x) => OrdLst, A>.GetHashCode(x); } ================================================ FILE: LanguageExt.Core/Class Instances/Ord/OrdMap.cs ================================================ using LanguageExt.Traits; using System.Diagnostics.Contracts; namespace LanguageExt.ClassInstances; /// /// Equality and ordering /// public struct OrdMap : Ord> where OrdK : Ord { /// /// Compare two values /// /// Left hand side of the compare operation /// Right hand side of the compare operation /// /// if `x` greater than `y` : `1` /// if `x` less than `y` : `-1` /// if `x` equals `y` : `0` /// [Pure] public static int Compare(Map x, Map y) => x.CompareTo(y); /// /// Equality test /// /// The left hand side of the equality operation /// The right hand side of the equality operation /// True if `x` and `y` are equal [Pure] public static bool Equals(Map x, Map y) => x.Equals(y); /// /// Get the hash-code of the provided value /// /// Hash code of `x` [Pure] public static int GetHashCode(Map x) => x.GetHashCode(); } /// /// Equality and ordering /// public struct OrdMap : Ord> { /// /// Compare two values /// /// Left hand side of the compare operation /// Right hand side of the compare operation /// /// if `x` greater than `y` : `1` /// if `x` less than `y` : `-1` /// if `x` equals `y` : `0` /// [Pure] public static int Compare(Map x, Map y) => x.CompareTo(y); /// /// Equality test /// /// The left hand side of the equality operation /// The right hand side of the equality operation /// True if `x` and `y` are equal [Pure] public static bool Equals(Map x, Map y) => x.Equals(y); /// /// Get the hash-code of the provided value /// /// Hash code of `x` [Pure] public static int GetHashCode(Map x) => x.GetHashCode(); } ================================================ FILE: LanguageExt.Core/Class Instances/Ord/OrdOption.cs ================================================ using System.Diagnostics.Contracts; using System.Runtime.CompilerServices; using LanguageExt.Traits; namespace LanguageExt.ClassInstances; public struct OrdOption : Ord> where OrdA : Ord { [Pure] public static int Compare(Option x, Option y) => x.CompareTo(y); [Pure] public static bool Equals(Option x, Option y) => x.Equals(y); [Pure] public static int GetHashCode(Option x) => x.GetHashCode(); } ================================================ FILE: LanguageExt.Core/Class Instances/Ord/OrdQue.cs ================================================ using LanguageExt.Traits; using System.Diagnostics.Contracts; using System.Runtime.CompilerServices; namespace LanguageExt.ClassInstances; /// /// Equality and ordering /// public struct OrdQue : Ord> where OrdA : Ord { /// /// Equality test /// /// The left hand side of the equality operation /// The right hand side of the equality operation /// True if x and y are equal [Pure] public static bool Equals(Que x, Que y) => EqQue.Equals(x, y); /// /// Compare two values /// /// Left hand side of the compare operation /// Right hand side of the compare operation /// /// if x greater than y : 1 /// if x less than y : -1 /// if x equals y : 0 /// [Pure] public static int Compare(Que x, Que y) { var cmp = x.Count.CompareTo(y.Count); if (cmp == 0) { using var enumx = x.GetEnumerator(); using var enumy = y.GetEnumerator(); var count = x.Count; for (var i = 0; i < count; i++) { enumx.MoveNext(); enumy.MoveNext(); cmp = OrdA.Compare(enumx.Current, enumy.Current); if (cmp != 0) return cmp; } return 0; } else { return cmp; } } /// /// Get the hash-code of the provided value /// /// Hash code of x [Pure] public static int GetHashCode(Que x) => x.GetHashCode(); } /// /// Equality and ordering /// public struct OrdQue : Ord> { /// /// Equality test /// /// The left hand side of the equality operation /// The right hand side of the equality operation /// True if x and y are equal [Pure] public static bool Equals(Que x, Que y) => OrdQue, A>.Equals(x, y); /// /// Compare two values /// /// Left hand side of the compare operation /// Right hand side of the compare operation /// /// if x greater than y : 1 /// if x less than y : -1 /// if x equals y : 0 /// [Pure] public static int Compare(Que x, Que y) => OrdQue, A>.Compare(x, y); /// /// Get the hash-code of the provided value /// /// Hash code of x [Pure] public static int GetHashCode(Que x) => OrdQue, A>.GetHashCode(x); } ================================================ FILE: LanguageExt.Core/Class Instances/Ord/OrdRecord.cs ================================================ using System.Diagnostics.Contracts; using LanguageExt.Traits; namespace LanguageExt.ClassInstances; /// /// Ordering class instance for all record types /// /// Record type public struct OrdRecord : Ord where A : Record { [Pure] public static int Compare(A x, A y) => RecordType.Compare(x, y); [Pure] public static bool Equals(A x, A y) => RecordType.EqualityTyped(x, y); [Pure] public static int GetHashCode(A x) => RecordType.Hash(x); } ================================================ FILE: LanguageExt.Core/Class Instances/Ord/OrdSeq.cs ================================================ using LanguageExt.Traits; using System.Diagnostics.Contracts; using static LanguageExt.Prelude; namespace LanguageExt.ClassInstances; /// /// Equality and ordering /// public struct OrdSeq : Ord> where OrdA : Ord { /// /// Equality test /// /// The left hand side of the equality operation /// The right hand side of the equality operation /// True if x and y are equal [Pure] public static bool Equals(Seq x, Seq y) => EqSeq.Equals(x, y); /// /// Compare two values /// /// Left hand side of the compare operation /// Right hand side of the compare operation /// /// if x greater than y : 1 /// if x less than y : -1 /// if x equals y : 0 /// [Pure] public static int Compare(Seq x, Seq y) { using var enumx = x.GetEnumerator(); using var enumy = y.GetEnumerator(); while(true) { var r1 = enumx.MoveNext(); var r2 = enumy.MoveNext(); if (!r1 && !r2) return 0; if (!r1) return -1; if (!r2) return 1; var cmp = OrdA.Compare(enumx.Current, enumy.Current); if (cmp != 0) return cmp; } } /// /// Get the hash-code of the provided value /// /// Hash code of x [Pure] public static int GetHashCode(Seq x) => hash(x); } /// /// Equality and ordering /// public struct OrdSeq : Ord> { /// /// Equality test /// /// The left hand side of the equality operation /// The right hand side of the equality operation /// True if x and y are equal [Pure] public static bool Equals(Seq x, Seq y) => OrdSeq, A>.Equals(x, y); /// /// Compare two values /// /// Left hand side of the compare operation /// Right hand side of the compare operation /// /// if x greater than y : 1 /// if x less than y : -1 /// if x equals y : 0 /// [Pure] public static int Compare(Seq x, Seq y) => OrdSeq, A>.Compare(x, y); /// /// Get the hash-code of the provided value /// /// Hash code of x [Pure] public static int GetHashCode(Seq x) => OrdSeq, A>.GetHashCode(x); } ================================================ FILE: LanguageExt.Core/Class Instances/Ord/OrdSet.cs ================================================ using LanguageExt.Traits; using System.Diagnostics.Contracts; using System.Runtime.CompilerServices; namespace LanguageExt.ClassInstances; /// /// Equality and ordering /// public struct OrdSet : Ord> where OrdA : Ord { /// /// Equality test /// /// The left hand side of the equality operation /// The right hand side of the equality operation /// True if x and y are equal [Pure] public static bool Equals(Set x, Set y) => EqSet.Equals(x, y); /// /// Compare two values /// /// Left hand side of the compare operation /// Right hand side of the compare operation /// /// if x greater than y : 1 /// if x less than y : -1 /// if x equals y : 0 /// [Pure] public static int Compare(Set x, Set y) => x.CompareTo(y); /// /// Get the hash-code of the provided value /// /// Hash code of x [Pure] public static int GetHashCode(Set x) => x.GetHashCode(); } /// /// Equality and ordering /// public struct OrdSet : Ord> { /// /// Equality test /// /// The left hand side of the equality operation /// The right hand side of the equality operation /// True if x and y are equal [Pure] public static bool Equals(Set x, Set y) => OrdSet, A>.Equals(x, y); /// /// Compare two values /// /// Left hand side of the compare operation /// Right hand side of the compare operation /// /// if x greater than y : 1 /// if x less than y : -1 /// if x equals y : 0 /// [Pure] public static int Compare(Set x, Set y) => OrdSet, A>.Compare(x, y); /// /// Get the hash-code of the provided value /// /// Hash code of x [Pure] public static int GetHashCode(Set x) => OrdSet, A>.GetHashCode(x); } ================================================ FILE: LanguageExt.Core/Class Instances/Ord/OrdShort.cs ================================================ using LanguageExt.Traits; using System.Diagnostics.Contracts; namespace LanguageExt.ClassInstances; /// /// Equality and ordering /// public struct OrdShort : Ord { /// /// Equality test /// /// The left hand side of the equality operation /// The right hand side of the equality operation /// True if x and y are equal [Pure] public static bool Equals(short x, short y) => x == y; /// /// Compare two values /// /// Left hand side of the compare operation /// Right hand side of the compare operation /// /// if x greater than y : 1 /// /// if x less than y : -1 /// /// if x equals y : 0 /// [Pure] public static int Compare(short x, short y) => x.CompareTo(y); /// /// Get the hash-code of the provided value /// /// Hash code of x [Pure] public static int GetHashCode(short x) => x.GetHashCode(); } ================================================ FILE: LanguageExt.Core/Class Instances/Ord/OrdStck.cs ================================================ using LanguageExt.Traits; using System.Diagnostics.Contracts; using System.Runtime.CompilerServices; namespace LanguageExt.ClassInstances; /// /// Equality and ordering /// public struct OrdStck : Ord> where OrdA : Ord { /// /// Equality test /// /// The left hand side of the equality operation /// The right hand side of the equality operation /// True if x and y are equal [Pure] public static bool Equals(Stck x, Stck y) => EqStck.Equals(x, y); /// /// Compare two values /// /// Left hand side of the compare operation /// Right hand side of the compare operation /// /// if x greater than y : 1 /// if x less than y : -1 /// if x equals y : 0 /// [Pure] public static int Compare(Stck x, Stck y) { var cmp = x.Count.CompareTo(y.Count); if (cmp == 0) { using var enumx = x.GetEnumerator(); using var enumy = y.GetEnumerator(); var count = x.Count; for (int i = 0; i < count; i++) { enumx.MoveNext(); enumy.MoveNext(); cmp = OrdA.Compare(enumx.Current, enumy.Current); if (cmp != 0) return cmp; } return 0; } else { return cmp; } } /// /// Get the hash-code of the provided value /// /// Hash code of x [Pure] public static int GetHashCode(Stck x) => x.GetHashCode(); } /// /// Equality and ordering /// public struct OrdStck : Ord> { /// /// Equality test /// /// The left hand side of the equality operation /// The right hand side of the equality operation /// True if x and y are equal [Pure] public static bool Equals(Stck x, Stck y) => OrdStck, A>.Equals(x, y); /// /// Compare two values /// /// Left hand side of the compare operation /// Right hand side of the compare operation /// /// if x greater than y : 1 /// if x less than y : -1 /// if x equals y : 0 /// [Pure] public static int Compare(Stck x, Stck y) => OrdStck, A>.Compare(x, y); /// /// Get the hash-code of the provided value /// /// Hash code of x [Pure] public static int GetHashCode(Stck x) => OrdStck, A>.GetHashCode(x); } ================================================ FILE: LanguageExt.Core/Class Instances/Ord/OrdString.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt.ClassInstances; /// /// String comparison /// public struct OrdString : Ord { /// /// Compare two values /// /// Left hand side of the compare operation /// Right hand side of the compare operation /// /// if a greater than b : 1 /// if a less than b : -1 /// if a equals b : 0 /// public static int Compare(string a, string b) => a?.CompareTo(b) ?? 1; /// /// Equality test /// /// The left hand side of the equality operation /// The right hand side of the equality operation /// True if a and b are equal public static bool Equals(string a, string b) => EqString.Equals(a, b); /// /// Get the hash-code of the provided value /// /// Hash code of x public static int GetHashCode(string x) => HashableString.GetHashCode(x); } /// /// String comparison (ordinal, ignore case) /// public struct OrdStringOrdinalIgnoreCase : Ord { /// /// Compare two values /// /// Left hand side of the compare operation /// Right hand side of the compare operation /// /// if a greater than b : 1 /// if a less than b : -1 /// if a equals b : 0 /// public static int Compare(string a, string b) => StringComparer.OrdinalIgnoreCase.Compare(a, b); /// /// Equality test /// /// The left hand side of the equality operation /// The right hand side of the equality operation /// True if a and b are equal public static bool Equals(string a, string b) => EqStringOrdinalIgnoreCase.Equals(a, b); /// /// Get the hash-code of the provided value /// /// Hash code of x public static int GetHashCode(string x) => HashableStringOrdinalIgnoreCase.GetHashCode(x); } /// /// String comparison (ordinal) /// public struct OrdStringOrdinal : Ord { /// /// Compare two values /// /// Left hand side of the compare operation /// Right hand side of the compare operation /// /// if a greater than b : 1 /// if a less than b : -1 /// if a equals b : 0 /// public static int Compare(string a, string b) => StringComparer.Ordinal.Compare(a, b); /// /// Equality test /// /// The left hand side of the equality operation /// The right hand side of the equality operation /// True if a and b are equal public static bool Equals(string a, string b) => EqStringOrdinal.Equals(a, b); /// /// Get the hash-code of the provided value /// /// Hash code of x public static int GetHashCode(string x) => HashableStringOrdinal.GetHashCode(x); } /// /// String comparison (current culture, ignore case) /// public struct OrdStringCurrentCultureIgnoreCase : Ord { /// /// Compare two values /// /// Left hand side of the compare operation /// Right hand side of the compare operation /// /// if a greater than b : 1 /// if a less than b : -1 /// if a equals b : 0 /// public static int Compare(string a, string b) => StringComparer.CurrentCultureIgnoreCase.Compare(a, b); /// /// Equality test /// /// The left hand side of the equality operation /// The right hand side of the equality operation /// True if a and b are equal public static bool Equals(string a, string b) => EqStringCurrentCultureIgnoreCase.Equals(a, b); /// /// Get the hash-code of the provided value /// /// Hash code of x public static int GetHashCode(string x) => HashableStringCurrentCultureIgnoreCase.GetHashCode(x); } /// /// String comparison (current culture) /// public struct OrdStringCurrentCulture : Ord { /// /// Compare two values /// /// Left hand side of the compare operation /// Right hand side of the compare operation /// /// if a greater than b : 1 /// if a less than b : -1 /// if a equals b : 0 /// public static int Compare(string a, string b) => StringComparer.CurrentCulture.Compare(a, b); /// /// Equality test /// /// The left hand side of the equality operation /// The right hand side of the equality operation /// True if a and b are equal public static bool Equals(string a, string b) => EqStringCurrentCulture.Equals(a, b); /// /// Get the hash-code of the provided value /// /// Hash code of x public static int GetHashCode(string x) => HashableStringCurrentCulture.GetHashCode(x); } ================================================ FILE: LanguageExt.Core/Class Instances/Ord/OrdTask.cs ================================================ using LanguageExt.Traits; using System.Threading.Tasks; namespace LanguageExt.ClassInstances; public struct OrdTask : Ord> { public static int Compare(Task x, Task y) => x.Id.CompareTo(y.Id); public static bool Equals(Task x, Task y) => EqTask.Equals(x, y); public static int GetHashCode(Task x) => HashableTask.GetHashCode(x); } ================================================ FILE: LanguageExt.Core/Class Instances/Ord/OrdTrue.cs ================================================ using LanguageExt.Traits; namespace LanguageExt.ClassInstances; /// /// Always returns true for equality checks and 0 for ordering /// public struct OrdTrue : Ord { public static int Compare(A x, A y) => 0; public static bool Equals(A x, A y) => true; public static int GetHashCode(A x) => OrdDefault.GetHashCode(x); } ================================================ FILE: LanguageExt.Core/Class Instances/Ord/OrdTupleFirst.cs ================================================ using LanguageExt.Traits; using System; namespace LanguageExt.ClassInstances; /// /// Ord instance for a pair tuple. It orders using the first /// item in the tuple only and the provided OrdA. /// public struct OrdTupleFirst : Ord> where OrdA : Ord { public static int Compare((A, B) x, (A, B) y) => OrdA.Compare(x.Item1, y.Item1); public static bool Equals((A, B) x, (A, B) y) => OrdA.Equals(x.Item1, y.Item1); public static int GetHashCode((A, B) x) => x.GetHashCode(); } ================================================ FILE: LanguageExt.Core/Class Instances/Ord/OrdTypeInfo.cs ================================================ using System; using LanguageExt.Traits; using System.Reflection; namespace LanguageExt.ClassInstances; public struct OrdTypeInfo : Ord { public static int Compare(TypeInfo x, TypeInfo y) => string.Compare(x.ToString(), y.ToString(), StringComparison.Ordinal); public static bool Equals(TypeInfo x, TypeInfo y) => EqTypeInfo.Equals(x, y); public static int GetHashCode(TypeInfo x) => EqTypeInfo.GetHashCode(x); } ================================================ FILE: LanguageExt.Core/Class Instances/README.md ================================================ __If you're new to this library or functional-programming this is almost certainly not the place to start browsing!__ Class-instances try to replicate the instances of Haskell in C#. They pair with the traits using ad-hoc polymorphism. Ad-hoc polymorphism has long been believed to not be possible in C#. However with some cunning _it is_. Ad-hoc polymorphism allows programmers to add traits to a type later. For example in C# it would be amazing if we had an interface called `INumeric` for numeric types like `int`, `long`, `double`, etc. The reason this doesn't exist is if you write a function like: INumeric Add(INumeric x, INumeric y) => x + y; Then it would cause boxing. Which is slow (well, slower). I can only assume that's why it wasn't added by the BCL team. Anyway, it's possible to create a numeric type, very much like a trait in Haskell, and ad-hoc instances of the numeric trait that allow for generic numeric operations without boxing. [See the wiki for a deeper dive into ad-hoc polymorphism](https://github.com/louthy/language-ext/wiki/Ad-hoc-polymorphism) ================================================ FILE: LanguageExt.Core/Class Instances/TBigInt.cs ================================================ using LanguageExt.Traits; using System.Diagnostics.Contracts; namespace LanguageExt.ClassInstances; /// /// Integer number /// public struct TBigInt : Num, Bool { /// /// Equality test /// /// The left hand side of the equality operation /// The right hand side of the equality operation /// True if x and y are equal [Pure] public static bool Equals(bigint x, bigint y) => x == y; /// /// Compare two values /// /// Left hand side of the compare operation /// Right hand side of the compare operation /// /// if x greater than y : 1 /// /// if x less than y : -1 /// /// if x equals y : 0 /// [Pure] public static int Compare(bigint x, bigint y) => x.CompareTo(y); /// /// Find the sum of two numbers /// /// left hand side of the addition operation /// right hand side of the addition operation /// The sum of x and y [Pure] public static bigint Add(bigint x, bigint y) => x + y; /// /// Find the difference between two values /// /// left hand side of the subtraction operation /// right hand side of the subtraction operation /// The difference between x and y [Pure] public static bigint Subtract(bigint x, bigint y) => x - y; /// /// Find the product of two numbers /// /// left hand side of the product operation /// right hand side of the product operation /// The product of x and y [Pure] public static bigint Multiply(bigint x, bigint y) => x * y; /// /// Divide two numbers /// /// left hand side of the division operation /// right hand side of the division operation /// x / y [Pure] public static bigint Divide(bigint x, bigint y) => x / y; /// /// Find the absolute value of a number /// /// The value to find the absolute value of /// The non-negative absolute value of x [Pure] public static bigint Abs(bigint x) => x < bigint.Zero ? -x : x; /// /// Find the sign of x /// /// The value to find the sign of /// -1, 0, or +1 [Pure] public static bigint Signum(bigint x) => x == bigint.Zero ? 0 : x < bigint.Zero ? -1 : 1; /// /// Generate a numeric value from an integer /// /// The integer to use /// The equivalent of x in the Num〈A〉 [Pure] public static bigint FromInteger(int x) => x; /// /// Generate a numeric value from a float /// /// The float to use /// The equivalent of x in the Num〈A〉 [Pure] public static bigint FromDecimal(decimal x) => (bigint)x; /// /// Generate a numeric value from a double /// /// The double to use /// The equivalent of x in the Num〈A〉 [Pure] public static bigint FromFloat(float x) => (bigint)x; /// /// Generate a numeric value from a decimal /// /// The decimal to use /// The equivalent of x in the Num〈A〉 [Pure] public static bigint FromDouble(double x) => (bigint)x; /// /// Monoid empty value (0) /// /// 0 [Pure] public static bigint Empty => bigint.Zero; /// /// Negate the value /// /// Value to negate /// The negated source value [Pure] public static bigint Negate(bigint x) => -x; /// /// Semigroup append (sum) /// /// left hand side of the append operation /// right hand side of the append operation /// x + y [Pure] public static bigint Append(bigint x, bigint y) => x + y; /// /// Get the hash-code of the provided value /// /// Hash code of x [Pure] public static int GetHashCode(bigint x) => x.GetHashCode(); /// /// Returns True /// /// True [Pure] public static bigint True() => bigint.MinusOne; /// /// Returns False /// /// False [Pure] public static bigint False() => bigint.Zero; /// /// Returns the result of the bitwise AND operation between `a` and `b` /// /// The result of the bitwise AND operation between `a` and `b` [Pure] public static bigint And(bigint a, bigint b) => a & b; /// /// Returns the result of the bitwise OR operation between `a` and `b` /// /// The result of the bitwise OR operation between `a` and `b` [Pure] public static bigint Or(bigint a, bigint b) => a | b; /// /// Returns the result of the bitwise NOT operation on `a` /// /// The result of the bitwise NOT operation on `a` [Pure] public static bigint Not(bigint a) => ~a; /// /// Returns the result of the bitwise exclusive-OR operation between `a` and `b` /// /// The result of the bitwise exclusive-OR operation between `a` and `b` [Pure] public static bigint XOr(bigint a, bigint b) => a ^ b; /// /// Logical implication /// /// If `a` is true that implies `b`, else `true` [Pure] public static bigint Implies(bigint a, bigint b) => And(a, True()) == False() ? True() : b; /// /// Bitwise bi-conditional. /// /// `Not(XOr(a, b))` [Pure] public static bigint BiCondition(bigint a, bigint b) => Not(XOr(a, b)); } ================================================ FILE: LanguageExt.Core/Class Instances/TBool.cs ================================================ using LanguageExt.Traits; using System.Diagnostics.Contracts; namespace LanguageExt.ClassInstances; /// /// Bool class instance. Implements /// /// Eq〈bool〉 /// Ord〈bool〉 /// Bool〈bool〉 /// public struct TBool : Ord, Bool { /// /// Returns the result of the logical AND operation between `a` and `b` /// /// The result of the logical AND operation between `a` and `b` [Pure] public static bool And(bool a, bool b) => a && b; /// /// Compare two values /// /// Left hand side of the compare operation /// Right hand side of the compare operation /// /// if x greater than y : 1 /// /// if x less than y : -1 /// /// if x equals y : 0 /// public static int Compare(bool x, bool y) => x.CompareTo(y); /// /// Equality test /// /// The left hand side of the equality operation /// The right hand side of the equality operation /// True if x and y are equal [Pure] public static bool Equals(bool x, bool y) => x == y; /// /// Returns False /// /// False [Pure] public static bool False() => false; /// /// Get the hash-code of the provided value /// /// Hash code of x [Pure] public static int GetHashCode(bool x) => x.GetHashCode(); /// /// Returns the result of the logical NOT operation on `a` /// /// The result of the logical NOT operation on `a` [Pure] public static bool Not(bool a) => !a; /// /// Returns the result of the logical OR operation between `a` and `b` /// /// The result of the logical OR operation between `a` and `b` [Pure] public static bool Or(bool a, bool b) => a || b; /// /// Returns True /// /// True [Pure] public static bool True() => true; /// /// Returns the result of the logical exclusive-OR operation between `a` and `b` /// /// The result of the logical exclusive-OR operation between `a` and `b` [Pure] public static bool XOr(bool a, bool b) => a ^ b; /// /// Logical implication /// /// If `a` is true that implies `b`, else `true` [Pure] public static bool Implies(bool a, bool b) => !a || b; /// /// Logical bi-conditional. Both `a` and `b` must be `true`, or both `a` and `b` must /// be false. /// /// `true` if `a == b`, `false` otherwise [Pure] public static bool BiCondition(bool a, bool b) => a == b; } ================================================ FILE: LanguageExt.Core/Class Instances/TBoolBool.cs ================================================ using LanguageExt.Traits; using System.Diagnostics.Contracts; namespace LanguageExt.ClassInstances; /// /// TBoolBool class instance. Implements /// /// Eq〈(bool, bool)〉 /// Ord〈(bool, bool)〉 /// Bool〈(bool, bool)〉 /// public struct TBoolBool : Ord<(bool, bool)>, Bool<(bool, bool)> { /// /// Returns the result of the logical AND operation between `a` and `b` /// /// The result of the logical AND operation between `a` and `b` [Pure] public static (bool, bool) And((bool, bool) a, (bool, bool) b) => (a.Item1 && b.Item1, a.Item2 && b.Item2); /// /// Compare two values /// /// Left hand side of the compare operation /// Right hand side of the compare operation /// /// if x greater than y : 1 /// /// if x less than y : -1 /// /// if x equals y : 0 /// public static int Compare((bool, bool) x, (bool, bool) y) => x.CompareTo(y); /// /// Equality test /// /// The left hand side of the equality operation /// The right hand side of the equality operation /// True if x and y are equal [Pure] public static bool Equals((bool, bool) x, (bool, bool) y) => x.Item1 == y.Item1 && x.Item2 == y.Item2; /// /// Returns False /// /// False [Pure] public static (bool, bool) False() => (false, false); /// /// Get the hash-code of the provided value /// /// Hash code of x [Pure] public static int GetHashCode((bool, bool) x) => x.GetHashCode(); /// /// Returns the result of the logical NOT operation on `a` /// /// The result of the logical NOT operation on `a` [Pure] public static (bool, bool) Not((bool, bool) a) => (!a.Item1, !a.Item2); /// /// Returns the result of the logical OR operation between `a` and `b` /// /// The result of the logical OR operation between `a` and `b` [Pure] public static (bool, bool) Or((bool, bool) a, (bool, bool) b) => (a.Item1 || b.Item1, a.Item2 || b.Item2); /// /// Returns True /// /// True [Pure] public static (bool, bool) True() => (true, true); /// /// Returns the result of the logical exclusive-OR operation between `a` and `b` /// /// The result of the logical exclusive-OR operation between `a` and `b` [Pure] public static (bool, bool) XOr((bool, bool) a, (bool, bool) b) => (a.Item1 ^ b.Item1, a.Item2 ^ b.Item2); /// /// Logical implication /// /// If `a` is true that implies `b`, else `true` [Pure] public static (bool, bool) Implies((bool, bool) a, (bool, bool) b) => (!a.Item1 || b.Item1, !a.Item2 || b.Item2); /// /// Logical bi-conditional. Both `a` and `b` must be `true`, or both `a` and `b` must /// be false. /// /// `true` if `a == b`, `false` otherwise [Pure] public static (bool, bool) BiCondition((bool, bool) a, (bool, bool) b) => (a.Item1 == b.Item1, a.Item2 == b.Item2); } ================================================ FILE: LanguageExt.Core/Class Instances/TChar.cs ================================================ using LanguageExt.Traits; using System.Diagnostics.Contracts; namespace LanguageExt.ClassInstances; /// /// Integer number /// public struct TChar : Ord, Arithmetic { /// /// Equality test /// /// The left hand side of the equality operation /// The right hand side of the equality operation /// True if x and y are equal [Pure] public static bool Equals(char x, char y) => x == y; /// /// Compare two values /// /// Left hand side of the compare operation /// Right hand side of the compare operation /// /// if x greater than y : 1 /// /// if x less than y : -1 /// /// if x equals y : 0 /// [Pure] public static int Compare(char x, char y) => x.CompareTo(y); /// /// Get the hash-code of the provided value /// /// Hash code of x [Pure] public static int GetHashCode(char x) => x.GetHashCode(); [Pure] public static char Add(char x, char y) => (char)(x + y); [Pure] public static char Subtract(char x, char y) => (char)(x - y); [Pure] public static char Multiply(char x, char y) => (char) (x * y); [Pure] public static char Negate(char x) => (char)-x; } ================================================ FILE: LanguageExt.Core/Class Instances/TDecimal.cs ================================================ using System; using LanguageExt.Traits; using System.Diagnostics.Contracts; namespace LanguageExt.ClassInstances; public struct TDecimal : Floating { /// /// Equality test /// /// Left hand side of the equality operation /// Right hand side of the equality operation /// True if parameters are equal [Pure] public static bool Equals(decimal x, decimal y) => x == y; /// /// Compare two values /// /// Left hand side of the compare operation /// Right hand side of the compare operation /// /// if x greater than y : 1 /// /// if x less than y : -1 /// /// if x equals y : 0 /// [Pure] public static int Compare(decimal x, decimal y) => x.CompareTo(y); /// /// Find the sum of two values /// /// left hand side of the addition operation /// right hand side of the addition operation /// The sum of x and y [Pure] public static decimal Add(decimal x, decimal y) => x + y; /// /// Find the difference between two values /// /// left hand side of the subtraction operation /// right hand side of the subtraction operation /// The difference between x and y [Pure] public static decimal Subtract(decimal x, decimal y) => x - y; /// /// Find the product of two values /// /// left hand side of the product operation /// right hand side of the product operation /// The product of x and y [Pure] public static decimal Multiply(decimal x, decimal y) => x * y; /// /// Divide two numbers /// /// left hand side of the division operation /// right hand side of the division operation /// x / y [Pure] public static decimal Divide(decimal x, decimal y) => x / y; /// /// Find the absolute value of a number /// /// The value to find the absolute value of /// The non-negative absolute value of x [Pure] public static decimal Abs(decimal x) => Math.Abs(x); /// /// Find the sign of x /// /// The value to find the sign of /// -1, 0, or +1 [Pure] public static decimal Signum(decimal x) => Math.Sign(x); /// /// Generate a numeric value from an integer /// /// The integer to use /// The equivalent of x in the Num〈A〉 [Pure] public static decimal FromInteger(int x) => x; /// /// Generate a numeric value from a decimal /// /// The decimal to use /// The equivalent of x in the Num〈A〉 [Pure] public static decimal FromDecimal(decimal x) => x; /// /// Generate a numeric value from a float /// /// The float to use /// The equivalent of x in the Num〈A〉 [Pure] public static decimal FromFloat(float x) => (decimal)x; /// /// Generate a numeric value from a double /// /// The double to use /// The equivalent of x in the Num〈A〉 [Pure] public static decimal FromDouble(double x) => (decimal)x; /// /// Generates a fractional value from an integer ratio. /// /// The ratio to convert /// The equivalent of x in the implementing type. [Pure] public static decimal FromRational(Ratio x) => (decimal)x.Numerator / x.Denominator; /// /// Returns an approximation of pi. /// /// A reasonable approximation of pi in this type [Pure] public static decimal Pi() => (decimal)Math.PI; /// /// The exponential function. /// /// The value for which we are calculating the exponential /// The value of e^x [Pure] public static decimal Exp(decimal x) => (decimal)Math.Exp((double)x); /// /// Calculates the square root of a value. /// /// The value for which we are calculating the square root. /// The value of sqrt(x). [Pure] public static decimal Sqrt(decimal x) => (decimal)Math.Sqrt((double)x); /// /// Calculates the natural logarithm of a value. /// /// /// The value for which we are calculating the natural logarithm. /// /// The value of ln(x). [Pure] public static decimal Log(decimal x) => (decimal)Math.Log((double)x); /// Raises x to the power y /// /// The base to be raised to y /// The exponent to which we are raising x /// The value of x^y. [Pure] public static decimal Pow(decimal x, decimal y) => (decimal)Math.Pow((double)x, (double)y); /// /// Calculates the logarithm of a value with respect to an arbitrary base. /// /// The base to use for the logarithm of t /// The value for which we are calculating the logarithm. /// The value of log x (y). [Pure] public static decimal LogBase(decimal b, decimal x) => (decimal)Math.Log((double)x, (double)b); /// /// Calculates the sine of an angle. /// /// An angle, in radians /// The value of sin(x) [Pure] public static decimal Sin(decimal x) => (decimal)Math.Sin((double)x); /// /// Calculates the cosine of an angle. /// /// An angle, in radians /// The value of cos(x) [Pure] public static decimal Cos(decimal x) => (decimal)Math.Cos((double)x); /// /// Calculates the tangent of an angle. /// /// An angle, in radians /// The value of tan(x) [Pure] public static decimal Tan(decimal x) => (decimal)Math.Tan((double)x); /// /// Calculates an arcsine. /// /// The value for which an arcsine is to be calculated. /// The value of asin(x), in radians. [Pure] public static decimal Asin(decimal x) => (decimal)Math.Asin((double)x); /// /// Calculates an arc-cosine. /// /// The value for which an arc-cosine is to be calculated /// The value of acos(x), in radians [Pure] public static decimal Acos(decimal x) => (decimal)Math.Acos((double)x); /// /// Calculates an arc-tangent. /// /// The value for which an arc-tangent is to be calculated /// The value of atan(x), in radians [Pure] public static decimal Atan(decimal x) => (decimal)Math.Atan((double)x); /// /// Calculates a hyperbolic sine. /// /// The value for which a hyperbolic sine is to be calculated /// The value of sinh(x) [Pure] public static decimal Sinh(decimal x) => (decimal)Math.Sinh((double)x); /// /// Calculates a hyperbolic cosine. /// /// The value for which a hyperbolic cosine is to be calculated /// The value of cosh(x) [Pure] public static decimal Cosh(decimal x) => (decimal)Math.Cosh((double)x); /// /// Calculates a hyperbolic tangent. /// /// /// The value for which a hyperbolic tangent is to be calculated. /// /// The value of tanh(x) [Pure] public static decimal Tanh(decimal x) => (decimal)Math.Tanh((double)x); /// Calculates an area hyperbolic sine /// The value for which an area hyperbolic sine is to be calculated. /// /// The value of asinh(x). [Pure] public static decimal Asinh(decimal x) => Log(x + Sqrt((x * x) + 1m)); /// /// Calculates an area hyperbolic cosine. /// /// The value for which an area hyperbolic cosine is to be calculated. /// /// The value of acosh(x). [Pure] public static decimal Acosh(decimal x) => Log(x + Sqrt((x * x) - 1m)); /// /// Calculates an area hyperbolic tangent. /// /// The value for which an area hyperbolic tangent is to be calculated. /// /// The value of atanh(x) [Pure] public static decimal Atanh(decimal x) => 0.5m * Log((1m + x) / (1m - x)); /// /// Negate the value /// /// Value to negate /// The negated source value [Pure] public static decimal Negate(decimal x) => -x; /// /// Get the hash-code of the provided value /// /// Hash code of x [Pure] public static int GetHashCode(decimal x) => x.GetHashCode(); } ================================================ FILE: LanguageExt.Core/Class Instances/TDouble.cs ================================================ using System; using LanguageExt.Traits; using System.Diagnostics.Contracts; namespace LanguageExt.ClassInstances; public struct TDouble : Floating { /// /// Equality test /// /// Left hand side of the equality operation /// Right hand side of the equality operation /// True if parameters are equal [Pure] public static bool Equals(double x, double y) => x.Equals(y); /// /// Compare two values /// /// Left hand side of the compare operation /// Right hand side of the compare operation /// /// if x greater than y : 1 /// /// if x less than y : -1 /// /// if x equals y : 0 /// [Pure] public static int Compare(double x, double y) => x.CompareTo(y); /// /// Find the sum of two values /// /// left hand side of the addition operation /// right hand side of the addition operation /// The sum of x and y [Pure] public static double Add(double x, double y) => x + y; /// /// Find the difference between two values /// /// left hand side of the subtraction operation /// right hand side of the subtraction operation /// The difference between x and y [Pure] public static double Subtract(double x, double y) => x - y; /// /// Find the product of two values /// /// left hand side of the product operation /// right hand side of the product operation /// The product of x and y [Pure] public static double Multiply(double x, double y) => x * y; /// /// Divide two numbers /// /// left hand side of the division operation /// right hand side of the division operation /// x / y [Pure] public static double Divide(double x, double y) => x / y; /// /// Find the absolute value of a number /// /// The value to find the absolute value of /// The non-negative absolute value of x [Pure] public static double Abs(double x) => Math.Abs(x); /// /// Find the sign of x /// /// The value to find the sign of /// -1, 0, or +1 [Pure] public static double Signum(double x) => Math.Sign(x); /// /// Generate a numeric value from an integer /// /// The integer to use /// The equivalent of x in the Num〈A〉 [Pure] public static double FromInteger(int x) => x; /// /// Generate a numeric value from a decimal /// /// The decimal to use /// The equivalent of x in the Num〈A〉 [Pure] public static double FromDecimal(decimal x) => (double)x; /// /// Generate a numeric value from a float /// /// The float to use /// The equivalent of x in the Num〈A〉 [Pure] public static double FromFloat(float x) => x; /// /// Generate a numeric value from a double /// /// The double to use /// The equivalent of x in the Num〈A〉 [Pure] public static double FromDouble(double x) => x; /// /// Generates a fractional value from an integer ratio. /// /// The ratio to convert /// The equivalent of x in the implementing type. [Pure] public static double FromRational(Ratio x) => (double)x.Numerator / x.Denominator; /// /// Returns an approximation of pi. /// /// A reasonable approximation of pi in this type [Pure] public static double Pi() => Math.PI; /// /// The exponential function. /// /// The value for which we are calculating the exponential /// The value of e^x [Pure] public static double Exp(double x) => Math.Exp(x); /// /// Calculates the square root of a value. /// /// The value for which we are calculating the square root. /// The value of sqrt(x). [Pure] public static double Sqrt(double x) => Math.Sqrt(x); /// /// Calculates the natural logarithm of a value. /// /// /// The value for which we are calculating the natural logarithm. /// /// The value of ln(x). [Pure] public static double Log(double x) => Math.Log(x); /// Raises x to the power y /// /// The base to be raised to y /// The exponent to which we are raising x /// The value of x^y. [Pure] public static double Pow(double x, double y) => Math.Pow(x, y); /// /// Calculates the logarithm of a value with respect to an arbitrary base. /// /// The base to use for the logarithm of t /// The value for which we are calculating the logarithm. /// The value of log x (y). [Pure] public static double LogBase(double b, double x) => Math.Log(x, b); /// /// Calculates the sine of an angle. /// /// An angle, in radians /// The value of sin(x) [Pure] public static double Sin(double x) => Math.Sin(x); /// /// Calculates the cosine of an angle. /// /// An angle, in radians /// The value of cos(x) [Pure] public static double Cos(double x) => Math.Cos(x); /// /// Calculates the tangent of an angle. /// /// An angle, in radians /// The value of tan(x) [Pure] public static double Tan(double x) => Math.Tan(x); /// /// Calculates an arcsine. /// /// The value for which an arcsine is to be calculated. /// The value of asin(x), in radians. [Pure] public static double Asin(double x) => Math.Asin(x); /// /// Calculates an arc-cosine. /// /// The value for which an arc-cosine is to be calculated /// The value of acos(x), in radians [Pure] public static double Acos(double x) => Math.Acos(x); /// /// Calculates an arc-tangent. /// /// The value for which an arc-tangent is to be calculated /// The value of atan(x), in radians [Pure] public static double Atan(double x) => Math.Atan(x); /// /// Calculates a hyperbolic sine. /// /// The value for which a hyperbolic sine is to be calculated /// The value of sinh(x) [Pure] public static double Sinh(double x) => Math.Sinh(x); /// /// Calculates a hyperbolic cosine. /// /// The value for which a hyperbolic cosine is to be calculated /// The value of cosh(x) [Pure] public static double Cosh(double x) => Math.Cosh(x); /// /// Calculates a hyperbolic tangent. /// /// /// The value for which a hyperbolic tangent is to be calculated. /// /// The value of tanh(x) [Pure] public static double Tanh(double x) => Math.Tanh(x); /// Calculates an area hyperbolic sine /// The value for which an area hyperbolic sine is to be calculated. /// /// The value of asinh(x). [Pure] public static double Asinh(double x) => Math.Log(x + Math.Sqrt(x * x + 1.0)); /// /// Calculates an area hyperbolic cosine. /// /// The value for which an area hyperbolic cosine is to be calculated. /// /// The value of acosh(x). [Pure] public static double Acosh(double x) => Math.Log(x + Math.Sqrt(x * x - 1.0)); /// /// Calculates an area hyperbolic tangent. /// /// The value for which an area hyperbolic tangent is to be calculated. /// /// The value of atanh(x) [Pure] public static double Atanh(double x) => 0.5 * Math.Log((1.0 + x) / (1.0 - x)); /// /// Negate the value /// /// Value to negate /// The negated source value [Pure] public static double Negate(double x) => -x; /// /// Get the hash-code of the provided value /// /// Hash code of x [Pure] public static int GetHashCode(double x) => x.GetHashCode(); } ================================================ FILE: LanguageExt.Core/Class Instances/TFloat.cs ================================================ using System; using LanguageExt.Traits; using System.Diagnostics.Contracts; namespace LanguageExt.ClassInstances; public struct TFloat : Floating { /// /// Equality test /// /// Left hand side of the equality operation /// Right hand side of the equality operation /// True if parameters are equal [Pure] public static bool Equals(float x, float y) => x.Equals(y); /// /// Compare two values /// /// Left hand side of the compare operation /// Right hand side of the compare operation /// /// if x greater than y : 1 /// /// if x less than y : -1 /// /// if x equals y : 0 /// [Pure] public static int Compare(float x, float y) => x.CompareTo(y); /// /// Find the sum of two values /// /// left hand side of the addition operation /// right hand side of the addition operation /// The sum of x and y [Pure] public static float Add(float x, float y) => x + y; /// /// Find the difference between two values /// /// left hand side of the subtraction operation /// right hand side of the subtraction operation /// The difference between x and y [Pure] public static float Subtract(float x, float y) => x - y; /// /// Find the product of two values /// /// left hand side of the product operation /// right hand side of the product operation /// The product of x and y [Pure] public static float Multiply(float x, float y) => x * y; /// /// Divide two numbers /// /// left hand side of the division operation /// right hand side of the division operation /// x / y [Pure] public static float Divide(float x, float y) => x / y; /// /// Find the absolute value of a number /// /// The value to find the absolute value of /// The non-negative absolute value of x [Pure] public static float Abs(float x) => Math.Abs(x); /// /// Find the sign of x /// /// The value to find the sign of /// -1, 0, or +1 [Pure] public static float Signum(float x) => Math.Sign(x); /// /// Generate a numeric value from an integer /// /// The integer to use /// The equivalent of x in the Num〈A〉 [Pure] public static float FromInteger(int x) => x; /// /// Generate a numeric value from a decimal /// /// The decimal to use /// The equivalent of x in the Num〈A〉 [Pure] public static float FromDecimal(decimal x) => (float)x; /// /// Generate a numeric value from a float /// /// The float to use /// The equivalent of x in the Num〈A〉 [Pure] public static float FromFloat(float x) => x; /// /// Generate a numeric value from a double /// /// The double to use /// The equivalent of x in the Num〈A〉 [Pure] public static float FromDouble(double x) => (float)x; /// /// Generates a fractional value from an integer ratio. /// /// The ratio to convert /// The equivalent of x in the implementing type. [Pure] public static float FromRational(Ratio x) => (float)x.Numerator / x.Denominator; /// /// Returns an approximation of pi. /// /// A reasonable approximation of pi in this type [Pure] public static float Pi() => (float)Math.PI; /// /// The exponential function. /// /// The value for which we are calculating the exponential /// The value of e^x [Pure] public static float Exp(float x) => (float)Math.Exp(x); /// /// Calculates the square root of a value. /// /// The value for which we are calculating the square root. /// The value of sqrt(x). [Pure] public static float Sqrt(float x) => (float)Math.Sqrt(x); /// /// Calculates the natural logarithm of a value. /// /// /// The value for which we are calculating the natural logarithm. /// /// The value of ln(x). [Pure] public static float Log(float x) => (float)Math.Log(x); /// Raises x to the power y /// /// The base to be raised to y /// The exponent to which we are raising x /// The value of x^y. [Pure] public static float Pow(float x, float y) => (float)Math.Pow(x, y); /// /// Calculates the logarithm of a value with respect to an arbitrary base. /// /// The base to use for the logarithm of t /// The value for which we are calculating the logarithm. /// The value of log x (y). [Pure] public static float LogBase(float b, float x) => (float)Math.Log(x, b); /// /// Calculates the sine of an angle. /// /// An angle, in radians /// The value of sin(x) [Pure] public static float Sin(float x) => (float)Math.Sin(x); /// /// Calculates the cosine of an angle. /// /// An angle, in radians /// The value of cos(x) [Pure] public static float Cos(float x) => (float)Math.Cos(x); /// /// Calculates the tangent of an angle. /// /// An angle, in radians /// The value of tan(x) [Pure] public static float Tan(float x) => (float)Math.Tan(x); /// /// Calculates an arcsine. /// /// The value for which an arcsine is to be calculated. /// The value of asin(x), in radians. [Pure] public static float Asin(float x) => (float)Math.Asin(x); /// /// Calculates an arc-cosine. /// /// The value for which an arc-cosine is to be calculated /// The value of acos(x), in radians [Pure] public static float Acos(float x) => (float)Math.Acos(x); /// /// Calculates an arc-tangent. /// /// The value for which an arc-tangent is to be calculated /// The value of atan(x), in radians [Pure] public static float Atan(float x) => (float)Math.Atan(x); /// /// Calculates a hyperbolic sine. /// /// The value for which a hyperbolic sine is to be calculated /// The value of sinh(x) [Pure] public static float Sinh(float x) => (float)Math.Sinh(x); /// /// Calculates a hyperbolic cosine. /// /// The value for which a hyperbolic cosine is to be calculated /// The value of cosh(x) [Pure] public static float Cosh(float x) => (float)Math.Cosh(x); /// /// Calculates a hyperbolic tangent. /// /// /// The value for which a hyperbolic tangent is to be calculated. /// /// The value of tanh(x) [Pure] public static float Tanh(float x) => (float)Math.Tanh(x); /// Calculates an area hyperbolic sine /// The value for which an area hyperbolic sine is to be calculated. /// /// The value of asinh(x). [Pure] public static float Asinh(float x) => (float)Math.Log(x + Math.Sqrt((x * x) + 1.0)); /// /// Calculates an area hyperbolic cosine. /// /// The value for which an area hyperbolic cosine is to be calculated. /// /// The value of acosh(x). [Pure] public static float Acosh(float x) => (float)Math.Log(x + Math.Sqrt((x * x) - 1.0)); /// /// Calculates an area hyperbolic tangent. /// /// The value for which an area hyperbolic tangent is to be calculated. /// /// The value of atanh(x) [Pure] public static float Atanh(float x) => 0.5f * (float)Math.Log((1.0 + x) / (1.0 - x)); /// /// Negate the value /// /// Value to negate /// The negated source value [Pure] public static float Negate(float x) => -x; /// /// Get the hash-code of the provided value /// /// Hash code of x [Pure] public static int GetHashCode(float x) => x.GetHashCode(); } ================================================ FILE: LanguageExt.Core/Class Instances/TInt.cs ================================================ using System; using LanguageExt.Traits; using System.Diagnostics.Contracts; namespace LanguageExt.ClassInstances; /// /// Integer number /// public struct TInt : Num, Bool { /// /// Equality test /// /// The left hand side of the equality operation /// The right hand side of the equality operation /// True if x and y are equal [Pure] public static bool Equals(int x, int y) => x == y; /// /// Compare two values /// /// Left hand side of the compare operation /// Right hand side of the compare operation /// /// if x greater than y : 1 /// /// if x less than y : -1 /// /// if x equals y : 0 /// [Pure] public static int Compare(int x, int y) => x.CompareTo(y); /// /// Find the sum of two numbers /// /// left hand side of the addition operation /// right hand side of the addition operation /// The sum of x and y [Pure] public static int Add(int x, int y) => x + y; /// /// Find the difference between two values /// /// left hand side of the subtraction operation /// right hand side of the subtraction operation /// The difference between x and y [Pure] public static int Subtract(int x, int y) => x - y; /// /// Find the product of two numbers /// /// left hand side of the product operation /// right hand side of the product operation /// The product of x and y [Pure] public static int Multiply(int x, int y) => x * y; /// /// Divide two numbers /// /// left hand side of the division operation /// right hand side of the division operation /// x / y [Pure] public static int Divide(int x, int y) => x / y; /// /// Find the absolute value of a number /// /// The value to find the absolute value of /// The non-negative absolute value of x [Pure] public static int Abs(int x) => Math.Abs(x); /// /// Find the sign of x /// /// The value to find the sign of /// -1, 0, or +1 [Pure] public static int Signum(int x) => Math.Sign(x); /// /// Generate a numeric value from an integer /// /// The integer to use /// The equivalent of x in the Num〈A〉 [Pure] public static int FromInteger(int x) => x; /// /// Generate a numeric value from a float /// /// The float to use /// The equivalent of x in the Num〈A〉 [Pure] public static int FromDecimal(decimal x) => (int)x; /// /// Generate a numeric value from a double /// /// The double to use /// The equivalent of x in the Num〈A〉 [Pure] public static int FromFloat(float x) => (int)x; /// /// Generate a numeric value from a decimal /// /// The decimal to use /// The equivalent of x in the Num〈A〉 [Pure] public static int FromDouble(double x) => (int)x; /// /// Negate the value /// /// Value to negate /// The negated source value [Pure] public static int Negate(int x) => -x; /// /// Get the hash-code of the provided value /// /// Hash code of x [Pure] public static int GetHashCode(int x) => x.GetHashCode(); /// /// Returns True /// /// True [Pure] public static int True() => -1; /// /// Returns False /// /// False [Pure] public static int False() => 0; /// /// Returns the result of the bitwise AND operation between `a` and `b` /// /// The result of the bitwise AND operation between `a` and `b` [Pure] public static int And(int a, int b) => a & b; /// /// Returns the result of the bitwise OR operation between `a` and `b` /// /// The result of the bitwise OR operation between `a` and `b` [Pure] public static int Or(int a, int b) => a | b; /// /// Returns the result of the bitwise NOT operation on `a` /// /// The result of the bitwise NOT operation on `a` [Pure] public static int Not(int a) => ~a; /// /// Returns the result of the bitwise exclusive-OR operation between `a` and `b` /// /// The result of the bitwise exclusive-OR operation between `a` and `b` [Pure] public static int XOr(int a, int b) => a ^ b; /// /// Logical implication /// /// If `a` is true that implies `b`, else `true` [Pure] public static int Implies(int a, int b) => And(a, True()) == False() ? True() : b; /// /// Bitwise bi-conditional. /// /// `Not(XOr(a, b))` [Pure] public static int BiCondition(int a, int b) => Not(XOr(a, b)); } ================================================ FILE: LanguageExt.Core/Class Instances/TLong.cs ================================================ using System; using LanguageExt.Traits; using System.Diagnostics.Contracts; namespace LanguageExt.ClassInstances; /// /// Long integer number /// public struct TLong : Num, Bool { /// /// Equality test /// /// The left hand side of the equality operation /// The right hand side of the equality operation /// True if x and y are equal [Pure] public static bool Equals(long x, long y) => x == y; /// /// Compare two values /// /// Left hand side of the compare operation /// Right hand side of the compare operation /// /// if x greater than y : 1 /// /// if x less than y : -1 /// /// if x equals y : 0 /// [Pure] public static int Compare(long x, long y) => x.CompareTo(y); /// /// Find the sum of two numbers /// /// left hand side of the addition operation /// right hand side of the addition operation /// The sum of x and y [Pure] public static long Add(long x, long y) => x + y; /// /// Find the difference between two values /// /// left hand side of the subtraction operation /// right hand side of the subtraction operation /// The difference between x and y [Pure] public static long Subtract(long x, long y) => x - y; /// /// Fund the product of two numbers /// /// left hand side of the product operation /// right hand side of the product operation /// The product of x and y [Pure] public static long Multiply(long x, long y) => x * y; /// /// Divide x by y /// /// left hand side of the division operation /// right hand side of the division operation /// x / y [Pure] public static long Divide(long x, long y) => x / y; /// /// Find the absolute value of a number /// /// The value to find the absolute value of /// The non-negative absolute value of x [Pure] public static long Abs(long x) => Math.Abs(x); /// /// Find the sign of x /// /// The value to find the sign of /// -1, 0, or +1 [Pure] public static long Signum(long x) => Math.Sign(x); /// /// Generate a numeric value from an integer /// /// The integer to use /// The equivalent of x in the Num〈A〉 [Pure] public static long FromInteger(int x) => x; /// /// Generate a numeric value from a float /// /// The float to use /// The equivalent of x in the Num〈A〉 [Pure] public static long FromDecimal(decimal x) => (long)x; /// /// Generate a numeric value from a double /// /// The double to use /// The equivalent of x in the Num〈A〉 [Pure] public static long FromFloat(float x) => (long)x; /// /// Generate a numeric value from a decimal /// /// The decimal to use /// The equivalent of x in the Num〈A〉 [Pure] public static long FromDouble(double x) => (long)x; /// /// Negate the value /// /// Value to negate /// The negated source value [Pure] public static long Negate(long x) => -x; /// /// Get the hash-code of the provided value /// /// Hash code of x [Pure] public static int GetHashCode(long x) => x.GetHashCode(); /// /// Returns True /// /// True [Pure] public static long True() => -1; /// /// Returns False /// /// False [Pure] public static long False() => 0; /// /// Returns the result of the bitwise AND operation between `a` and `b` /// /// The result of the bitwise AND operation between `a` and `b` [Pure] public static long And(long a, long b) => a & b; /// /// Returns the result of the bitwise OR operation between `a` and `b` /// /// The result of the bitwise OR operation between `a` and `b` [Pure] public static long Or(long a, long b) => a | b; /// /// Returns the result of the bitwise NOT operation on `a` /// /// The result of the bitwise NOT operation on `a` [Pure] public static long Not(long a) => ~a; /// /// Returns the result of the bitwise exclusive-OR operation between `a` and `b` /// /// The result of the bitwise exclusive-OR operation between `a` and `b` [Pure] public static long XOr(long a, long b) => a ^ b; /// /// Logical implication /// /// If `a` is true that implies `b`, else `true` [Pure] public static long Implies(long a, long b) => And(a, True()) == False() ? True() : b; /// /// Bitwise bi-conditional. /// /// `Not(XOr(a, b))` [Pure] public static long BiCondition(long a, long b) => Not(XOr(a, b)); } ================================================ FILE: LanguageExt.Core/Class Instances/TNumericChar.cs ================================================ using LanguageExt.Traits; using System.Diagnostics.Contracts; namespace LanguageExt.ClassInstances; /// /// Integer number /// public struct TNumericChar : Num { /// /// Equality test /// /// The left hand side of the equality operation /// The right hand side of the equality operation /// True if x and y are equal [Pure] public static bool Equals(char x, char y) => x == y; /// /// Compare two values /// /// Left hand side of the compare operation /// Right hand side of the compare operation /// /// if x greater than y : 1 /// /// if x less than y : -1 /// /// if x equals y : 0 /// [Pure] public static int Compare(char x, char y) => CharToInt(x).CompareTo(CharToInt(y)); /// /// Get the hash-code of the provided value /// /// Hash code of x [Pure] public static int GetHashCode(char x) => x.GetHashCode(); [Pure] public static char Add(char x, char y) => (char)(CharToInt(x) + CharToInt(y)); [Pure] public static char Subtract(char x, char y) => (char)(CharToInt(x) - CharToInt(y)); [Pure] public static char Multiply(char x, char y) => (char)(CharToInt(x) * CharToInt(y)); [Pure] public static char Negate(char x) => (char)(-CharToInt(x)); static int CharToInt(int x) => x > 32768 ? -(65536 - x) : x; public static char Abs(char x) => x; public static char Signum(char x) => (char)1; public static char FromInteger(int x) => (char)x; public static char FromDecimal(decimal x) => (char)x; public static char FromFloat(float x) => (char)x; public static char FromDouble(double x) => (char)x; public static char Divide(char x, char y) => (char)(CharToInt(x) / CharToInt(y)); } ================================================ FILE: LanguageExt.Core/Class Instances/TShort.cs ================================================ using System; using LanguageExt.Traits; using System.Diagnostics.Contracts; namespace LanguageExt.ClassInstances; /// /// Short integer number /// public struct TShort : Num, Bool { /// /// Equality test /// /// The left hand side of the equality operation /// The right hand side of the equality operation /// True if x and y are equal [Pure] public static bool Equals(short x, short y) => x == y; /// /// Compare two values /// /// Left hand side of the compare operation /// Right hand side of the compare operation /// /// if x greater than y : 1 /// /// if x less than y : -1 /// /// if x equals y : 0 /// [Pure] public static int Compare(short x, short y) => x.CompareTo(y); /// /// Find the sum of two numbers /// /// left hand side of the addition operation /// right hand side of the addition operation /// The sum of x and y [Pure] public static short Add(short x, short y) => (short)(x + y); /// /// Find the difference between two values /// /// left hand side of the subtraction operation /// right hand side of the subtraction operation /// The difference between x and y [Pure] public static short Subtract(short x, short y) => (short)(x - y); /// /// Fund the product of two numbers /// /// left hand side of the product operation /// right hand side of the product operation /// The product of x and y [Pure] public static short Multiply(short x, short y) => (short)(x * y); /// /// Divide x by y /// /// left hand side of the division operation /// right hand side of the division operation /// x / y [Pure] public static short Divide(short x, short y) => (short)(x / y); /// /// Find the absolute value of a number /// /// The value to find the absolute value of /// The non-negative absolute value of x [Pure] public static short Abs(short x) => Math.Abs(x); /// /// Find the sign of x /// /// The value to find the sign of /// -1, 0, or +1 [Pure] public static short Signum(short x) => (short)Math.Sign(x); /// /// Generate a numeric value from an integer /// /// The integer to use /// The equivalent of x in the Num〈A〉 [Pure] public static short FromInteger(int x) => (short)x; /// /// Generate a numeric value from a float /// /// The float to use /// The equivalent of x in the Num〈A〉 [Pure] public static short FromDecimal(decimal x) => (short)x; /// /// Generate a numeric value from a double /// /// The double to use /// The equivalent of x in the Num〈A〉 [Pure] public static short FromFloat(float x) => (short)x; /// /// Generate a numeric value from a decimal /// /// The decimal to use /// The equivalent of x in the Num〈A〉 [Pure] public static short FromDouble(double x) => (short)x; /// /// Negate the value /// /// Value to negate /// The negated source value [Pure] public static short Negate(short x) => (short)-x; /// /// Get the hash-code of the provided value /// /// Hash code of x [Pure] public static int GetHashCode(short x) => x.GetHashCode(); /// /// Returns True /// /// True [Pure] public static short True() => -1; /// /// Returns False /// /// False [Pure] public static short False() => 0; /// /// Returns the result of the bitwise AND operation between `a` and `b` /// /// The result of the bitwise AND operation between `a` and `b` [Pure] public static short And(short a, short b) => (short)(a & b); /// /// Returns the result of the bitwise OR operation between `a` and `b` /// /// The result of the bitwise OR operation between `a` and `b` [Pure] public static short Or(short a, short b) => (short)(a | b); /// /// Returns the result of the bitwise NOT operation on `a` /// /// The result of the bitwise NOT operation on `a` [Pure] public static short Not(short a) => (short)(~a); /// /// Returns the result of the bitwise exclusive-OR operation between `a` and `b` /// /// The result of the bitwise exclusive-OR operation between `a` and `b` [Pure] public static short XOr(short a, short b) => (short)(a ^ b); /// /// Logical implication /// /// If `a` is true that implies `b`, else `true` [Pure] public static short Implies(short a, short b) => And(a, True()) == False() ? True() : b; /// /// Bitwise bi-conditional. /// /// `Not(XOr(a, b))` [Pure] public static short BiCondition(short a, short b) => Not(XOr(a, b)); } ================================================ FILE: LanguageExt.Core/Class Instances/TString.cs ================================================ using LanguageExt.Traits; using System.Diagnostics.Contracts; namespace LanguageExt.ClassInstances; public struct TString : Ord { /// /// Equality test /// /// The left hand side of the equality operation /// The right hand side of the equality operation /// True if x and y are equal [Pure] public static bool Equals(string x, string y) => EqString.Equals(x, y); /// /// Compare two values /// /// Left hand side of the compare operation /// Right hand side of the compare operation /// /// if x greater than y : 1 /// /// if x less than y : -1 /// /// if x equals y : 0 /// [Pure] public static int Compare(string x, string y) => OrdString.Compare(x,y); /// /// Get the hash-code of the provided value /// /// Hash code of x [Pure] public static int GetHashCode(string x) => OrdString.GetHashCode(x); } public struct TStringOrdinalIgnoreCase : Ord { /// /// Equality test /// /// The left hand side of the equality operation /// The right hand side of the equality operation /// True if x and y are equal [Pure] public static bool Equals(string x, string y) => EqStringOrdinalIgnoreCase.Equals(x, y); /// /// Compare two values /// /// Left hand side of the compare operation /// Right hand side of the compare operation /// /// if x greater than y : 1 /// /// if x less than y : -1 /// /// if x equals y : 0 /// [Pure] public static int Compare(string x, string y) => OrdStringOrdinalIgnoreCase.Compare(x, y); /// /// Get the hash-code of the provided value /// /// Hash code of x [Pure] public static int GetHashCode(string x) => x.IsNull() ? 0 : x.GetHashCode(); } public struct TStringOrdinal : Ord { /// /// Equality test /// /// The left hand side of the equality operation /// The right hand side of the equality operation /// True if x and y are equal [Pure] public static bool Equals(string x, string y) => EqStringOrdinal.Equals(x, y); /// /// Compare two values /// /// Left hand side of the compare operation /// Right hand side of the compare operation /// /// if x greater than y : 1 /// /// if x less than y : -1 /// /// if x equals y : 0 /// [Pure] public static int Compare(string x, string y) => OrdStringOrdinal.Compare(x, y); /// /// Get the hash-code of the provided value /// /// Hash code of x [Pure] public static int GetHashCode(string x) => x.GetHashCode(); } public struct TStringCurrentCultureIgnoreCase : Ord { /// /// Equality test /// /// The left hand side of the equality operation /// The right hand side of the equality operation /// True if x and y are equal [Pure] public static bool Equals(string x, string y) => EqStringCurrentCultureIgnoreCase.Equals(x, y); /// /// Compare two values /// /// Left hand side of the compare operation /// Right hand side of the compare operation /// /// if x greater than y : 1 /// /// if x less than y : -1 /// /// if x equals y : 0 /// [Pure] public static int Compare(string x, string y) => OrdStringCurrentCultureIgnoreCase.Compare(x, y); /// /// Get the hash-code of the provided value /// /// Hash code of x [Pure] public static int GetHashCode(string x) => x.IsNull() ? 0 : x.GetHashCode(); } public struct TStringCurrentCulture : Ord { /// /// Equality test /// /// The left hand side of the equality operation /// The right hand side of the equality operation /// True if x and y are equal [Pure] public static bool Equals(string x, string y) => EqStringCurrentCulture.Equals(x, y); /// /// Compare two values /// /// Left hand side of the compare operation /// Right hand side of the compare operation /// /// if x greater than y : 1 /// /// if x less than y : -1 /// /// if x equals y : 0 /// [Pure] public static int Compare(string x, string y) => OrdStringCurrentCulture.Compare(x, y); /// /// Get the hash-code of the provided value /// /// Hash code of x [Pure] public static int GetHashCode(string x) => x.IsNull() ? 0 : x.GetHashCode(); } ================================================ FILE: LanguageExt.Core/Combinators.cs ================================================ using System; using static LanguageExt.Prelude; namespace LanguageExt { public static class CombinatorsDynamic { /// /// Identity function, or the Idiot bird [dkeenan.com/Lambda/](http://dkeenan.com/Lambda/) /// public static Func I = (dynamic x) => x; /// /// The Mockingbird [dkeenan.com/Lambda/](http://dkeenan.com/Lambda/) /// public static Func> M = (dynamic x) => (dynamic a) => x(x(a)); /// /// The Kestrel [dkeenan.com/Lambda/](http://dkeenan.com/Lambda/) /// public static Func> K = (dynamic x) => (dynamic y) => x; /// /// The Thrush [dkeenan.com/Lambda/](http://dkeenan.com/Lambda/) /// public static Func> T = (dynamic x) => (dynamic y) => y(x); /// /// The Queer bird [dkeenan.com/Lambda/](http://dkeenan.com/Lambda/) /// public static Func>> Q = (dynamic x) => (dynamic y) => (dynamic z) => y(x(z)); /// /// The Starling [dkeenan.com/Lambda/](http://dkeenan.com/Lambda/) /// public static Func>> S = (dynamic x) => (dynamic y) => (dynamic z) => x(z)(y(z)); /// /// The infamous Y-combinator, or Sage bird [dkeenan.com/Lambda/](http://dkeenan.com/Lambda/) /// public static Func> Y = (dynamic f) => (dynamic x) => f(Y(f), x); } public static class Combinators { /// /// Identity function, or the Idiot bird [dkeenan.com/Lambda/](http://dkeenan.com/Lambda/) /// public static Func I = (A x) => x; /// /// The Mockingbird [dkeenan.com/Lambda/](http://dkeenan.com/Lambda/) /// public static Func, Func> M = (Func x) => a => x(x(a)); } public static class Combinators { /// /// The Kestrel [dkeenan.com/Lambda/](http://dkeenan.com/Lambda/) /// public static Func> K = (A x) => (B y) => x; /// /// The Thrush [dkeenan.com/Lambda/](http://dkeenan.com/Lambda/) /// public static Func, B>> T = (A x) => (Func y) => y(x); } public static class Combinators { /// /// The Queer bird [dkeenan.com/Lambda/](http://dkeenan.com/Lambda/) /// public static Func, Func, Func>> Q = (Func x) => (Func y) => (A z) => y(x(z)); /// /// The Starling [dkeenan.com/Lambda/](http://dkeenan.com/Lambda/) /// public static Func>, Func, Func>> S = (Func> x) => (Func y) => (A z) => x(z)(y(z)); /// /// The infamous Y-combinator, or Sage bird [dkeenan.com/Lambda/](http://dkeenan.com/Lambda/) /// public static Func, A, B>, Func> Y = (Func, A, B> f) => (A x) => f(Y!(f), x); } public static class Combinators { /// /// Identity function, or the Idiot bird [dkeenan.com/Lambda/](http://dkeenan.com/Lambda/) /// public static A I(A x) => x; /// /// The Kestrel [dkeenan.com/Lambda/](http://dkeenan.com/Lambda/) /// public static Func K(A x) => (B y) => x; /// /// The Mockingbird [dkeenan.com/Lambda/](http://dkeenan.com/Lambda/) /// public static Func M(Func x) => a => x(x(a)); /// /// The Thrush [dkeenan.com/Lambda/](http://dkeenan.com/Lambda/) /// public static Func, B> T(A x) => (Func y) => y(x); /// /// The Queer bird [dkeenan.com/Lambda/](http://dkeenan.com/Lambda/) /// public static Func, Func> Q(Func x) => (Func y) => (A z) => y(x(z)); /// /// The Starling [dkeenan.com/Lambda/](http://dkeenan.com/Lambda/) /// public static Func, Func> S(Func x) => (Func y) => (A z) => x(z, y(z)); } } ================================================ FILE: LanguageExt.Core/Comment Alternatives.cs ================================================ namespace LanguageExt.Core; /// /// Certain characters don't work in the XML comments, so we use these alternatives /// /// < becomes 〈 (Unicode 3008) /// > becomes 〉 (Unicode 3009) /// & becomes & (Unicode FF06) /// /// public class Comment_Alternatives; ================================================ FILE: LanguageExt.Core/Common/Error.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics.Contracts; using System.Runtime.CompilerServices; using System.Runtime.ExceptionServices; using System.Runtime.Serialization; using System.Text; using System.Threading.Tasks; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt.Common; /// /// Abstract error value /// [DataContract] public abstract record Error : Monoid { /// /// Error code /// [Pure] [DataMember] public virtual int Code => 0; /// /// Error message /// [Pure] [DataMember] public abstract string Message { get; } /// /// Inner error /// [Pure] [IgnoreDataMember] public virtual Option Inner => None; /// /// If this error represents an exceptional error, then this will return true if the exceptional error is of type E /// [Pure] public virtual bool HasException() where E : Exception => false; /// /// Return true if an `Error` with `Code` equalling `code` is contained within /// [Pure] public virtual bool HasCode(int code) => Code == code; /// /// Return true if this error contains or *is* the `error` provided /// [Pure] public virtual bool IsType() where E : Error => this is E; /// /// Return true if this error contains or *is* the `error` provided /// [Pure] public virtual bool Is(Error error) => error is ManyErrors errors ? errors.Errors.Exists(Is) : Code == 0 ? Message == error.Message : Code == error.Code; /// /// Filter the error(s) so that only errors of type `E` are left /// /// /// If no errors match, then returns `Errors.None`. /// [Pure] public virtual Error Filter() where E : Error => this is E ? this : Errors.None; /// /// For each error in this structure, invoke the `f` function and /// monad bind it with the subsequent value. /// /// If any `f` invocation yields a failure, then subsequent error will /// not be processed. /// monad /// /// /// The final `A` is returned. /// /// Function /// The final `A` is returned. [Pure] public virtual K ForAllM(Func> f) where M : Monad, Fallible => f(this); /// /// For each error in this structure, invoke the `f` function and /// monoid combine it with the subsequent value. /// /// If any `f` invocation yields a failure, then subsequent error /// values may be processed. This is dependent on the `M` monoid. /// /// /// The aggregated `K〈M, A〉` is returned. /// /// Function /// The aggregated `K〈M, A〉` is returned. [Pure] public virtual K FoldM(Func> f) where M : MonoidK => f(this); /// /// True if the error is exceptional /// [Pure] [IgnoreDataMember] public abstract bool IsExceptional { get; } /// /// True if the error is expected /// [Pure] [IgnoreDataMember] public abstract bool IsExpected { get; } /// /// Get the first error (this will be `Errors.None` if there are zero errors) /// [Pure] [IgnoreDataMember] public virtual Error Head => this; /// /// Get the errors with the head removed (this may be `Errors.None` if there are zero errors in the tail) /// [Pure] [IgnoreDataMember] public virtual Error Tail => Errors.None; /// /// This type can contain zero or more errors. /// /// If `IsEmpty` is `true` then this is like `None` in `Option`: still an error, but without any specific /// information about the error. /// [Pure] [IgnoreDataMember] public virtual bool IsEmpty => false; /// /// This type can contain zero or more errors. This property returns the number of information carrying errors. /// /// If `Count` is `0` then this is like `None` in `Option`: still an error, but without any specific information /// about the error. /// [Pure] [IgnoreDataMember] public virtual int Count => 1; /// /// If this error represents an exceptional error, then this will return that exception, otherwise it will /// generate a new ErrorException that contains the code, message, and inner of this Error. /// [Pure] public virtual Exception ToException() => ToErrorException(); /// /// If this error represents an exceptional error, then this will return that exception, otherwise `None` /// [Pure] public Option Exception => IsExceptional ? Some(ToException()) : None; /// /// Convert to an `ErrorException` which is like `Error` but derived from the `Exception` hierarchy /// [Pure] public abstract ErrorException ToErrorException(); /// /// Append an error to this error /// /// Single errors will be converted to `ManyErrors`; `ManyErrors` will have their collection updated /// Error /// [Pure] public Error Combine(Error error) => (this, error) switch { ({IsEmpty: true}, var e) => e, (var e, {IsEmpty: true}) => e, (ManyErrors e1, ManyErrors e2) => new ManyErrors(e1.Errors + e2.Errors), (ManyErrors e1, var e2) => new ManyErrors(e1.Errors.Add(e2)), (var e1, ManyErrors e2) => new ManyErrors(e1.Cons(e2.Errors)), (var e1, var e2) => new ManyErrors(Seq(e1, e2)) }; [Pure] public static Error Empty => ManyErrors.Empty; /// /// Append an error to this error /// /// Single errors will be converted to `ManyErrors`; `ManyErrors` will have their collection updated [Pure, MethodImpl(MethodImplOptions.AggressiveInlining)] public static Error operator+(Error lhs, Error rhs) => lhs.Combine(rhs); [Pure, MethodImpl(MethodImplOptions.AggressiveInlining)] public virtual Iterable AsIterable() { return Iterable.createRange(go()); IEnumerable go() { yield return this; } } /// /// Convert the error to a string /// [Pure] public override string ToString() => Message; /// /// Create an `Exceptional` error /// /// Exception [Pure, MethodImpl(MethodImplOptions.AggressiveInlining)] public static Error New(Exception? thisException) => thisException switch { null => Errors.None, WrappedErrorExceptionalException w => w.ToError(), WrappedErrorExpectedException w => w.ToError(), ErrorException e => e.ToError(), OperationCanceledException => Errors.Cancelled, TimeoutException => Errors.TimedOut, AggregateException a => ManyErrors.FromAggregate(a), var e => new Exceptional(e) }; /// /// Create a `Exceptional` error with an overriden message. This can be useful for sanitising the display message /// when internally we're carrying the exception. /// /// Error message /// Exception [Pure, MethodImpl(MethodImplOptions.AggressiveInlining)] public static Error New(string message, Exception? thisException) => new Exceptional(message, thisException ?? new BottomException()); /// /// Create an `Expected` error /// /// Error message [Pure, MethodImpl(MethodImplOptions.AggressiveInlining)] public static Error New(string message) => new Expected(message, 0, None); /// /// Create an `Expected` error /// /// Error code /// Error message [Pure, MethodImpl(MethodImplOptions.AggressiveInlining)] public static Error New(int code, string message) => new Expected(message, code, None); /// /// Create an `Expected` error /// /// Error code /// Error message /// The inner error to this error [Pure, MethodImpl(MethodImplOptions.AggressiveInlining)] public static Error New(int code, string message, Error inner) => new Expected(message, code, inner); /// /// Create an `Expected` error /// /// Error message /// The inner error to this error [Pure, MethodImpl(MethodImplOptions.AggressiveInlining)] public static Error New(string message, Error inner) => new Expected(message, 0, inner); /// /// Create a `ManyErrors` error /// /// Collects many errors into a single `Error` type, called `ManyErrors` /// Error code /// The inner error to this error [Pure, MethodImpl(MethodImplOptions.AggressiveInlining)] public static Error Many(params Error[] errors) => errors.Length == 0 ? Errors.None : errors.Length == 1 ? errors[0] : new ManyErrors(errors.AsIterable().ToSeq()); /// /// Create a new error /// /// Error code /// The inner error to this error [Pure, MethodImpl(MethodImplOptions.AggressiveInlining)] public static Error Many(Seq errors) => errors.IsEmpty ? Errors.None : errors.Tail.IsEmpty ? (Error)errors.Head : new ManyErrors(errors); [Pure, MethodImpl(MethodImplOptions.AggressiveInlining)] public static explicit operator Error(string e) => New(e); [Pure, MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator Error((int Code, string Message) e) => New(e.Code, e.Message); [Pure, MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator Error(Exception e) => New(e); [Pure, MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator Exception(Error e) => e.ToException(); /// /// Attempt to recover an error from an object. /// Will accept Error, ErrorException, Exception, string, Option〈Error〉 /// If it fails, Errors.Bottom is returned /// [Pure] public static Error FromObject(object? value) => value switch { Error err => err, WrappedErrorExceptionalException w => w.ToError(), WrappedErrorExpectedException w => w.ToError(), ErrorException ex => ex.ToError(), Exception ex => New(ex), string str => New(str), Option oerr => oerr.IfNone(Errors.Bottom), _ => Errors.Bottom }; [Pure] internal static Option Convert(object? err) => err switch { // Messy, but we're doing our best to recover an error rather than return Bottom FAIL fail => fail, Exception e when typeof(FAIL) == typeof(Error) => (FAIL)(object)New(e), Exception e when typeof(FAIL) == typeof(string) => (FAIL)(object)e.Message, Error e when typeof(FAIL) == typeof(Exception) => (FAIL)(object)e.ToException(), Error e when typeof(FAIL) == typeof(string) => (FAIL)(object)e.ToString(), string e when typeof(FAIL) == typeof(Exception) => (FAIL)(object)new Exception(e), string e when typeof(FAIL) == typeof(Error) => (FAIL)(object)New(e), _ => None }; /// /// Throw the error as an exception /// public Unit Throw() { ExceptionDispatchInfo.Capture(ToException()).Throw(); return default; } /// /// Throw the error as an exception /// public virtual R Throw() { ExceptionDispatchInfo.Capture(ToException()).Throw(); return default; } protected virtual bool PrintMembers(StringBuilder builder) { builder.Append("Message = "); builder.Append(Message); if (Code != 0) { builder.Append(", Code = "); builder.Append(Code); } return true; } } /// /// Contains the following: /// /// * `Code` - an integer value /// * `Message` - display text /// * `Option〈Error〉` - a nested inner error /// /// It returns `false` when `IsExceptional` is called against it; and `true` when `IsExpected` is /// called against it. /// /// Equality is done via the `Code`, so any two `Expected` errors with the same `Code` will be considered /// the same. This is useful when using the `@catch` behaviours with the `Aff` and `Eff` monads. If the /// `Code` is `0` then equality is done by comparing `Message`. /// /// > This allows for localised error messages where the message is ignored when matching/catching /// /// Error message /// Error code /// Optional inner error [DataContract] public record Expected(string Message, int Code, Option Inner = default) : Error { /// /// Error message /// [Pure] [DataMember] public override string Message { get; } = Message; /// /// Error code /// [Pure] [DataMember] public override int Code { get; } = Code; /// /// Inner error /// [Pure] [IgnoreDataMember] public override Option Inner { get; } = Inner; [Pure] public override string ToString() => Message; /// /// Generates a new `ErrorException` that contains the `Code`, `Message`, and `Inner` of this `Error`. /// [Pure] public override ErrorException ToErrorException() => new WrappedErrorExpectedException(this); public override R Throw() => throw ToErrorException(); /// /// Returns false because this type isn't exceptional /// [Pure] public override bool HasException() => false; /// /// True if the error is exceptional /// [Pure] [IgnoreDataMember] public override bool IsExceptional => false; /// /// True if the error is expected /// [Pure] [IgnoreDataMember] public override bool IsExpected => true; } /// /// This contains an `Exception` is the classic sense. This also returns `true` when `IsExceptional` is /// called against it; and `false` when `IsExpected` is called against it. /// /// /// If this record is constructed via deserialisation, or the default constructor then the internal `Exception` /// will be `null`. This is intentional to stop exceptions leaking over application boundaries. The type will /// gracefully handle that, but all stack-trace information (and the like) will be erased. It is still considered /// an exceptional error, however. /// [DataContract] public record Exceptional(string Message, int Code) : Error { /// /// Internal exception. If this record is constructed via deserialisation, or the default constructor then this /// value will be `null`. This is intentional to stop exceptions leaking over application boundaries. /// [IgnoreDataMember] readonly Exception? Value; /// /// Construct from an exception /// /// Exception internal Exceptional(Exception value) : this(value.Message, value.HResult) => Value = value; /// /// Construct from an exception, but override the message /// /// Message to override with /// Exception internal Exceptional(string message, Exception value) : this(message, value.HResult) => Value = value; [DataMember] public override string Message { get; } = Message; [DataMember] public override int Code { get; } = Code; public override string ToString() => Message; /// /// Returns the inner exception as an `Error` (if one exists), `None` otherwise /// [Pure] public override Option Inner => Value?.InnerException == null ? None : New(Value.InnerException); /// /// Gets the `Exception` /// /// [Pure] public override Exception ToException() => Value ?? new ExceptionalException(Message, Code); /// /// Gets the `ErrorException` /// /// [Pure] public override ErrorException ToErrorException() => Value == null ? new WrappedErrorExceptionalException(this) : new ExceptionalException(Value); public override R Throw() => Value is null ? throw ToErrorException() : Value.Rethrow(); /// /// Return true if the exceptional error is of type E /// [Pure] public override bool HasException() => Value is E; [Pure] public override bool Is(Error error) => error is ManyErrors errors ? errors.Errors.Exists(Is) : Value == null ? error.IsExceptional && Code == error.Code && Message == error.Message : error.IsExceptional && Value.GetType().IsInstanceOfType(error.ToException()); /// /// True if the error is exceptional /// [Pure] [IgnoreDataMember] public override bool IsExceptional => true; /// /// True if the error is expected /// [Pure] [IgnoreDataMember] public override bool IsExpected => false; } /// /// Bottom error /// [DataContract] public sealed record BottomError() : Exceptional(BottomException.Default) { public static readonly Error Default = new BottomError(); [DataMember] public override int Code => ErrorCodes.Bottom; [DataMember] public override string Message => Errors.BottomText; public override string ToString() => Message; /// /// Gets the Exception /// /// public override Exception ToException() => BottomException.Default; /// /// Gets the `ErrorException` /// /// public override ErrorException ToErrorException() => BottomException.Default; public override R Throw() => throw new BottomException(); /// /// Return true if the exceptional error is of type E /// [Pure] public override bool HasException() => BottomException.Default is E; /// /// Return true this error contains or *is* the `error` provided /// [Pure] public override bool Is(Error error) => error is ManyErrors errors ? errors.Errors.Exists(Is) : error is BottomError; /// /// True if the error is exceptional /// [Pure] [IgnoreDataMember] public override bool IsExceptional => true; /// /// True if the error is expected /// [Pure] [IgnoreDataMember] public override bool IsExpected => false; } /// /// `ManyErrors` allows for zero or more errors to be collected. This is useful for applicative behaviours /// like validation. It effectively turns the `Error` type into a monoid, with 'zero' being `Errors.None`, /// and 'append' coming from the `Append` method or use of `operator+` /// /// Errors [DataContract] public sealed record ManyErrors([property: DataMember] Seq Errors) : Error { public new static Error Empty { get; } = new ManyErrors(Seq.empty()); public override int Code => ErrorCodes.ManyErrors; public override string Message { get; } = Errors.ToFullArrayString(); public override string ToString() => Errors.ToFullArrayString(); /// /// Gets the `Exception` /// public override Exception ToException() => new AggregateException(Errors.Map(static e => e.ToException())); /// /// Gets the `ErrorException` /// public override ErrorException ToErrorException() => new ManyExceptions(Errors.Map(static e => e.ToErrorException())); public override R Throw() => throw ToErrorException(); /// /// Return true if an exception of type E is contained within /// [Pure] public override bool HasException() => Errors.Exists(static e => e.HasException()); /// /// Return true if an `Error` with `Code` equalling `code` is contained within /// [Pure] public override bool HasCode(int code) => Errors.Exists(e => e.Code == code); [Pure] public override bool IsType() => Errors.Exists(static e => e.IsType()); /// /// Return true this error contains or *is* the `error` provided /// [Pure] public override bool Is(Error error) => Errors.Exists(e => e.Is(error)); /// /// Filter the error(s) so that only errors of type `E` are left /// /// /// If no errors match, then returns `Errors.None`. /// [Pure] public override Error Filter() => Errors.Choose(e => e.IsType() ? Some(e.Filter()) : None) switch { [] => Common.Errors.None, var es => Many(es) }; /// /// For each error in this structure, invoke the `f` function and /// monad bind it with the subsequent value. /// /// If any `f` invocation yields a failure, then subsequent error will /// not be processed. /// monad /// /// /// The final `A` is returned. /// /// Function /// The final `A` is returned. [Pure] public override K ForAllM(Func> f) { if(Errors.IsEmpty) return M.Fail(this); var result = Errors[0].ForAllM(f); foreach (var e in Errors.Tail) { result = result.Bind(_ => e.ForAllM(f)); } return result; } /// /// For each error in this structure, invoke the `f` function and /// monoid combine it with the subsequent value. /// /// If any `f` invocation yields a failure, then subsequent error /// values may be processed. This is dependent on the `M` monoid. /// /// /// The aggregated `K〈M, A〉` is returned. /// /// Function /// The aggregated `K〈M, A〉` is returned. [Pure] public override K FoldM(Func> f) { var result = M.Empty(); foreach (var e in Errors) { result = result.Combine(e.FoldM(f)); } return result; } /// /// True if any errors are exceptional /// [Pure] [IgnoreDataMember] public override bool IsExceptional => Errors.Exists(static e => e.IsExceptional); /// /// True if all the errors are expected /// [Pure] [IgnoreDataMember] public override bool IsExpected => Errors.ForAll(static e => e.IsExpected); /// /// Get the first error (this may be `Errors.None` if there are zero errors) /// [Pure] [IgnoreDataMember] public override Error Head => Errors.IsEmpty ? Common.Errors.None : (Error)Errors.Head; /// /// Get the errors with the head removed (this may be `Errors.None` if there are zero errors in the tail) /// [Pure] [IgnoreDataMember] public override Error Tail => Errors.Tail.IsEmpty ? Common.Errors.None : this with {Errors = Errors.Tail}; /// /// This type can contain zero or more errors. If `IsEmpty` is `true` then this is like `None` in `Option`: still /// an error, but without any specific information about the error. /// [Pure] [IgnoreDataMember] public override bool IsEmpty => Errors.IsEmpty; /// /// This type can contain zero or more errors. This property returns the number of information carrying errors. /// /// If `Count` is `0` then this is like `None` in `Option`: still an error, but without any specific information /// about the error. /// [Pure] [IgnoreDataMember] public override int Count => Errors.Count; [Pure, MethodImpl(MethodImplOptions.AggressiveInlining)] public override Iterable AsIterable() => Errors.AsIterable(); [Pure] internal static Error FromAggregate(AggregateException? e) { if (e is null) return Common.Errors.None; var errs = e.InnerExceptions.Bind(x => New(x).AsIterable()).AsIterable().ToSeq(); if (errs.Count == 0) return Common.Errors.None; if (errs.Count == 1) return (Error)errs.Head; return Many(errs); } } ================================================ FILE: LanguageExt.Core/Common/ErrorCodes.cs ================================================ namespace LanguageExt.Common; public static class ErrorCodes { /// /// An error that indicates a value from an operation couldn't be evaluated. This is a hard /// fail for systems that depend on expressions to produce results. /// public const int Bottom = -2000000000; /// /// Cancelled error /// public const int Cancelled = -2000000001; /// /// Timed-out error /// public const int TimedOut = -2000000002; /// /// Sequence-empty error /// public const int SequenceEmpty = -2000000003; /// /// Closed error /// public const int Closed = -2000000004; /// /// Parsing error /// public const int ParseError = -2000000005; /// /// Error code that represents multiple errors /// public const int ManyErrors = -2000000006; /// /// IO monad not in transformer stack or `MonadIO.LiftIO` not implemented /// public const int LiftIONotSupported = -2000000007; /// /// IO monad not in transformer stack or `MonadIO.Fork` not implemented /// public const int ForkIONotSupported = -2000000008; /// /// IO monad not in transformer stack or `MonadUnliftIO.ToIO` not implemented /// public const int ToIONotSupported = -2000000009; /// /// End of stream error code /// public const int EndOfStream = -2000000010; /// /// Validation failed /// public const int ValidationFailed = -2000000011; /// /// Source complete /// public const int SourceCompleted = -2000000012; /// /// Source is closed /// public const int SourceClosed = -2000000013; /// /// The programmer is trying to extend the `IO` type, they need to use specific base-types: /// /// * `InvokeAsync` /// * `InvokeSync` /// * `InvokeAsyncIO` /// * `InvokeSyncIO` /// * `IOCatch` /// public const int IODSLExtension = -2000000014; /// /// Sink full error /// public const int SinkFull = -2000000015; } ================================================ FILE: LanguageExt.Core/Common/ErrorException.cs ================================================ using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics.Contracts; using System.Runtime.CompilerServices; using System.Runtime.ExceptionServices; using System.Text; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt.Common; /// /// Error value /// /// /// This is a pair to the `Error` type, to allow `Error` to be converted to-and-from a classic `Exception`. /// /// This allows code that can't handle the `Error` type to still throw something that keeps the fidelity of the `Error` /// type, and can be converted directly to an `Error` in a `catch` block. /// /// /// Unlike exceptions, `Error` can be either: /// /// * `ExceptionalException` - representing an unexpected error /// * `ExpectedException` - representing an expected error /// * `ManyExceptions` - representing many errors /// /// i.e. it is either created from an exception or it isn't. This allows for expected errors to be represented /// without throwing exceptions. /// public abstract class ErrorException : Exception, IEnumerable, Monoid { protected ErrorException(int code) => HResult = code; /// /// Error code /// [Pure] public abstract int Code { get; } /// /// Inner error /// [Pure] public abstract Option Inner { get; } /// /// Convert to an `Error` /// [Pure] public abstract Error ToError(); /// /// This type can contain zero or more errors. If `IsEmpty` is `true` then this is like `None` in `Option`: still /// an error, but without any specific information about the error. /// [Pure] public virtual bool IsEmpty => false; /// /// True if the error is exceptional /// [Pure] public abstract bool IsExceptional { get; } /// /// True if the error is expected /// [Pure] public abstract bool IsExpected { get; } /// /// Append an error to this error /// /// Single errors will be converted to `ManyErrors`; `ManyErrors` will have their collection updated [Pure] public abstract ErrorException Combine(ErrorException error); [Pure] public static ErrorException Empty => ManyExceptions.Empty; /// /// Append an error to this error /// /// Single errors will be converted to `ManyErrors`; `ManyErrors` will have their collection updated [Pure] public static ErrorException operator+(ErrorException lhs, ErrorException rhs) => lhs.Combine(rhs); [Pure] public virtual IEnumerator GetEnumerator() { yield return this; } [Pure] IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); /// /// Convert the error to a string /// [Pure] public override string ToString() => Message; /// /// Create a new error /// /// Error message [Pure, MethodImpl(MethodImplOptions.AggressiveInlining)] public static ErrorException New(Exception thisException) => new ExceptionalException(thisException); /// /// Create a new error /// /// Error message /// The exception this error represents [Pure, MethodImpl(MethodImplOptions.AggressiveInlining)] public static ErrorException New(string message, Exception thisException) => new ExceptionalException(message, thisException); /// /// Create a new error /// /// Error message [Pure, MethodImpl(MethodImplOptions.AggressiveInlining)] public static ErrorException New(string message) => new ExpectedException(message, 0, None); /// /// Create a new error /// /// Error code /// Error message [Pure, MethodImpl(MethodImplOptions.AggressiveInlining)] public static ErrorException New(int code, string message) => new ExpectedException(message, code, None); /// /// Create a new error /// /// Error code /// Error message /// The inner error to this error [Pure, MethodImpl(MethodImplOptions.AggressiveInlining)] public static ErrorException New(int code, string message, ErrorException inner) => new ExpectedException(message, code, inner); /// /// Create a new error /// /// Error code /// The inner error to this error [Pure, MethodImpl(MethodImplOptions.AggressiveInlining)] public static ErrorException New(string message, ErrorException inner) => new ExpectedException(message, 0, inner); /// /// Create a new error /// /// Error code /// The inner error to this error [Pure, MethodImpl(MethodImplOptions.AggressiveInlining)] public static ErrorException Many(params ErrorException[] errors) => new ManyExceptions(errors.AsIterable().ToSeq()); /// /// Create a new error /// /// Error code /// The inner error to this error [Pure, MethodImpl(MethodImplOptions.AggressiveInlining)] public static ErrorException Many(Seq errors) => new ManyExceptions(errors); } /// /// Represents expected errors /// public class ExpectedException(string message, int code, Option inner = default) : ErrorException(code) { /// /// Error code /// [Pure] public override int Code { get; } = code; /// /// Error message /// [Pure] public override string Message { get; } = message; /// /// Convert the error to a string /// [Pure] public override string ToString() => Message; /// /// Inner error /// [Pure] public override Option Inner { get; } = inner; /// /// Generates a new `Error` that contains the `Code`, `Message`, and `Inner` of this `ErrorException`. /// [Pure] public override Error ToError() => new Expected(Message, Code, Inner.Map(static e => e.ToError())); /// /// True if the error is exceptional /// [Pure] public override bool IsExceptional => false; /// /// True if the error is expected /// [Pure] public override bool IsExpected => true; /// /// Append an error to this error /// /// Single errors will be converted to `ManyErrors`; `ManyErrors` will have their collection updated [Pure] public override ErrorException Combine(ErrorException error) => error is ManyExceptions m ? new ManyExceptions(error.Cons(m.Errors)) : new ManyExceptions(Seq(this, error)); } /// /// Wraps an `Error` maintaining its type for subsequent conversion back to an `Error` later /// /// public sealed class WrappedErrorExpectedException(Error Error) : ExpectedException(Error.Message, Error.Code, Error.Inner.Map(e => e.ToErrorException())) { /// /// Convert back to an `Error` /// /// public override Error ToError() => Error; /// /// Convert the error to a string /// [Pure] public override string ToString() => Error.ToString(); } /// /// Represents an exceptional (unexpected) error /// /// Exceptional error public class ExceptionalException : ErrorException { public ExceptionalException(Exception Exception) : base(Exception.HResult) { this.Exception = Exception; Code = Exception.HResult; Message = Exception.Message; } public ExceptionalException(string Message, int Code) : base(Code) { this.Code = Code; this.Message = Message; } public ExceptionalException(string Message, Exception Exception) : base(Exception.HResult) { Code = Exception.HResult; this.Message = Message; } public readonly Exception? Exception; public override int Code { get; } public override string Message { get; } /// /// Convert the error to a string /// [Pure] public override string ToString() => Message; /// /// Returns the inner exception as an `Error` (if one exists), None otherwise /// [Pure] public override Option Inner => Exception?.InnerException == null ? None : New(Exception.InnerException); /// /// Gets the `Error` /// /// public override Error ToError() => Exception == null ? new Exceptional(Message, Code) : new Exceptional(Exception); /// /// True if the error is exceptional /// [Pure] public override bool IsExceptional => true; /// /// True if the error is expected /// [Pure] public override bool IsExpected => false; /// /// Append an error to this error /// /// Single errors will be converted to `ManyErrors`; `ManyErrors` will have their collection updated /// Error /// [Pure] public override ErrorException Combine(ErrorException error) => error is ManyExceptions m ? new ManyExceptions(error.Cons(m.Errors)) : new ManyExceptions(Seq(this, error)); } /// /// Wraps an `Error` maintaining its type for subsequent conversion back to an `Error` later /// /// public sealed class WrappedErrorExceptionalException(Error Error) : ExceptionalException(Error.Message, Error.Code) { /// /// Convert back to an `Error` /// /// public override Error ToError() => Error; /// /// Convert the error to a string /// [Pure] public override string ToString() => Message; } /// /// Represents multiple errors /// /// Errors public sealed class ManyExceptions(Seq errors) : ErrorException(0) { public new static ErrorException Empty { get; } = new ManyExceptions([]); public readonly Seq Errors = errors; public override int Code => ErrorCodes.ManyErrors; public override string Message => Errors.ToFullArrayString(); /// /// Returns the inner exception as an `Error` (if one exists), None otherwise /// [Pure] public override Option Inner => None; /// /// Gets the Exception /// public override Error ToError() => new ManyErrors(Errors.Map(static e => e.ToError())); /// /// Convert the error to a string /// [Pure] public override string ToString() => Errors.ToFullArrayString(); /// /// This type can contain zero or more errors. If `IsEmpty` is `true` then this is like `None` in `Option`: still /// an error, but without any specific information about the error. /// [Pure] public override bool IsEmpty => Errors.IsEmpty; /// /// True if any of the errors are exceptional /// [Pure] public override bool IsExceptional => Errors.Exists(static e => e.IsExceptional); /// /// True if all the errors are expected /// [Pure] public override bool IsExpected => Errors.ForAll(static e => e.IsExpected); /// /// Append an error to this error /// /// Single errors will be converted to `ManyErrors`; `ManyErrors` will have their collection updated /// Error /// [Pure] public override ErrorException Combine(ErrorException error) => error is ManyExceptions m ? new ManyExceptions(Errors + m.Errors) : new ManyExceptions(Seq(this, error)); [Pure] public override IEnumerator GetEnumerator() => Errors.GetEnumerator(); } /// /// Value is bottom /// [Serializable] public class BottomException() : ExceptionalException(Errors.BottomText, ErrorCodes.Bottom) { public static readonly BottomException Default; static BottomException() => Default = new(); public override string Message => Errors.BottomText; public override int Code => ErrorCodes.Bottom; public override Option Inner => default; public override Error ToError() => BottomError.Default; public override ErrorException Combine(ErrorException error) => throw new NotImplementedException(); } public static class ExceptionExtensions { /// /// Throw the error as an exception /// public static Unit Rethrow(this Exception e) { ExceptionDispatchInfo.Capture(e).Throw(); return default; } /// /// Throw the error as an exception /// public static R Rethrow(this Exception e) { ExceptionDispatchInfo.Capture(e).Throw(); return default; } } ================================================ FILE: LanguageExt.Core/Common/Errors.cs ================================================ namespace LanguageExt.Common; public static class Errors { /// /// An error state without any error values /// public static readonly Error None = new ManyErrors(Seq.Empty); /// /// Bottom error text /// public const string BottomText = "In a bottom state and therefore cannot proceed. This can happen when an expression couldn't " + "evaluate to a value. This might be due to filter/where, or sometimes if a `struct` wasn't initialised " + "properly (i.e. via `default(T)` or an uninitialised member)."; /// /// An error that indicates a value from an operation couldn't be evaluated. This is a hard /// fail for systems that depend on expressions to produce results. /// public static readonly Error Bottom = BottomError.Default; /// /// Cancelled error /// public static readonly Error Cancelled = (ErrorCodes.Cancelled, "cancelled"); /// /// Timed-out error /// public static readonly Error TimedOut = (ErrorCodes.TimedOut, "timed out"); /// /// Sequence-empty error /// public static readonly Error SequenceEmpty = (ErrorCodes.SequenceEmpty, "sequence empty"); /// /// Closed error /// public static readonly Error Closed = (ErrorCodes.Closed, "closed"); /// /// Parse error /// public static Error ParseError(string msg) => (ErrorCodes.ParseError, msg); /// /// IO monad not in transformer stack or `MonadIO.LiftIO` not implemented /// public static readonly Error LiftIONotSupported = (ErrorCodes.LiftIONotSupported, "The IO monad is not in the monad-transformer stack or MonadIO.LiftIO has not been implemented in the trait " + "implementation for your monad-type. Therefore it's not possible to leverage `MonadIO` lifting functionality. " + "To resolve this, implement `MonadIO.LiftIO`."); /// /// Transformer stack has no `ToIO` support error /// public static readonly Error ToIONotSupported = (ErrorCodes.ToIONotSupported, "The IO monad is not in the monad-transformer stack or MonadIO.ToIO has not been implemented in the trait " + "implementation for your monad-type. Therefore it's not possible to leverage `MonadIO` unlifting trait " + "functionality. To resolve this, implement `MonadIO.ToIO` and/or `MonadIO.MapIO`."); /// /// Transformer stack has no `Fork` support error /// public static readonly Error ForkNotSupported = (ErrorCodes.ForkIONotSupported, "The IO monad is not in the monad-transformer stack or MonadIO.Fork has not been implemented in the trait " + "implementation for your monad-type."); /// /// Transformer stack has no `Await` support error /// public static readonly Error AwaitNotSupported = (ErrorCodes.ForkIONotSupported, "The IO monad is not in the monad-transformer stack or MonadIO.Await has not been implemented in the trait " + "implementation for your monad-type."); /// /// End-of-stream error /// public static readonly Error EndOfStream = (ErrorCodes.EndOfStream, "end of stream"); /// /// Validation failed error /// public static readonly Error ValidationFailed = (ErrorCodes.ValidationFailed, "validation failed"); /// /// Source completed error /// public static readonly Error SourceCompleted = (ErrorCodes.SourceCompleted, "source completed"); /// /// IO DSL extension error /// public static readonly Error IODSLExtension = new Exceptional( "If you are trying to extend the `IO` type then you must use: `InvokeAsync`, `InvokeSync`, `InvokeAsyncIO`, "+ "`InvokeSyncIO`, or `IOCatch` as the base-type, not `IO`", ErrorCodes.IODSLExtension); /// /// Sink is full error /// public static readonly Error SinkFull = new Expected("Sink is full", ErrorCodes.SinkFull); /// /// Source is closed error /// public static readonly Error SourceClosed = new Expected("Source is closed", ErrorCodes.SourceClosed); } ================================================ FILE: LanguageExt.Core/Common/Exceptions.cs ================================================ namespace LanguageExt.Common; public class Exceptions { /// /// An error state without any error values /// public static readonly ErrorException None = new ManyExceptions([]); /// /// An error that indicates a value from an operation couldn't be evaluated. This is a hard /// fail for systems that depend on expressions to produce results. /// public static readonly ExceptionalException Bottom = new (Errors.Bottom.Message, Errors.Bottom.Code); /// /// Cancelled error /// public static readonly ExpectedException Cancelled = new (Errors.Cancelled.Message, Errors.Cancelled.Code); /// /// Timed-out error /// public static readonly ExpectedException TimedOut = new (Errors.TimedOut.Message, Errors.TimedOut.Code); /// /// Sequence-empty error /// public static readonly ExpectedException SequenceEmpty = new (Errors.SequenceEmpty.Message, Errors.SequenceEmpty.Code); /// /// Closed error /// public static readonly ExpectedException Closed = new (Errors.Closed.Message, Errors.Closed.Code); /// /// Parse error code /// public const int ParseErrorCode = -2000000005; /// /// Parse error /// public static ExpectedException ParseError(string msg) => new (msg, ErrorCodes.ParseError); /// /// IO monad not in transformer stack error /// public static readonly ExceptionalException LiftIONotSupported = new (Errors.LiftIONotSupported.Message, Errors.LiftIONotSupported.Code); /// /// Transformer stack has no unliftIO support error /// public static readonly ExceptionalException UnliftIONotSupported = new (Errors.ToIONotSupported.Message, Errors.ToIONotSupported.Code); /// /// End-of-stream error /// public static readonly ExpectedException EndOfStream = new (Errors.EndOfStream.Message, Errors.EndOfStream.Code); /// /// Validation failed error /// public static readonly ExpectedException ValidationFailed = new (Errors.ValidationFailed.Message, Errors.ValidationFailed.Code); } ================================================ FILE: LanguageExt.Core/Common/README.md ================================================ ## `Error` The `Error` type works like a discriminated-union, it is an `abstract record` type with many sub-cases (which are listed below). It is used extensively with various monadic types, like `Fin`, the _Effect System_ monads of `Eff`, `Eff`, `Aff`, `Aff` and the compositional streaming Pipes features. > The reason they're buried in the `Common` namespace is because, `Error` is a common type name. And so, this gives the programmer a chance to not include it when `using LanguageExt;` `Error` exists because `Exception` is really only meant for _exceptional_ errors. However, in C#-land we've been trained to throw them even for *expected* errors. Instead we use `Error` to represent three key types of error: * `Exceptional` - An unexpected error * `Expected` - An expected error * `ManyErrors` - Many errors (possibly zero) These are the key base-types that indicate the *'flavour'* of the error. For example, a 'user not found' error isn't something exceptional, it's something we expect *might* happen. An `OutOfMemoryException` however, *is* exceptional - it should never happen, and we should treat it as such. Most of the time we want sensible handling of expected errors, and bail out completely for something exceptional. We also want to protect ourselves from information leakage. Leaking exceptional errors via public APIs is a surefire way to open up more information to hackers than you would like. The `Error` derived types all try to protect against this kind of leakage without losing the context of the type of error thrown. Essentially an error is either created from an `Exception` or it isn't. This allows for expected errors to be represented without throwing exceptions, but also it allows for more principled error handling. We can pattern-match on the type, or use some of the built-in properties and methods to inspect the `Error`: * `IsExceptional` - `true` for exceptional errors. For `ManyErrors` this is `true` if _any_ of the errors are exceptional. * `IsExpected` - `true` for non-exceptional/expected errors. For `ManyErrors` this is `true` if _all_ of the errors are expected. * `Is(E exception)` - `true` if the `Error` is exceptional and any of the the internal `Exception` values are of type `E`. * `Is(Error error)` - `true` if the `Error` matches the one provided. i.e. `error.Is(Errors.TimedOut)`. The `Error` type can be constructed to be exceptional or expected. For example, this is an expected error: Error.New("This error was expected") When expected errors are used with codes then equality and matching is done via the code only: Error.New(404, "Page not found"); And this is an exceptional error: try { // This wraps up the exceptional error } catch(Exception e) { return Error.New(e); } Finally, you can collect many errors: Error.Many(Error.New("error one"), Error.New("error two")); Or more simply: Error.New("error one") + Error.New("error two") You can extend the set of error types (perhaps for passing through extra data) by creating a new record inherits `Exceptional` or `Expected`: public record BespokeError(bool MyData) : Expected("Something bespoke", 100, None); By default the properties of the new error-type won't be serialised. So, if you want to pass a payload over the wire, add the `[property: DataMember]` attribute to each member: public record BespokeError([property: DataMember] bool MyData) : Expected("Something bespoke", 100, None); Using this technique it's trivial to create new error-types when additional data needs to be moved around, but also there's a ton of built-in functionality for the most common use-cases. ================================================ FILE: LanguageExt.Core/Concurrency/Async/Async.Module.cs ================================================ using System; using System.Threading; using System.Threading.Tasks; using LanguageExt.Common; namespace LanguageExt.Async; /// /// The `Async` module helps transition away from the `Task` / `async` / `await` world and into one /// where awaiting is the default setting for concurrent programming and branching/forking is the /// thing we do the least. /// /// The `Async.await` function will convert a `Task〈A〉` into an `A` by waiting for the `Task` to /// complete; it will yield the thread whilst it's waiting (to play nice with other tasks in the /// task-pool). This is just like the regular `await` keyword without all the ceremony and /// colouring of methods. /// /// `Async.fork` lifts a function into an IO monad, forks it, and then runs the IO monad returning /// a `ForkIO` object. The forked operation continues to run in parallel. The `ForkIO` object /// contains two properties: `Await` and `Cancel` that be used to either await the result or /// cancel the operation. /// /// These two functions remove the need for methods that are 'tainted' with `Task` or `async` / /// `await` mechanics and assume that the thing we will do the most with asynchronous code is to /// await it. /// /// This module shouldn't be needed too much, as the IO monad is where most of the asynchrony /// should be. But, when converting from existing async/await code, or if you're coming from /// language-ext v4, or earlier, where there was lots of `*Async` methods in the key types, then /// this module will help ease the transition. /// public static class Async { /// /// Simple awaiter that yields the thread whilst waiting. Allows for the `Task` to /// be used with synchronous code without blocking any threads for concurrent /// processing. /// /// Task to await /// Bound value type /// Result of the task, `OperationCanceledException`, or any exception raised by the task /// public static A await(Task operation) { SpinWait sw = default; while (true) { if (operation.IsCanceled) { throw new OperationCanceledException(); } else if (operation.IsFaulted) { operation.Exception.Rethrow(); } else if (operation.IsCompleted) { return operation.Result; } sw.SpinOnce(); } } /// /// `Async.fork` lifts a function into an IO monad, forks it, and then runs the IO monad returning /// the `ForkIO` object. The forked operation continues to run in parallel. The `ForkIO` object /// contains two properties: `Await` and `Cancel` that be used to either await the result or /// cancel the operation. /// /// Operation to fork /// Optional timeout /// Bound value type /// The `ForkIO` object contains two properties: `Await` and `Cancel` that be used to either /// await the result or cancel the operation. public static ForkIO fork(Func operation, TimeSpan timeout = default) => IO.lift(operation).ForkIO(timeout).Run(); /// /// `Async.fork` lifts a function into an IO monad, forks it, and then runs the IO monad returning /// the `ForkIO` object. The forked operation continues to run in parallel. The `ForkIO` object /// contains two properties: `Await` and `Cancel` that be used to either await the result or /// cancel the operation. /// /// Operation to fork /// Optional timeout /// Bound value type /// The `ForkIO` object contains two properties: `Await` and `Cancel` that be used to either /// await the result or cancel the operation. public static ForkIO fork(Func> operation, TimeSpan timeout = default) => IO.liftAsync(operation).ForkIO(timeout).Run(); } ================================================ FILE: LanguageExt.Core/Concurrency/Async/AsyncEnumerable/AsyncEnumberable.Extensions.cs ================================================ using System; using System.Collections.Generic; namespace LanguageExt; public static class AsyncEnumerableExtensions { /* public static StreamT AsStream(this IAsyncEnumerable ma) where M : Monad => StreamT.lift(ma); */ public static async IAsyncEnumerable MapAsync(this IAsyncEnumerable ma, Func f) { await foreach (var a in ma) { yield return f(a); } } public static async IAsyncEnumerable ApplyAsync(this IAsyncEnumerable> ff, IAsyncEnumerable fa) { await foreach (var f in ff) { // ReSharper disable once PossibleMultipleEnumeration await foreach (var a in fa) { yield return f(a); } } } public static async IAsyncEnumerable BindAsync(this IAsyncEnumerable ma, Func> f) { await foreach (var a in ma) { await foreach (var b in f(a)) { yield return b; } } } public static async IAsyncEnumerable FilterAsync( this IAsyncEnumerable ma, Func f) { await foreach (var a in ma) { if(f(a)) yield return a; } } } ================================================ FILE: LanguageExt.Core/Concurrency/Async/AsyncEnumerable/AsyncEnumberable.LINQ.Extensions.cs ================================================ using System; using System.Collections.Generic; using System.Threading.Tasks; using LanguageExt.Traits; namespace LanguageExt.Async.Linq; public static class AsyncEnumerableExtensions { extension(IAsyncEnumerable ma) { public async IAsyncEnumerable Select(Func> f) { await foreach (var a in ma) { yield return await f(a); } } public async IAsyncEnumerable SelectMany(Func> f) { await foreach (var a in ma) { await foreach (var b in f(a)) { yield return b; } } } public async IAsyncEnumerable SelectMany(Func> bind, Func project) { await foreach (var a in ma) { await foreach (var b in bind(a)) { yield return project(a, b); } } } public async IAsyncEnumerable Where(Func f) { await foreach (var a in ma) { if(f(a)) yield return a; } } public async IAsyncEnumerable Skip(int amount) { await foreach (var a in ma) { if (amount == 0) yield return a; else amount--; } } public async IAsyncEnumerable Take(int amount) { await foreach (var a in ma) { if (amount > 0) yield return a; else amount--; } } } } ================================================ FILE: LanguageExt.Core/Concurrency/Atom/Atom.Metadata.cs ================================================ using System; using System.Runtime.CompilerServices; using System.Threading; using static LanguageExt.Prelude; namespace LanguageExt; /// /// Atoms provide a way to manage shared, synchronous, independent state without /// locks. /// /// /// The intended use of atom is to hold an immutable data structure. You change /// the value by applying a function to the old value. This is done in an atomic /// manner by `Swap`. /// /// Internally, `Swap` reads the current value, applies the function to it, and /// attempts to `CompareExchange` it in. Since another thread may have changed the /// value in the intervening time, it may have to retry, and does so in a spin loop. /// /// The net effect is that the value will always be the result of the application /// of the supplied function to a current value, atomically. However, because the /// function might be called multiple times, it must be free of side effects. /// /// Atoms are an efficient way to represent some state that will never need to be /// coordinated with any other, and for which you wish to make synchronous changes. /// /// /// See the [concurrency section](https://github.com/louthy/language-ext/wiki/Concurrency) of the wiki for more info. /// public sealed class Atom { volatile object value; Func validator; readonly M metadata; public event AtomChangedEvent? Change; /// /// Constructor /// [MethodImpl(MethodImplOptions.AggressiveInlining)] Atom(M metadata, A value, Func? validator) { this.value = Box.New(value); this.metadata = metadata; this.validator = validator ?? True; } /// /// Internal constructor function that runs the validator on the value /// before returning the Atom so that the Atom can never be in an invalid /// state. The validator is then used for all state transitions going /// forward. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static Option> New(M metadata, A value, Func validator) { var atom = new Atom(metadata, value, validator ?? throw new ArgumentNullException(nameof(validator))); return validator(value) ? Some(atom) : None; } /// /// Internal constructor /// [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static Atom New(M metadata, A value) => new (metadata, value, True); /// /// Atomically updates the value by passing the old value to `f` and updating /// the atom with the result. Note: `f` may be called multiple times, so it /// should be free of side effects. /// /// Function to update the atom /// /// If the swap operation succeeded then a snapshot of the value that was set is returned. /// If the swap operation fails (which can only happen due to its validator returning false), /// then a snapshot of the current value within the Atom is returned. /// If there is no validator for the Atom then the return value is always the snapshot of /// the successful `f` function. /// public A Swap(Func f) { f = f ?? throw new ArgumentNullException(nameof(f)); SpinWait sw = default; while (true) { var current = value; var currentV = Box.GetValue(current); var newValueA = f(metadata, currentV); var newValue = Box.New(newValueA); if (!validator(newValueA)) { return currentV; } if(Interlocked.CompareExchange(ref value, newValue, current) == current) { Change?.Invoke(newValueA); return newValueA; } sw.SpinOnce(); } } /// /// Atomically updates the value by passing the old value to `f` and updating /// the atom with the result. Note: `f` may be called multiple times, so it /// should be free of side effects. /// /// Function to update the atom /// /// * If `f` returns `None` then no update occurs and the result of the call /// to `Swap` will be the latest (unchanged) value of `A`. /// * If the swap operation fails, due to its validator returning false, then a snapshot of /// the current value within the Atom is returned. /// * If the swap operation succeeded then a snapshot of the value that was set is returned. /// * If there is no validator for the Atom then the return value is always the snapshot of /// the successful `f` function. /// public A Swap(Func> f) { f = f ?? throw new ArgumentNullException(nameof(f)); SpinWait sw = default; while (true) { var current = value; var currentV = Box.GetValue(current); var optionalNewValueA = f(metadata, currentV); if (optionalNewValueA.IsNone) return currentV; var newValueA = (A)optionalNewValueA; var newValue = Box.New(newValueA); if (!validator(newValueA)) { return currentV; } if(Interlocked.CompareExchange(ref value, newValue, current) == current) { Change?.Invoke(newValueA); return newValueA; } sw.SpinOnce(); } } /// /// Atomically updates the value by passing the old value to `f` and updating /// the atom with the result. Note: `f` may be called multiple times, so it /// should be free of side effects. /// /// Additional value to pass to `f` /// Function to update the atom /// /// If the swap operation succeeded then a snapshot of the value that was set is returned. /// If the swap operation fails (which can only happen due to its validator returning false), /// then a snapshot of the current value within the Atom is returned. /// If there is no validator for the Atom then the return value is always the snapshot of /// the successful `f` function. /// public IO SwapIO(Func f) => IO.lift(_ => Swap(f)); /// /// Atomically updates the value by passing the old value to `f` and updating /// the atom with the result. Note: `f` may be called multiple times, so it /// should be free of side effects. /// /// Function to update the atom /// /// * If `f` returns `None` then no update occurs and the result of the call /// to `Swap` will be the latest (unchanged) value of `A`. /// * If the swap operation fails, due to its validator returning false, then a snapshot of /// the current value within the Atom is returned. /// * If the swap operation succeeded then a snapshot of the value that was set is returned. /// * If there is no validator for the Atom then the return value is always the snapshot of /// the successful `f` function. /// public IO SwapIO(Func> f) => IO.lift(_ => Swap(f)); /// /// Value accessor (read and write) /// /// /// /// * Gets will return a freshly constructed `IO` monad that can be repeatedly /// evaluated to get the latest state of the `Atom`. /// /// * Sets pass an `IO` monad that will be mapped to an operation that will set /// the value of the `Atom` each time it's evaluated. /// /// public IO ValueIO { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => IO.lift(_ => Value); set => value.Bind(v => SwapIO((_, _) => v)); } /// /// Current state /// public A Value { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => Box.GetValue(value); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public override string ToString() => Value?.ToString() ?? "[null]"; /// /// Implicit conversion to `A` /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator A(Atom atom) => atom.Value; /// /// Helper for validator /// /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] static bool True(A _) => true; } ================================================ FILE: LanguageExt.Core/Concurrency/Atom/Atom.cs ================================================ using System; using System.Runtime.CompilerServices; using System.Threading; using static LanguageExt.Prelude; namespace LanguageExt; /// /// Atoms provide a way to manage shared, synchronous, independent state without /// locks. /// /// /// The intended use of atom is to hold an immutable data structure. You change /// the value by applying a function to the old value. This is done in an atomic /// manner by `Swap`. /// /// Internally, `Swap` reads the current value, applies the function to it, and /// attempts to `CompareExchange` it in. Since another thread may have changed the /// value in the intervening time, it may have to retry, and does so in a spin loop. /// /// The net effect is that the value will always be the result of the application /// of the supplied function to a current value, atomically. However, because the /// function might be called multiple times, it must be free of side effects. /// /// Atoms are an efficient way to represent some state that will never need to be /// coordinated with any other, and for which you wish to make synchronous changes. /// /// /// See the [concurrency section](https://github.com/louthy/language-ext/wiki/Concurrency) of the wiki for more info. /// public sealed class Atom { volatile object value; readonly Func validator; public event AtomChangedEvent? Change; /// /// Constructor /// [MethodImpl(MethodImplOptions.AggressiveInlining)] Atom(A value, Func? validator) { this.value = Box.New(value); this.validator = validator ?? True; } /// /// Internal constructor function that runs the validator on the value /// before returning the Atom so that the Atom can never be in an invalid /// state. The validator is then used for all state transitions going /// forward. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static Option> New(A value, Func validator) { var atom = new Atom(value, validator ?? throw new ArgumentNullException(nameof(validator))); return validator(value) ? Some(atom) : None; } /// /// Internal constructor /// [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static Atom New(A value) => new (value, True); /// /// Atomically updates the value by passing the old value to `f` and updating /// the atom with the result. Note: `f` may be called multiple times, so it /// should be free of side effects. /// /// Function to update the atom /// /// If the swap operation succeeded then a snapshot of the value that was set is returned. /// If the swap operation fails (which can only happen due to its validator returning false), /// then a snapshot of the current value within the Atom is returned. /// If there is no validator for the Atom then the return value is always the snapshot of /// the successful `f` function. /// public A Swap(Func f) { f = f ?? throw new ArgumentNullException(nameof(f)); SpinWait sw = default; while (true) { var current = value; var currentV = Box.GetValue(current); var newValueA = f(currentV); var newValue = Box.New(newValueA); if (!validator(newValueA)) { return currentV; } if(Interlocked.CompareExchange(ref value, newValue, current) == current) { Change?.Invoke(newValueA); return newValueA; } sw.SpinOnce(); } } /// /// Atomically updates the value by passing the old value to `f` and updating /// the atom with the result. Note: `f` may be called multiple times, so it /// should be free of side effects. /// /// Function to update the atom /// /// * If `f` returns `None` then no update occurs and the result of the call /// to `Swap` will be the latest (unchanged) value of `A`. /// * If the swap operation fails, due to its validator returning false, then a snapshot of /// the current value within the Atom is returned. /// * If the swap operation succeeded then a snapshot of the value that was set is returned. /// * If there is no validator for the Atom then the return value is always the snapshot of /// the successful `f` function. /// public A SwapMaybe(Func> f) { f = f ?? throw new ArgumentNullException(nameof(f)); SpinWait sw = default; while (true) { var current = value; var currentV = Box.GetValue(current); var optionalNewValueA = f(currentV); if (optionalNewValueA.IsNone) return currentV; var newValueA = (A)optionalNewValueA; var newValue = Box.New(newValueA); if (!validator(newValueA)) { return currentV; } if(Interlocked.CompareExchange(ref value, newValue, current) == current) { Change?.Invoke(newValueA); return newValueA; } sw.SpinOnce(); } } /// /// Atomically updates the value by passing the old value to `f` and updating /// the atom with the result. Note: `f` may be called multiple times, so it /// should be free of side effects. /// /// Function to update the atom /// /// If the swap operation succeeded then a snapshot of the value that was set is returned. /// If the swap operation fails (which can only happen due to its validator returning false), /// then a snapshot of the current value within the Atom is returned. /// If there is no validator for the Atom then the return value is always the snapshot of /// the successful `f` function. /// public IO SwapIO(Func f) => IO.lift(_ => Swap(f)); /// /// Atomically updates the value by passing the old value to `f` and updating /// the atom with the result. Note: `f` may be called multiple times, so it /// should be free of side effects. /// /// Function to update the atom /// /// * If `f` returns `None` then no update occurs and the result of the call /// to `Swap` will be the latest (unchanged) value of `A`. /// * If the swap operation fails, due to its validator returning false, then a snapshot of /// the current value within the Atom is returned. /// * If the swap operation succeeded then a snapshot of the value that was set is returned. /// * If there is no validator for the Atom then the return value is always the snapshot of /// the successful `f` function. /// public IO SwapMaybeIO(Func> f) => IO.lift(_ => SwapMaybe(f)); /// /// Value accessor (read and write) /// /// /// /// * Gets will return a freshly constructed `IO` monad that can be repeatedly /// evaluated to get the latest state of the `Atom`. /// /// * Sets pass an `IO` monad that will be mapped to an operation that will set /// the value of the `Atom` each time it's evaluated. /// /// public IO ValueIO { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => IO.lift(_ => Value); set => value.Bind(v => SwapIO(_ => v)); } /// /// Current state /// public A Value { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => Box.GetValue(value); } /// /// Implicit conversion to `A` /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator A(Atom atom) => atom.Value; /// /// Helper for validator /// /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] static bool True(A _) => true; [MethodImpl(MethodImplOptions.AggressiveInlining)] public override string ToString() => Value?.ToString() ?? "[null]"; } ================================================ FILE: LanguageExt.Core/Concurrency/Atom/AtomChangedEvent.cs ================================================ namespace LanguageExt; /// /// Announces Atom change events /// /// /// See the [concurrency section](https://github.com/louthy/language-ext/wiki/Concurrency) of the wiki for more info. /// public delegate void AtomChangedEvent(A value); ================================================ FILE: LanguageExt.Core/Concurrency/Atom/README.md ================================================ Atoms provide a way to manage shared, synchronous, independent state without locks. You can use them to wrap up immutable data structures and then atomically update them using the various `Swap` methods, or read them by calling the `Value` property. If a conflict is encountered during a `Swap` operation, the operation is re-run using the latest state, and so you should minimise the time spent in the `Swap` function, as well as make sure there are no side-effects, otherwise all bets are off. See the [concurrency section](https://github.com/louthy/language-ext/wiki/Concurrency) of the wiki for more info. ### Usage record Person(string Name, string Surname); // Create a new atom var person = Atom(new Person("Paul", "Louth")); // Modify it atomically person.Swap(p => p with { Surname = $"{p.Name}y" }); // Take a snapshot of the state of the atom var snapshot = p.Value; ================================================ FILE: LanguageExt.Core/Concurrency/AtomHashMap/AtomHashMap.Eq.cs ================================================ using System; using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics.Contracts; using System.Linq; using System.Runtime.CompilerServices; using System.Threading; using LanguageExt.ClassInstances; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; /// /// Atoms provide a way to manage shared, synchronous, independent state without /// locks. `AtomHashMap` wraps the language-ext `HashMap`, and makes sure all operations are atomic and thread-safe /// without resorting to locking /// /// /// See the [concurrency section](https://github.com/louthy/language-ext/wiki/Concurrency) of the wiki for more info. /// public class AtomHashMap : IEnumerable<(K Key, V Value)>, IEquatable>, IEquatable>, IReadOnlyDictionary where EqK : Eq { internal volatile TrieMap Items; public event AtomHashMapChangeEvent? Change; /// /// Creates a new atom-hashmap /// public static AtomHashMap Empty => new AtomHashMap(TrieMap.Empty); /// /// Constructor /// /// Trie map AtomHashMap(TrieMap items) => this.Items = items; /// /// Constructor /// /// Hash map internal AtomHashMap(HashMap items) => this.Items = items.Value; /// /// 'this' accessor /// /// Key /// Optional value [Pure] public V this[K key] => Items[key]; /// /// Is the map empty /// [Pure] public bool IsEmpty { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => Items.IsEmpty; } /// /// Number of items in the map /// [Pure] public int Count { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => Items.Count; } /// /// Alias of Count /// [Pure] public int Length { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => Items.Count; } /// /// Atomically swap the underlying hash-map. Allows for multiple operations on the hash-map in an entirely /// transactional and atomic way. /// /// Swap function, maps the current state of the AtomHashMap to a new state /// Any functions passed as arguments may be run multiple times if there are multiple threads competing /// to update this data structure. Therefore the functions must spend as little time performing the injected /// behaviours as possible to avoid repeated attempts /// /// NOTE: The change-tracking only works if you transform the `TrackingHashMap` provided to the `Func`. If you /// build a fresh one then it won't have any tracking. You can call `map.Clear()` to get to an empty /// map, which will also track the removals. /// public Unit Swap(Func, TrackingHashMap> swap) { SpinWait sw = default; while (true) { var oitems = Items; var nitems = swap(new TrackingHashMap(oitems)); if(ReferenceEquals(oitems, nitems.Value)) { // no change return default; } if (ReferenceEquals(Interlocked.CompareExchange(ref Items, nitems.Value, oitems), oitems)) { AnnounceChanges(oitems, nitems.Value, nitems.Changes.Value); return default; } else { sw.SpinOnce(); } } } /// /// Internal version of `Swap` that doesn't do any change tracking /// internal Unit SwapInternal(Func, TrieMap> swap) { SpinWait sw = default; while (true) { var oitems = Items; var nitems = swap(oitems); if(ReferenceEquals(oitems, nitems)) { // no change return default; } if (ReferenceEquals(Interlocked.CompareExchange(ref Items, nitems, oitems), oitems)) { return default; } else { sw.SpinOnce(); } } } /// /// Atomically swap a key in the hash-map, if it exists. If it doesn't exist, nothing changes. /// Allows for multiple operations on the hash-map value in an entirely transactional and atomic way. /// /// Swap function, maps the current state of a value in the AtomHashMap to a new value /// Any functions passed as arguments may be run multiple times if there are multiple threads competing /// to update this data structure. Therefore the functions must spend as little time performing the injected /// behaviours as possible to avoid repeated attempts public Unit SwapKey(K key, Func swap) { SpinWait sw = default; while (true) { var oitems = Items; var ovalue = oitems.Find(key); if (ovalue.IsNone) return unit; var nvalue = swap((V)ovalue); var onChange = Change; var (nitems, change) = onChange == null ? (oitems.SetItem(key, nvalue), Change.None) : oitems.SetItemWithLog(key, nvalue); if (ReferenceEquals(Interlocked.CompareExchange(ref Items, nitems, oitems), oitems)) { AnnounceChange(oitems, nitems, key, change); return default; } else { sw.SpinOnce(); } } } /// /// Atomically swap a key in the hash-map: /// /// * If the key doesn't exist, then `None` is passed to `swap` /// * If the key does exist, then `Some(x)` is passed to `swap` /// /// The operation performed on the hash-map depends on the value going in and out of `swap`: /// /// In | Out | Operation /// --------- | --------- | --------------- /// `Some(x)` | `Some(y)` | `SetItem(key, y)` /// `Some(x)` | `None` | `Remove(key)` /// `None` | `Some(y)` | `Add(key, y)` /// `None` | `None` | `no-op` /// /// Allows for multiple operations on the hash-map value in an entirely transactional and atomic way. /// /// Swap function, maps the current state of a value in the AtomHashMap to a new value /// Any functions passed as arguments may be run multiple times if there are multiple threads competing /// to update this data structure. Therefore the functions must spend as little time performing the injected /// behaviours as possible to avoid repeated attempts public Unit SwapKey(K key, Func, Option> swap) { SpinWait sw = default; while (true) { var oitems = Items; var ovalue = oitems.Find(key); var nvalue = swap(ovalue); var onChange = Change; var (nitems, change) = onChange == null ? (ovalue.IsSome, nvalue.IsSome) switch { (true, true) => (oitems.SetItem(key, (V)nvalue), Change.None), (true, false) => (oitems.Remove(key), Change.None), (false, true) => (oitems.Add(key, (V)nvalue), Change.None), (false, false) => (oitems, Change.None) } : (ovalue.IsSome, nvalue.IsSome) switch { (true, true) => oitems.SetItemWithLog(key, (V)nvalue), (true, false) => oitems.RemoveWithLog(key), (false, true) => oitems.AddWithLog(key, (V)nvalue), (false, false) => (oitems, Change.None) }; if(ReferenceEquals(oitems, nitems)) { // no change return default; } if (ReferenceEquals(Interlocked.CompareExchange(ref Items, nitems, oitems), oitems)) { AnnounceChange(oitems, nitems, key, change); return default; } else { sw.SpinOnce(); } } } /// /// Atomically filter out items that return false when a predicate is applied /// /// Predicate /// New map with items filtered [Pure] public AtomHashMap Filter(Func pred) => new(Items.Filter(pred)); /// /// Atomically filter out items that return false when a predicate is applied /// /// Predicate /// New map with items filtered /// Any functions passed as arguments may be run multiple times if there are multiple threads competing /// to update this data structure. Therefore the functions must spend as little time performing the injected /// behaviours as possible to avoid repeated attempts public Unit FilterInPlace(Func pred) { SpinWait sw = default; while (true) { var oitems = Items; var onChange = Change; var (nitems, changes) = onChange == null ? (oitems.Filter(pred), null) : oitems.FilterWithLog(pred); if (ReferenceEquals(Interlocked.CompareExchange(ref Items, nitems, oitems), oitems)) { AnnounceChanges(oitems, nitems, changes); return default; } else { sw.SpinOnce(); } } } /// /// Atomically filter out items that return false when a predicate is applied /// /// Predicate [Pure] public AtomHashMap Filter(Func pred) => new(Items.Filter(pred)); /// /// Atomically filter out items that return false when a predicate is applied /// /// Predicate /// Any functions passed as arguments may be run multiple times if there are multiple threads competing /// to update this data structure. Therefore the functions must spend as little time performing the injected /// behaviours as possible to avoid repeated attempts public Unit FilterInPlace(Func pred) { SpinWait sw = default; while (true) { var oitems = Items; var onChange = Change; var (nitems, changes) = onChange == null ? (oitems.Filter(pred), null) : oitems.FilterWithLog(pred); if (ReferenceEquals(Interlocked.CompareExchange(ref Items, nitems, oitems), oitems)) { AnnounceChanges(oitems, nitems, changes); return default; } else { sw.SpinOnce(); } } } /// /// Atomically maps the map to a new map /// /// Mapped items in a new map /// Any functions passed as arguments may be run multiple times if there are multiple threads competing /// to update this data structure. Therefore the functions must spend as little time performing the injected /// behaviours as possible to avoid repeated attempts public Unit MapInPlace(Func f) { SpinWait sw = default; while (true) { var oitems = Items; var onChange = Change; var (nitems, changes) = onChange == null ? (oitems.Map(f), null) : oitems.MapWithLog(f); if (ReferenceEquals(Interlocked.CompareExchange(ref Items, nitems, oitems), oitems)) { AnnounceChanges(oitems, nitems, changes); return default; } else { sw.SpinOnce(); } } } /// /// Atomically maps the map to a new map /// /// Mapped items in a new map public AtomHashMap Map(Func f) => new (Items.Map(f)); /// /// Atomically adds a new item to the map /// /// Null is not allowed for a Key or a Value /// Key /// Value /// Throws ArgumentException if the key already exists /// Throws ArgumentNullException the key or value are null public Unit Add(K key, V value) { SpinWait sw = default; while (true) { var oitems = Items; var onChange = Change; var (nitems, change) = onChange == null ? (oitems.Add(key, value), null) : oitems.AddWithLog(key, value); if (ReferenceEquals(Interlocked.CompareExchange(ref Items, nitems, oitems), oitems)) { AnnounceChange(oitems, nitems, key, change); return default; } else { sw.SpinOnce(); } } } /// /// Atomically adds a new item to the map. /// If the key already exists, then the new item is ignored /// /// Null is not allowed for a Key or a Value /// Key /// Value /// Throws ArgumentNullException the key or value are null public Unit TryAdd(K key, V value) { SpinWait sw = default; while (true) { var oitems = Items; var onChange = Change; var (nitems, change) = onChange == null ? (oitems.TryAdd(key, value), null) : oitems.TryAddWithLog(key, value); if(ReferenceEquals(oitems, nitems)) { return default; } if (ReferenceEquals(Interlocked.CompareExchange(ref Items, nitems, oitems), oitems)) { AnnounceChange(oitems, nitems, key, change); return default; } else { sw.SpinOnce(); } } } /// /// Atomically adds a new item to the map. /// If the key already exists, the new item replaces it. /// /// Null is not allowed for a Key or a Value /// Key /// Value /// Throws ArgumentNullException the key or value are null public Unit AddOrUpdate(K key, V value) { SpinWait sw = default; while (true) { var oitems = Items; var onChange = Change; var (nitems, change) = onChange == null ? (oitems.AddOrUpdate(key, value), null) : oitems.AddOrUpdateWithLog(key, value); if (ReferenceEquals(Interlocked.CompareExchange(ref Items, nitems, oitems), oitems)) { AnnounceChange(oitems, nitems, key, change); return default; } else { sw.SpinOnce(); } } } /// /// Retrieve a value from the map by key, map it to a new value, /// put it back. If it doesn't exist, add a new one based on None result. /// /// Key to find /// Throws Exception if None returns null /// Throws Exception if Some returns null /// Any functions passed as arguments may be run multiple times if there are multiple threads competing /// to update this data structure. Therefore the functions must spend as little time performing the injected /// behaviours as possible to avoid repeated attempts public Unit AddOrUpdate(K key, Func Some, Func None) { SpinWait sw = default; while (true) { var oitems = Items; var onChange = Change; var (nitems, change) = onChange == null ? (oitems.AddOrUpdate(key, Some, None), null) : oitems.AddOrUpdateWithLog(key, Some, None); if (ReferenceEquals(Interlocked.CompareExchange(ref Items, nitems, oitems), oitems)) { AnnounceChange(oitems, nitems, key, change); return default; } else { sw.SpinOnce(); } } } /// /// Retrieve a value from the map by key, map it to a new value, /// put it back. If it doesn't exist, add a new one based on None result. /// /// Key to find /// Throws ArgumentNullException if None is null /// Throws Exception if Some returns null /// Any functions passed as arguments may be run multiple times if there are multiple threads competing /// to update this data structure. Therefore the functions must spend as little time performing the injected /// behaviours as possible to avoid repeated attempts public Unit AddOrUpdate(K key, Func Some, V None) { SpinWait sw = default; while (true) { var oitems = Items; var onChange = Change; var (nitems, change) = onChange == null ? (oitems.AddOrUpdate(key, Some, None), null) : oitems.AddOrUpdateWithLog(key, Some, None); if (ReferenceEquals(Interlocked.CompareExchange(ref Items, nitems, oitems), oitems)) { AnnounceChange(oitems, nitems, key, change); return default; } else { sw.SpinOnce(); } } } /// /// Atomically adds a range of items to the map. /// /// Null is not allowed for a Key or a Value /// Range of tuples to add /// Throws ArgumentException if any of the keys already exist /// Throws ArgumentNullException the keys or values are null public Unit AddRange(IEnumerable<(K Key, V Value)> range) { SpinWait sw = default; var srange = toSeq(range); if (srange.IsEmpty) return default; while (true) { var oitems = Items; var onChange = Change; var (nitems, changes) = onChange == null ? (oitems.AddRange(srange), null) : oitems.AddRangeWithLog(srange); if (ReferenceEquals(Interlocked.CompareExchange(ref Items, nitems, oitems), oitems)) { AnnounceChanges(oitems, nitems, changes); return default; } else { sw.SpinOnce(); } } } /// /// Atomically adds a range of items to the map. If any of the keys exist already /// then they're ignored. /// /// Null is not allowed for a Key or a Value /// Range of tuples to add /// Throws ArgumentNullException the keys or values are null public Unit TryAddRange(IEnumerable<(K Key, V Value)> range) { SpinWait sw = default; var srange = toSeq(range); if (srange.IsEmpty) return default; while (true) { var oitems = Items; var onChange = Change; var (nitems, changes) = onChange == null ? (oitems.TryAddRange(srange), null) : oitems.TryAddRangeWithLog(srange); if(ReferenceEquals(oitems, nitems)) { return default; } if (ReferenceEquals(Interlocked.CompareExchange(ref Items, nitems, oitems), oitems)) { AnnounceChanges(oitems, nitems, changes); return default; } else { sw.SpinOnce(); } } } /// /// Atomically adds a range of items to the map. If any of the keys exist already /// then they're ignored. /// /// Null is not allowed for a Key or a Value /// Range of KeyValuePairs to add /// Throws ArgumentNullException the keys or values are null public Unit TryAddRange(IEnumerable> range) { SpinWait sw = default; var srange = toSeq(range); if (srange.IsEmpty) return default; while (true) { var oitems = Items; var onChange = Change; var (nitems, changes) = onChange == null ? (oitems.TryAddRange(srange), null) : oitems.TryAddRangeWithLog(srange); if(ReferenceEquals(oitems, nitems)) { return default; } if (ReferenceEquals(Interlocked.CompareExchange(ref Items, nitems, oitems), oitems)) { AnnounceChanges(oitems, nitems, changes); return default; } else { sw.SpinOnce(); } } } /// /// Atomically adds a range of items to the map. If any of the keys exist already /// then they're replaced. /// /// Null is not allowed for a Key or a Value /// Range of tuples to add /// Throws ArgumentNullException the keys or values are null /// New Map with the items added public Unit AddOrUpdateRange(IEnumerable<(K Key, V Value)> range) { SpinWait sw = default; var srange = toSeq(range); if (srange.IsEmpty) return default; while (true) { var oitems = Items; var onChange = Change; var (nitems, changes) = onChange == null ? (oitems.AddOrUpdateRange(srange), null) : oitems.AddOrUpdateRangeWithLog(srange); if (ReferenceEquals(Interlocked.CompareExchange(ref Items, nitems, oitems), oitems)) { AnnounceChanges(oitems, nitems, changes); return default; } else { sw.SpinOnce(); } } } /// /// Atomically adds a range of items to the map. If any of the keys exist already /// then they're replaced. /// /// Null is not allowed for a Key or a Value /// Range of KeyValuePairs to add /// Throws ArgumentNullException the keys or values are null public Unit AddOrUpdateRange(IEnumerable> range) { SpinWait sw = default; var srange = toSeq(range); if (srange.IsEmpty) return default; while (true) { var oitems = Items; var onChange = Change; var (nitems, changes) = onChange == null ? (oitems.AddOrUpdateRange(srange), null) : oitems.AddOrUpdateRangeWithLog(srange); if (ReferenceEquals(Interlocked.CompareExchange(ref Items, nitems, oitems), oitems)) { AnnounceChanges(oitems, nitems, changes); return default; } else { sw.SpinOnce(); } } } /// /// Atomically removes an item from the map /// If the key doesn't exists, the request is ignored. /// /// Key public Unit Remove(K key) { SpinWait sw = default; while (true) { var oitems = Items; var onChange = Change; var (nitems, change) = onChange == null ? (oitems.Remove(key), null) : oitems.RemoveWithLog(key); if(ReferenceEquals(oitems, nitems)) { return default; } if (ReferenceEquals(Interlocked.CompareExchange(ref Items, nitems, oitems), oitems)) { AnnounceChange(oitems, nitems, key, change); return default; } else { sw.SpinOnce(); } } } /// /// Retrieve a value from the map by key /// /// Key to find /// Found value [Pure] public Option Find(K value) => Items.Find(value); /// /// Retrieve a value from the map by key as an enumerable /// /// Key to find /// Found value [Pure] public Seq FindSeq(K key) => Items.FindAll(key); /// /// Retrieve a value from the map by key and pattern match the /// result. /// /// Key to find /// Found value [Pure] public R Find(K key, Func Some, Func None) => Items.Find(key, Some, None); /// /// Try to find the key in the map, if it doesn't exist, add a new /// item by invoking the delegate provided. /// /// Key to find /// Delegate to get the value /// Added value /// Any functions passed as arguments may be run multiple times if there are multiple threads competing /// to update this data structure. Therefore the functions must spend as little time performing the injected /// behaviours as possible to avoid repeated attempts public V FindOrAdd(K key, Func None) { SpinWait sw = default; while (true) { var oitems = Items; var (nitems, value, change) = Items.FindOrAddWithLog(key, None); if(ReferenceEquals(oitems, nitems)) { return value; } if (ReferenceEquals(Interlocked.CompareExchange(ref Items, nitems, oitems), oitems)) { AnnounceChange(oitems, nitems, key, change); return value; } else { sw.SpinOnce(); } } } /// /// Try to find the key in the map, if it doesn't exist, add a new /// item provided. /// /// Key to find /// Delegate to get the value /// Added value public V FindOrAdd(K key, V value) { SpinWait sw = default; while (true) { var oitems = Items; var (nitems, nvalue, change) = Items.FindOrAddWithLog(key, value); if(ReferenceEquals(oitems, nitems)) { return nvalue; } if (ReferenceEquals(Interlocked.CompareExchange(ref Items, nitems, oitems), oitems)) { AnnounceChange(oitems, nitems, key, change); return nvalue; } else { sw.SpinOnce(); } } } /// /// Try to find the key in the map, if it doesn't exist, add a new /// item by invoking the delegate provided. /// /// Key to find /// Delegate to get the value /// Added value /// Any functions passed as arguments may be run multiple times if there are multiple threads competing /// to update this data structure. Therefore the functions must spend as little time performing the injected /// behaviours as possible to avoid repeated attempts public Option FindOrMaybeAdd(K key, Func> None) { SpinWait sw = default; while (true) { var oitems = Items; var (nitems, nvalue, change) = Items.FindOrMaybeAddWithLog(key, None); if(ReferenceEquals(oitems, nitems)) { return nvalue; } if (ReferenceEquals(Interlocked.CompareExchange(ref Items, nitems, oitems), oitems)) { AnnounceChange(oitems, nitems, key, change); return nvalue; } else { sw.SpinOnce(); } } } /// /// Try to find the key in the map, if it doesn't exist, add a new /// item by invoking the delegate provided. /// /// Key to find /// Delegate to get the value /// Added value public Option FindOrMaybeAdd(K key, Option None) { SpinWait sw = default; while (true) { var oitems = Items; var (nitems, nvalue, change) = Items.FindOrMaybeAddWithLog(key, None); if(ReferenceEquals(oitems, nitems)) { return nvalue; } if (ReferenceEquals(Interlocked.CompareExchange(ref Items, nitems, oitems), oitems)) { AnnounceChange(oitems, nitems, key, change); return nvalue; } else { sw.SpinOnce(); } } } /// /// Atomically updates an existing item /// /// Null is not allowed for a Key or a Value /// Key /// Value /// Throws ArgumentNullException the key or value are null public Unit SetItem(K key, V value) { SpinWait sw = default; while (true) { var oitems = Items; var onChange = Change; var (nitems, change) = onChange == null ? (oitems.SetItem(key, value), null) : oitems.SetItemWithLog(key, value); if (ReferenceEquals(Interlocked.CompareExchange(ref Items, nitems, oitems), oitems)) { AnnounceChange(oitems, nitems, key, change); return default; } else { sw.SpinOnce(); } } } /// /// Retrieve a value from the map by key, map it to a new value, /// put it back. /// /// Key to set /// Throws ArgumentException if the item isn't found /// Throws Exception if Some returns null /// Any functions passed as arguments may be run multiple times if there are multiple threads competing /// to update this data structure. Therefore the functions must spend as little time performing the injected /// behaviours as possible to avoid repeated attempts public Unit SetItem(K key, Func Some) { SpinWait sw = default; while (true) { var oitems = Items; var onChange = Change; var(nitems, change) = onChange == null ? (oitems.SetItem(key, Some), null) : oitems.SetItemWithLog(key, Some); if (ReferenceEquals(Interlocked.CompareExchange(ref Items, nitems, oitems), oitems)) { AnnounceChange(oitems, nitems, key, change); return default; } else { sw.SpinOnce(); } } } /// /// Atomically updates an existing item, unless it doesn't exist, in which case /// it is ignored /// /// Null is not allowed for a Key or a Value /// Key /// Value /// Throws ArgumentNullException the value is null public Unit TrySetItem(K key, V value) { SpinWait sw = default; while (true) { var oitems = Items; var onChange = Change; var (nitems, change) = onChange == null ? (oitems.TrySetItem(key, value), null) : oitems.TrySetItemWithLog(key, value); if(ReferenceEquals(oitems, nitems)) { return default; } if (ReferenceEquals(Interlocked.CompareExchange(ref Items, nitems, oitems), oitems)) { AnnounceChange(oitems, nitems, key, change); return default; } else { sw.SpinOnce(); } } } /// /// Atomically sets an item by first retrieving it, applying a map, and then putting it back. /// Silently fails if the value doesn't exist /// /// Key to set /// delegate to map the existing value to a new one before setting /// Throws Exception if Some returns null /// Throws ArgumentNullException the key or value are null /// Any functions passed as arguments may be run multiple times if there are multiple threads competing /// to update this data structure. Therefore the functions must spend as little time performing the injected /// behaviours as possible to avoid repeated attempts public Unit TrySetItem(K key, Func Some) { SpinWait sw = default; while (true) { var oitems = Items; var onChange = Change; var (nitems, change) = onChange == null ? (oitems.TrySetItem(key, Some), null) : oitems.TrySetItemWithLog(key, Some); if(ReferenceEquals(oitems, nitems)) { return default; } if (ReferenceEquals(Interlocked.CompareExchange(ref Items, nitems, oitems), oitems)) { AnnounceChange(oitems, nitems, key, change); return default; } else { sw.SpinOnce(); } } } /// /// Checks for existence of a key in the map /// /// Key to check /// True if an item with the key supplied is in the map [Pure] public bool ContainsKey(K key) => Items.ContainsKey(key); /// /// Checks for existence of a key in the map /// /// Key to check /// True if an item with the key supplied is in the map [Pure] public bool Contains(K key, V value) => Items.Contains(key, value); /// /// Checks for existence of a value in the map /// /// Value to check /// True if an item with the value supplied is in the map [Pure] public bool Contains(V value) => Items.Contains(value); /// /// Checks for existence of a value in the map /// /// Value to check /// True if an item with the value supplied is in the map [Pure] public bool Contains(V value) where EqV : Eq => Items.Contains(value); /// /// Checks for existence of a key in the map /// /// Key to check /// True if an item with the key supplied is in the map [Pure] public bool Contains(K key, V value) where EqV : Eq => Items.Contains(key, value); /// /// Clears all items from the map /// public Unit Clear() { SpinWait sw = default; while (true) { var oitems = Items; var onChange = Change; var (nitems, changes) = onChange == null ? (oitems.Clear(), null) : oitems.ClearWithLog(); if (ReferenceEquals(Interlocked.CompareExchange(ref Items, nitems, oitems), oitems)) { AnnounceChanges(oitems, nitems, changes); return default; } else { sw.SpinOnce(); } } } /// /// Atomically adds a range of items to the map /// /// Range of KeyValuePairs to add /// Throws ArgumentException if any of the keys already exist public Unit AddRange(IEnumerable> pairs) { SpinWait sw = default; var spairs = toSeq(pairs); if (spairs.IsEmpty) return default; while (true) { var oitems = Items; var onChange = Change; var (nitems, changes) = onChange == null ? (oitems.AddRange(spairs), null) : oitems.AddRangeWithLog(spairs); if (ReferenceEquals(Interlocked.CompareExchange(ref Items, nitems, oitems), oitems)) { AnnounceChanges(oitems, nitems, changes); return default; } else { sw.SpinOnce(); } } } /// /// Atomically sets a series of items using the KeyValuePairs provided /// /// Items to set /// Throws ArgumentException if any of the keys aren't in the map public Unit SetItems(IEnumerable> items) { SpinWait sw = default; var sitems = toSeq(items); if (sitems.IsEmpty) return default; while (true) { var oitems = this.Items; var onChange = Change; var (nitems, changes) = onChange == null ? (oitems.SetItems(sitems), null) : oitems.SetItemsWithLog(sitems); if (ReferenceEquals(Interlocked.CompareExchange(ref this.Items, nitems, oitems), oitems)) { AnnounceChanges(oitems, nitems, changes); return default; } else { sw.SpinOnce(); } } } /// /// Atomically sets a series of items using the Tuples provided. /// /// Items to set /// Throws ArgumentException if any of the keys aren't in the map public Unit SetItems(IEnumerable<(K Key, V Value)> items) { SpinWait sw = default; var sitems = toSeq(items); if (sitems.IsEmpty) return default; while (true) { var oitems = this.Items; var onChange = Change; var (nitems, changes) = onChange == null ? (oitems.SetItems(sitems), null) : oitems.SetItemsWithLog(sitems); if (ReferenceEquals(Interlocked.CompareExchange(ref this.Items, nitems, oitems), oitems)) { AnnounceChanges(oitems, nitems, changes); return default; } else { sw.SpinOnce(); } } } /// /// Atomically sets a series of items using the KeyValuePairs provided. If any of the /// items don't exist then they're silently ignored. /// /// Items to set public Unit TrySetItems(IEnumerable> items) { SpinWait sw = default; var sitems = toSeq(items); if (sitems.IsEmpty) return default; while (true) { var oitems = this.Items; var onChange = Change; var (nitems, changes) = onChange == null ? (oitems.TrySetItems(sitems), null) : oitems.TrySetItemsWithLog(sitems); if (ReferenceEquals(oitems, nitems)) { return default; } if (ReferenceEquals(Interlocked.CompareExchange(ref this.Items, nitems, oitems), oitems)) { AnnounceChanges(oitems, nitems, changes); return default; } else { sw.SpinOnce(); } } } /// /// Atomically sets a series of items using the Tuples provided If any of the /// items don't exist then they're silently ignored. /// /// Items to set public Unit TrySetItems(IEnumerable<(K Key, V Value)> items) { SpinWait sw = default; var sitems = toSeq(items); if (sitems.IsEmpty) return default; while (true) { var oitems = this.Items; var onChange = Change; var (nitems, changes) = onChange == null ? (oitems.TrySetItems(sitems), null) : oitems.TrySetItemsWithLog(sitems); if (ReferenceEquals(oitems, nitems)) { return default; } if (ReferenceEquals(Interlocked.CompareExchange(ref this.Items, nitems, oitems), oitems)) { AnnounceChanges(oitems, nitems, changes); return default; } else { sw.SpinOnce(); } } } /// /// Atomically sets a series of items using the keys provided to find the items /// and the Some delegate maps to a new value. If the items don't exist then /// they're silently ignored. /// /// Keys of items to set /// Function map the existing item to a new one /// Any functions passed as arguments may be run multiple times if there are multiple threads competing /// to update this data structure. Therefore the functions must spend as little time performing the injected /// behaviours as possible to avoid repeated attempts public Unit TrySetItems(IEnumerable keys, Func Some) { SpinWait sw = default; var skeys = toSeq(keys); if (skeys.IsEmpty) return default; while (true) { var oitems = this.Items; var onChange = Change; var (nitems, changes) = onChange == null ? (oitems.TrySetItems(skeys, Some), null) : oitems.TrySetItemsWithLog(skeys, Some); if (ReferenceEquals(oitems, nitems)) { return default; } if (ReferenceEquals(Interlocked.CompareExchange(ref this.Items, nitems, oitems), oitems)) { AnnounceChanges(oitems, nitems, changes); return default; } else { sw.SpinOnce(); } } } /// /// Atomically removes a set of keys from the map /// /// Keys to remove public Unit RemoveRange(IEnumerable keys) { SpinWait sw = default; var skeys = toSeq(keys); if (skeys.IsEmpty) return default; while (true) { var oitems = this.Items; var onChange = Change; var (nitems, changes) = onChange == null ? (oitems.RemoveRange(skeys), null) : oitems.RemoveRangeWithLog(skeys); if (ReferenceEquals(oitems, nitems)) { return default; } if (ReferenceEquals(Interlocked.CompareExchange(ref this.Items, nitems, oitems), oitems)) { AnnounceChanges(oitems, nitems, changes); return default; } else { sw.SpinOnce(); } } } /// /// Returns true if a Key/Value pair exists in the map /// /// Pair to find /// True if exists, false otherwise [Pure] public bool Contains(KeyValuePair pair) => Items.Contains(pair.Key, pair.Value); /// /// Enumerable of map keys /// [Pure] public Iterable Keys => Items.Keys; /// /// Enumerable of map values /// [Pure] public Iterable Values => Items.Values; /// /// Convert to a HashMap /// /// This is effectively a zero cost operation, not even a single allocation [Pure] public HashMap ToHashMap() => new (Items); /// /// GetEnumerator - IEnumerable interface /// public IEnumerator<(K Key, V Value)> GetEnumerator() => // ReSharper disable once NotDisposedResourceIsReturned Items.GetEnumerator(); /// /// GetEnumerator - IEnumerable interface /// IEnumerator IEnumerable.GetEnumerator() => // ReSharper disable once NotDisposedResourceIsReturned Items.GetEnumerator(); [Pure] public Seq<(K Key, V Value)> ToSeq() => toSeq(AsIterable()); /// /// Format the collection as `[(key: value), (key: value), (key: value), ...]` /// The ellipsis is used for collections over 50 items /// To get a formatted string with all the items, use `ToFullString` /// or `ToFullArrayString`. /// [Pure] public override string ToString() => CollectionFormat.ToShortArrayString(AsIterable().Map(kv => $"({kv.Key}: {kv.Value})"), Count); /// /// Format the collection as `(key: value), (key: value), (key: value), ...` /// [Pure] public string ToFullString(string separator = ", ") => CollectionFormat.ToFullString(AsIterable().Map(kv => $"({kv.Key}: {kv.Value})"), separator); /// /// Format the collection as `[(key: value), (key: value), (key: value), ...]` /// [Pure] public string ToFullArrayString(string separator = ", ") => CollectionFormat.ToFullArrayString(AsIterable().Map(kv => $"({kv.Key}: {kv.Value})"), separator); [Pure] public Iterable<(K Key, V Value)> AsIterable() => IterableExtensions.AsIterable(Items); /// /// Implicit conversion from an untyped empty list /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator AtomHashMap(SeqEmpty _) => Empty; /// /// Equality of keys and values with `EqDefault〈V〉` used for values /// [Pure] public static bool operator ==(AtomHashMap lhs, AtomHashMap rhs) => lhs.Equals(rhs); /// /// Equality of keys and values with `EqDefault〈V〉` used for values /// [Pure] public static bool operator ==(AtomHashMap lhs, HashMap rhs) => lhs?.Items.Equals(rhs.Value) ?? false; /// /// Equality of keys and values with `EqDefault〈V〉` used for values /// [Pure] public static bool operator ==(HashMap lhs, AtomHashMap rhs) => lhs.Value.Equals(rhs.Items); /// /// In-equality of keys and values with `EqDefault〈V〉` used for values /// [Pure] public static bool operator !=(AtomHashMap lhs, AtomHashMap rhs) => !(lhs == rhs); /// /// In-equality of keys and values with `EqDefault〈V〉` used for values /// [Pure] public static bool operator !=(AtomHashMap lhs, HashMap rhs) => !(lhs == rhs); /// /// In-equality of keys and values with `EqDefault〈V〉` used for values /// [Pure] public static bool operator !=(HashMap lhs, AtomHashMap rhs) => !(lhs == rhs); public Unit Append(AtomHashMap rhs) { if (rhs.IsEmpty) return default; SpinWait sw = default; while (true) { var oitems = Items; var onChange = Change; var (nitems, changes) = onChange == null ? (oitems.Append(rhs.Items), null) : oitems.AppendWithLog(rhs.Items); if (ReferenceEquals(Interlocked.CompareExchange(ref Items, nitems, oitems), oitems)) { AnnounceChanges(oitems, nitems, changes); return default; } else { sw.SpinOnce(); } } } public Unit Append(HashMap rhs) { if (rhs.IsEmpty) return default; SpinWait sw = default; while (true) { var oitems = this.Items; var onChange = Change; var (nitems, changes) = onChange == null ? (oitems.Append(rhs.Value), null) : oitems.AppendWithLog(rhs.Value); if (ReferenceEquals(Interlocked.CompareExchange(ref this.Items, nitems, oitems), oitems)) { AnnounceChanges(oitems, nitems, changes); return default; } else { sw.SpinOnce(); } } } public Unit Subtract(AtomHashMap rhs) { if (rhs.IsEmpty) return default; SpinWait sw = default; while (true) { var oitems = this.Items; var onChange = Change; var (nitems, changes) = onChange == null ? (oitems.Subtract(rhs.Items), null) : oitems.SubtractWithLog(rhs.Items); if (ReferenceEquals(Interlocked.CompareExchange(ref this.Items, nitems, oitems), oitems)) { AnnounceChanges(oitems, nitems, changes); return default; } else { sw.SpinOnce(); } } } public Unit Subtract(HashMap rhs) { if (rhs.IsEmpty) return default; SpinWait sw = default; while (true) { var oitems = this.Items; var onChange = Change; var (nitems, changes) = onChange == null ? (oitems.Subtract(rhs.Value), null) : oitems.SubtractWithLog(rhs.Value); if (ReferenceEquals(Interlocked.CompareExchange(ref this.Items, nitems, oitems), oitems)) { AnnounceChanges(oitems, nitems, changes); return default; } else { sw.SpinOnce(); } } } /// /// Returns True if 'other' is a proper subset of this set /// /// True if 'other' is a proper subset of this set [Pure] public bool IsProperSubsetOf(IEnumerable<(K Key, V Value)> other) => Items.IsProperSubsetOf(other); /// /// Returns True if 'other' is a proper subset of this set /// /// True if 'other' is a proper subset of this set [Pure] public bool IsProperSubsetOf(IEnumerable other) => Items.IsProperSubsetOf(other); /// /// Returns True if 'other' is a proper superset of this set /// /// True if 'other' is a proper superset of this set [Pure] public bool IsProperSupersetOf(IEnumerable<(K Key, V Value)> other) => Items.IsProperSupersetOf(other); /// /// Returns True if 'other' is a proper superset of this set /// /// True if 'other' is a proper superset of this set [Pure] public bool IsProperSupersetOf(IEnumerable other) => Items.IsProperSupersetOf(other); /// /// Returns True if 'other' is a superset of this set /// /// True if 'other' is a superset of this set [Pure] public bool IsSubsetOf(IEnumerable<(K Key, V Value)> other) => Items.IsSubsetOf(other); /// /// Returns True if 'other' is a superset of this set /// /// True if 'other' is a superset of this set [Pure] public bool IsSubsetOf(IEnumerable other) => Items.IsSubsetOf(other); /// /// Returns True if 'other' is a superset of this set /// /// True if 'other' is a superset of this set [Pure] public bool IsSubsetOf(HashMap other) => Items.IsSubsetOf(other.Value); /// /// Returns True if 'other' is a superset of this set /// /// True if 'other' is a superset of this set [Pure] public bool IsSupersetOf(IEnumerable<(K Key, V Value)> other) => Items.IsSupersetOf(other); /// /// Returns True if 'other' is a superset of this set /// /// True if 'other' is a superset of this set [Pure] public bool IsSupersetOf(IEnumerable rhs) => Items.IsSupersetOf(rhs); /// /// Returns the elements that are in both this and other /// public Unit Intersect(IEnumerable rhs) { SpinWait sw = default; var srhs = toSeq(rhs); while (true) { var oitems = this.Items; var onChange = Change; var (nitems, changes) = onChange == null ? (oitems.Intersect(srhs), null) : oitems.IntersectWithLog(srhs); if (ReferenceEquals(Interlocked.CompareExchange(ref this.Items, nitems, oitems), oitems)) { AnnounceChanges(oitems, nitems, changes); return default; } else { sw.SpinOnce(); } } } /// /// Returns the elements that are in both this and other /// public Unit Intersect(IEnumerable<(K Key, V Value)> rhs) { SpinWait sw = default; var srhs = new TrieMap(rhs); while (true) { var oitems = this.Items; var onChange = Change; var (nitems, changes) = onChange == null ? (oitems.Intersect(srhs), null) : oitems.IntersectWithLog(srhs); if (ReferenceEquals(Interlocked.CompareExchange(ref this.Items, nitems, oitems), oitems)) { AnnounceChanges(oitems, nitems, changes); return default; } else { sw.SpinOnce(); } } } /// /// Returns the elements that are in both this and other /// public Unit Intersect(IEnumerable<(K Key, V Value)> rhs, WhenMatched Merge) { SpinWait sw = default; var srhs = new TrieMap(rhs); while (true) { var oitems = this.Items; var onChange = Change; var (nitems, changes) = onChange == null ? (oitems.Intersect(srhs, Merge), null) : oitems.IntersectWithLog(srhs, Merge); if (ReferenceEquals(Interlocked.CompareExchange(ref this.Items, nitems, oitems), oitems)) { AnnounceChanges(oitems, nitems, changes); return default; } else { sw.SpinOnce(); } } } /// /// Returns the elements that are in both this and other /// public Unit Intersect(HashMap rhs, WhenMatched Merge) { SpinWait sw = default; while (true) { var oitems = this.Items; var onChange = Change; var (nitems, changes) = onChange == null ? (oitems.Intersect(rhs.Value, Merge), null) : oitems.IntersectWithLog(rhs.Value, Merge); if (ReferenceEquals(Interlocked.CompareExchange(ref this.Items, nitems, oitems), oitems)) { AnnounceChanges(oitems, nitems, changes); return default; } else { sw.SpinOnce(); } } } /// /// Returns True if other overlaps this set /// [Pure] public bool Overlaps(IEnumerable<(K Key, V Value)> other) => Items.Overlaps(other); /// /// Returns True if other overlaps this set /// [Pure] public bool Overlaps(IEnumerable other) => Items.Overlaps(other); /// /// Returns this - rhs. Only the items in this that are not in /// rhs will be returned. /// public Unit Except(IEnumerable rhs) { SpinWait sw = default; var srhs = toSeq(rhs); while (true) { var oitems = this.Items; var onChange = Change; var (nitems, changes) = onChange == null ? (oitems.Except(srhs), null) : oitems.ExceptWithLog(srhs); if (ReferenceEquals(Interlocked.CompareExchange(ref this.Items, nitems, oitems), oitems)) { AnnounceChanges(oitems, nitems, changes); return default; } else { sw.SpinOnce(); } } } /// /// Returns this - other. Only the items in this that are not in /// other will be returned. /// public Unit Except(IEnumerable<(K Key, V Value)> rhs) { SpinWait sw = default; var srhs = toSeq(rhs); while (true) { var oitems = this.Items; var onChange = Change; var (nitems, changes) = onChange == null ? (oitems.Except(srhs), null) : oitems.ExceptWithLog(srhs); if (ReferenceEquals(Interlocked.CompareExchange(ref this.Items, nitems, oitems), oitems)) { AnnounceChanges(oitems, nitems, changes); return default; } else { sw.SpinOnce(); } } } /// /// Only items that are in one set or the other will be returned. /// If an item is in both, it is dropped. /// public Unit SymmetricExcept(HashMap rhs) { SpinWait sw = default; while (true) { var oitems = this.Items; var onChange = Change; var (nitems, changes) = onChange == null ? (oitems.SymmetricExcept(rhs), null) : oitems.SymmetricExceptWithLog(rhs); if (ReferenceEquals(Interlocked.CompareExchange(ref this.Items, nitems, oitems), oitems)) { AnnounceChanges(oitems, nitems, changes); return default; } else { sw.SpinOnce(); } } } /// /// Only items that are in one set or the other will be returned. /// If an item is in both, it is dropped. /// public Unit SymmetricExcept(IEnumerable<(K Key, V Value)> rhs) { SpinWait sw = default; var srhs = toSeq(rhs); while (true) { var oitems = this.Items; var onChange = Change; var (nitems, changes) = onChange == null ? (oitems.SymmetricExcept(srhs), null) : oitems.SymmetricExceptWithLog(srhs); if (ReferenceEquals(Interlocked.CompareExchange(ref this.Items, nitems, oitems), oitems)) { AnnounceChanges(oitems, nitems, changes); return default; } else { sw.SpinOnce(); } } } /// /// Finds the union of two sets and produces a new set with /// the results /// /// Other set to union with /// A set which contains all items from both sets public Unit Union(IEnumerable<(K, V)> rhs) { SpinWait sw = default; var srhs = toSeq(rhs); if (srhs.IsEmpty) return unit; while (true) { var oitems = this.Items; var onChange = Change; var (nitems, changes) = onChange == null ? (oitems.Union(srhs), null) : oitems.UnionWithLog(srhs); if (ReferenceEquals(Interlocked.CompareExchange(ref this.Items, nitems, oitems), oitems)) { AnnounceChanges(oitems, nitems, changes); return default; } else { sw.SpinOnce(); } } } /// /// Union two maps. /// /// /// The `WhenMatched` merge function is called when keys are present in both map to allow resolving to a /// sensible value. /// public Unit Union(IEnumerable<(K Key, V Value)> rhs, WhenMatched Merge) { SpinWait sw = default; var srhs = toSeq(rhs); if (srhs.IsEmpty) return unit; while (true) { var oitems = this.Items; var onChange = Change; var (nitems, changes) = onChange == null ? (oitems.Union(srhs, Merge), null) : oitems.UnionWithLog(srhs, Merge); if (ReferenceEquals(Interlocked.CompareExchange(ref this.Items, nitems, oitems), oitems)) { AnnounceChanges(oitems, nitems, changes); return default; } else { sw.SpinOnce(); } } } /// /// Union two maps. /// /// /// The `WhenMatched` merge function is called when keys are present in both map to allow resolving to a /// sensible value. /// /// /// The `WhenMissing` function is called when there is a key in the right-hand side, but not the left-hand-side. /// This allows the `V2` value-type to be mapped to the target `V` value-type. /// public Unit Union(IEnumerable<(K Key, W Value)> rhs, WhenMissing MapRight, WhenMatched Merge) { SpinWait sw = default; var srhs = toSeq(rhs); if (srhs.IsEmpty) return unit; while (true) { var oitems = this.Items; var onChange = Change; var (nitems, changes) = onChange == null ? (oitems.Union(srhs, MapRight, Merge), null) : oitems.UnionWithLog(srhs, MapRight, Merge); if (ReferenceEquals(Interlocked.CompareExchange(ref this.Items, nitems, oitems), oitems)) { AnnounceChanges(oitems, nitems, changes); return default; } else { sw.SpinOnce(); } } } /// /// Equality of keys and values with `EqDefault〈V〉` used for values /// [Pure] public override bool Equals(object? obj) => obj is AtomHashMap hm && Equals(hm); /// /// Equality of keys and values with `EqDefault〈V〉` used for values /// [Pure] public bool Equals(AtomHashMap? other) => other is not null && Items.Equals(other.Items); /// /// Equality of keys and values with `EqDefault〈V〉` used for values /// [Pure] public bool Equals(HashMap other) => Items.Equals(other.Value); /// /// Equality of keys only /// [Pure] public bool EqualsKeys(AtomHashMap other) => Items.Equals>(other.Items); /// /// Equality of keys only /// [Pure] public bool EqualsKeys(HashMap other) => Items.Equals>(other.Value); [Pure] public override int GetHashCode() => Items.GetHashCode(); /// /// Atomically maps the map to a new map /// /// Mapped items in a new map [Pure] public AtomHashMap Select(Func f) => new AtomHashMap(Items.Map(f)); /// /// Atomically maps the map to a new map /// /// Mapped items in a new map [Pure] public AtomHashMap Select(Func f) => new AtomHashMap(Items.Map(f)); /// /// Atomically filter out items that return false when a predicate is applied /// /// Predicate /// New map with items filtered [Pure] [EditorBrowsable(EditorBrowsableState.Never)] public AtomHashMap Where(Func pred) => new AtomHashMap(Items.Filter(pred)); /// /// Atomically filter out items that return false when a predicate is applied /// /// Predicate /// New map with items filtered [Pure] [EditorBrowsable(EditorBrowsableState.Never)] public AtomHashMap Where(Func pred) => new AtomHashMap(Items.Filter(pred)); /// /// Return true if all items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] public bool ForAll(Func pred) { foreach (var (key, value) in AsIterable()) { if (!pred(key, value)) return false; } return true; } /// /// Return true if all items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] public bool ForAll(Func<(K Key, V Value), bool> pred) => AsIterable().Map(kv => (Key: kv.Key, Value: kv.Value)).ForAll(pred); /// /// Return true if all items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] public bool ForAll(Func pred) => Values.ForAll(pred); /// /// Return true if *any* items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied public bool Exists(Func pred) { foreach (var (key, value) in AsIterable()) { if (pred(key, value)) return true; } return false; } /// /// Return true if *any* items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] public bool Exists(Func<(K Key, V Value), bool> pred) => AsIterable().Map(kv => (kv.Key, kv.Value)).Exists(pred); /// /// Return true if *any* items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] public bool Exists(Func pred) => Values.Exists(pred); /// /// Atomically iterate through all key/value pairs in the map (in order) and execute an /// action on each /// /// Action to execute /// Unit public Unit Iter(Action action) { foreach (var (key, value) in this) { action(key, value); } return unit; } /// /// Atomically iterate through all values in the map (in order) and execute an /// action on each /// /// Action to execute /// Unit public Unit Iter(Action action) { foreach (var item in this) { action(item.Value); } return unit; } /// /// Atomically iterate through all key/value pairs (as tuples) in the map (in order) /// and execute an action on each /// /// Action to execute /// Unit public Unit Iter(Action<(K Key, V Value)> action) { foreach (var item in this) { action(item); } return unit; } /// /// Atomically iterate through all key/value pairs in the map (in order) and execute an /// action on each /// /// Action to execute /// Unit public Unit Iter(Action> action) { foreach (var item in this) { action(new KeyValuePair(item.Key, item.Value)); } return unit; } /// /// Atomically folds all items in the map (in order) using the folder function provided. /// /// State type /// Initial state /// Fold function /// Folded state [Pure] public S Fold(S state, Func folder) => AsIterable().Fold(state, (s, x) => folder(s, x.Key, x.Value)); /// /// Atomically folds all items in the map (in order) using the folder function provided. /// /// State type /// Initial state /// Fold function /// Folded state [Pure] public S Fold(S state, Func folder) => Values.Fold(state, folder); [MethodImpl(MethodImplOptions.AggressiveInlining)] void AnnounceChange(TrieMap prev, TrieMap current, K key, Change? change) { if (change?.HasChanged ?? false) { Change?.Invoke(new HashMapPatch(prev, current, key, change)); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] void AnnounceChanges(TrieMap prev, TrieMap current, TrieMap>? changes) { if (changes is not null) { Change?.Invoke(new HashMapPatch(prev, current, changes)); } } ///////////////////////////////////////////////////////////////////////////////////////////// // // IReadOnlyDictionary [Pure] public IDictionary ToDictionary( Func<(K Key, V Value), KR> keySelector, Func<(K Key, V Value), VR> valueSelector) where KR : notnull => AsIterable().ToDictionary(keySelector, valueSelector); [Pure] public bool TryGetValue(K key, out V value) { var v = Find(key); if (v.IsSome) { value = (V)v; return true; } else { value = default!; return false; } } [Pure] IEnumerator> IEnumerable>.GetEnumerator() => AsIterable() .Select(p => new KeyValuePair(p.Key, p.Value)) .GetEnumerator() ; [Pure] IEnumerable IReadOnlyDictionary.Keys => Keys; [Pure] IEnumerable IReadOnlyDictionary.Values => Values; [Pure] public IReadOnlyDictionary ToReadOnlyDictionary() => this; } ================================================ FILE: LanguageExt.Core/Concurrency/AtomHashMap/AtomHashMap.Module.Eq.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using LanguageExt; using static LanguageExt.Prelude; using System.ComponentModel; using System.Diagnostics.Contracts; using LanguageExt.Traits; namespace LanguageExt { /// /// Atoms provide a way to manage shared, synchronous, independent state without /// locks. `AtomHashMap` wraps the language-ext `HashMap`, and makes sure all operations are atomic and thread-safe /// without resorting to locking /// /// /// See the [concurrency section](https://github.com/louthy/language-ext/wiki/Concurrency) of the wiki for more info. /// public static partial class AtomHashMap { public static AtomHashMap ToAtom(this HashMap self) where EqK : Eq => new AtomHashMap(self); } } ================================================ FILE: LanguageExt.Core/Concurrency/AtomHashMap/AtomHashMap.Module.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using LanguageExt; using static LanguageExt.Prelude; using System.ComponentModel; using System.Diagnostics.Contracts; using LanguageExt.Traits; namespace LanguageExt { /// /// Atoms provide a way to manage shared, synchronous, independent state without /// locks. `AtomHashMap` wraps the language-ext `HashMap`, and makes sure all operations are atomic and thread-safe /// without resorting to locking /// /// /// See the [concurrency section](https://github.com/louthy/language-ext/wiki/Concurrency) of the wiki for more info. /// public static partial class AtomHashMap { public static AtomHashMap ToAtom(this HashMap self) => new AtomHashMap(self); } } ================================================ FILE: LanguageExt.Core/Concurrency/AtomHashMap/AtomHashMap.cs ================================================ #pragma warning disable CS0693 // Type parameter has the same name as the type parameter from outer type using System; using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics.Contracts; using System.Linq; using System.Runtime.CompilerServices; using System.Threading; using LanguageExt.ClassInstances; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; /// /// Atoms provide a way to manage shared, synchronous, independent state without /// locks. `AtomHashMap` wraps the language-ext `HashMap`, and makes sure all operations are atomic and thread-safe /// without resorting to locking /// /// /// See the [concurrency section](https://github.com/louthy/language-ext/wiki/Concurrency) of the wiki for more info. /// public class AtomHashMap : IEnumerable<(K Key, V Value)>, IEquatable>, IEquatable>, IReadOnlyDictionary { volatile TrieMap, K, V> Items; public event AtomHashMapChangeEvent? Change; /// /// Creates a new atom-hashmap /// public static AtomHashMap Empty => new AtomHashMap(TrieMap, K, V>.Empty); /// /// Constructor /// /// Trie map AtomHashMap(TrieMap, K, V> items) => this.Items = items; /// /// Constructor /// /// Hash map internal AtomHashMap(HashMap items) => this.Items = items.Value; /// /// 'this' accessor /// /// Key /// Optional value [Pure] public V this[K key] => Items[key]; /// /// Is the map empty /// [Pure] public bool IsEmpty { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => Items.IsEmpty; } /// /// Number of items in the map /// [Pure] public int Count { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => Items.Count; } /// /// Alias of Count /// [Pure] public int Length { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => Items.Count; } /// /// Atomically swap the underlying hash-map. Allows for multiple operations on the hash-map in an entirely /// transactional and atomic way. /// /// Swap function, maps the current state of the AtomHashMap to a new state /// Any functions passed as arguments may be run multiple times if there are multiple threads competing /// to update this data structure. Therefore the functions must spend as little time performing the injected /// behaviours as possible to avoid repeated attempts /// /// NOTE: The change-tracking only works if you transform the `TrackingHashMap` provided to the `Func`. If you /// build a fresh one then it won't have any tracking. You can call `map.Clear()` to get to an empty /// map, which will also track the removals. /// public Unit Swap(Func, TrackingHashMap> swap) { SpinWait sw = default; while (true) { var oitems = Items; var nitems = swap(new TrackingHashMap(oitems)); if(ReferenceEquals(oitems, nitems.Value)) { // no change return default; } if (ReferenceEquals(Interlocked.CompareExchange(ref Items, nitems.Value, oitems), oitems)) { AnnounceChanges(oitems, nitems.Value, nitems.Changes.Value); return default; } else { sw.SpinOnce(); } } } /// /// Atomically swap a key in the hash-map, if it exists. If it doesn't exist, nothing changes. /// Allows for multiple operations on the hash-map value in an entirely transactional and atomic way. /// /// Swap function, maps the current state of a value in the AtomHashMap to a new value /// Any functions passed as arguments may be run multiple times if there are multiple threads competing /// to update this data structure. Therefore the functions must spend as little time performing the injected /// behaviours as possible to avoid repeated attempts public Unit SwapKey(K key, Func swap) { SpinWait sw = default; while (true) { var oitems = Items; var ovalue = oitems.Find(key); if (ovalue.IsNone) return unit; var nvalue = swap((V)ovalue); var onChange = Change; var (nitems, change) = onChange == null ? (oitems.SetItem(key, nvalue), Change.None) : oitems.SetItemWithLog(key, nvalue); if (ReferenceEquals(Interlocked.CompareExchange(ref Items, nitems, oitems), oitems)) { AnnounceChange(oitems, nitems, key, change); return default; } else { sw.SpinOnce(); } } } /// /// Atomically swap a key in the hash-map: /// /// * If the key doesn't exist, then `None` is passed to `swap` /// * If the key does exist, then `Some(x)` is passed to `swap` /// /// The operation performed on the hash-map depends on the value going in and out of `swap`: /// /// In | Out | Operation /// --------- | --------- | --------------- /// `Some(x)` | `Some(y)` | `SetItem(key, y)` /// `Some(x)` | `None` | `Remove(key)` /// `None` | `Some(y)` | `Add(key, y)` /// `None` | `None` | `no-op` /// /// Allows for multiple operations on the hash-map value in an entirely transactional and atomic way. /// /// Swap function, maps the current state of a value in the AtomHashMap to a new value /// Any functions passed as arguments may be run multiple times if there are multiple threads competing /// to update this data structure. Therefore the functions must spend as little time performing the injected /// behaviours as possible to avoid repeated attempts public Unit SwapKey(K key, Func, Option> swap) { SpinWait sw = default; while (true) { var oitems = Items; var ovalue = oitems.Find(key); var nvalue = swap(ovalue); var onChange = Change; var (nitems, change) = onChange == null ? (ovalue.IsSome, nvalue.IsSome) switch { (true, true) => (oitems.SetItem(key, (V)nvalue), Change.None), (true, false) => (oitems.Remove(key), Change.None), (false, true) => (oitems.Add(key, (V)nvalue), Change.None), (false, false) => (oitems, Change.None) } : (ovalue.IsSome, nvalue.IsSome) switch { (true, true) => oitems.SetItemWithLog(key, (V)nvalue), (true, false) => oitems.RemoveWithLog(key), (false, true) => oitems.AddWithLog(key, (V)nvalue), (false, false) => (oitems, Change.None) }; if(ReferenceEquals(oitems, nitems)) { // no change return default; } if (ReferenceEquals(Interlocked.CompareExchange(ref Items, nitems, oitems), oitems)) { AnnounceChange(oitems, nitems, key, change); return default; } else { sw.SpinOnce(); } } } /// /// Atomically filter out items that return false when a predicate is applied /// /// Predicate /// New map with items filtered [Pure] public AtomHashMap Filter(Func pred) => new(Items.Filter(pred)); /// /// Atomically filter out items that return false when a predicate is applied /// /// Predicate /// New map with items filtered /// Any functions passed as arguments may be run multiple times if there are multiple threads competing /// to update this data structure. Therefore the functions must spend as little time performing the injected /// behaviours as possible to avoid repeated attempts public Unit FilterInPlace(Func pred) { SpinWait sw = default; while (true) { var oitems = Items; var onChange = Change; var (nitems, changes) = onChange == null ? (oitems.Filter(pred), null) : oitems.FilterWithLog(pred); if (ReferenceEquals(Interlocked.CompareExchange(ref Items, nitems, oitems), oitems)) { AnnounceChanges(oitems, nitems, changes); return default; } else { sw.SpinOnce(); } } } /// /// Atomically filter out items that return false when a predicate is applied /// /// Predicate [Pure] public AtomHashMap Filter(Func pred) => new(Items.Filter(pred)); /// /// Atomically filter out items that return false when a predicate is applied /// /// Predicate /// Any functions passed as arguments may be run multiple times if there are multiple threads competing /// to update this data structure. Therefore the functions must spend as little time performing the injected /// behaviours as possible to avoid repeated attempts public Unit FilterInPlace(Func pred) { SpinWait sw = default; while (true) { var oitems = Items; var onChange = Change; var (nitems, changes) = onChange == null ? (oitems.Filter(pred), null) : oitems.FilterWithLog(pred); if (ReferenceEquals(Interlocked.CompareExchange(ref Items, nitems, oitems), oitems)) { AnnounceChanges(oitems, nitems, changes); return default; } else { sw.SpinOnce(); } } } /// /// Atomically maps the map to a new map /// /// Mapped items in a new map /// Any functions passed as arguments may be run multiple times if there are multiple threads competing /// to update this data structure. Therefore the functions must spend as little time performing the injected /// behaviours as possible to avoid repeated attempts public Unit MapInPlace(Func f) { SpinWait sw = default; while (true) { var oitems = Items; var onChange = Change; var (nitems, changes) = onChange == null ? (oitems.Map(f), null) : oitems.MapWithLog(f); if (ReferenceEquals(Interlocked.CompareExchange(ref Items, nitems, oitems), oitems)) { AnnounceChanges(oitems, nitems, changes); return default; } else { sw.SpinOnce(); } } } /// /// Atomically maps the map to a new map /// /// Mapped items in a new map public AtomHashMap Map(Func f) => new AtomHashMap(Items.Map(f)); /// /// Atomically adds a new item to the map /// /// Null is not allowed for a Key or a Value /// Key /// Value /// Throws ArgumentException if the key already exists /// Throws ArgumentNullException the key or value are null public Unit Add(K key, V value) { SpinWait sw = default; while (true) { var oitems = Items; var onChange = Change; var (nitems, change) = onChange == null ? (oitems.Add(key, value), null) : oitems.AddWithLog(key, value); if (ReferenceEquals(Interlocked.CompareExchange(ref Items, nitems, oitems), oitems)) { AnnounceChange(oitems, nitems, key, change); return default; } else { sw.SpinOnce(); } } } /// /// Atomically adds a new item to the map. /// If the key already exists, then the new item is ignored /// /// Null is not allowed for a Key or a Value /// Key /// Value /// Throws ArgumentNullException the key or value are null public Unit TryAdd(K key, V value) { SpinWait sw = default; while (true) { var oitems = Items; var onChange = Change; var (nitems, change) = onChange == null ? (oitems.TryAdd(key, value), null) : oitems.TryAddWithLog(key, value); if(ReferenceEquals(oitems, nitems)) { return default; } if (ReferenceEquals(Interlocked.CompareExchange(ref Items, nitems, oitems), oitems)) { AnnounceChange(oitems, nitems, key, change); return default; } else { sw.SpinOnce(); } } } /// /// Atomically adds a new item to the map. /// If the key already exists, the new item replaces it. /// /// Null is not allowed for a Key or a Value /// Key /// Value /// Throws ArgumentNullException the key or value are null public Unit AddOrUpdate(K key, V value) { SpinWait sw = default; while (true) { var oitems = Items; var onChange = Change; var (nitems, change) = onChange == null ? (oitems.AddOrUpdate(key, value), null) : oitems.AddOrUpdateWithLog(key, value); if (ReferenceEquals(Interlocked.CompareExchange(ref Items, nitems, oitems), oitems)) { AnnounceChange(oitems, nitems, key, change); return default; } else { sw.SpinOnce(); } } } /// /// Retrieve a value from the map by key, map it to a new value, /// put it back. If it doesn't exist, add a new one based on None result. /// /// Key to find /// Throws Exception if None returns null /// Throws Exception if Some returns null /// Any functions passed as arguments may be run multiple times if there are multiple threads competing /// to update this data structure. Therefore the functions must spend as little time performing the injected /// behaviours as possible to avoid repeated attempts public Unit AddOrUpdate(K key, Func Some, Func None) { SpinWait sw = default; while (true) { var oitems = Items; var onChange = Change; var (nitems, change) = onChange == null ? (oitems.AddOrUpdate(key, Some, None), null) : oitems.AddOrUpdateWithLog(key, Some, None); if (ReferenceEquals(Interlocked.CompareExchange(ref Items, nitems, oitems), oitems)) { AnnounceChange(oitems, nitems, key, change); return default; } else { sw.SpinOnce(); } } } /// /// Retrieve a value from the map by key, map it to a new value, /// put it back. If it doesn't exist, add a new one based on None result. /// /// Key to find /// Throws ArgumentNullException if None is null /// Throws Exception if Some returns null /// Any functions passed as arguments may be run multiple times if there are multiple threads competing /// to update this data structure. Therefore the functions must spend as little time performing the injected /// behaviours as possible to avoid repeated attempts public Unit AddOrUpdate(K key, Func Some, V None) { SpinWait sw = default; while (true) { var oitems = Items; var onChange = Change; var (nitems, change) = onChange == null ? (oitems.AddOrUpdate(key, Some, None), null) : oitems.AddOrUpdateWithLog(key, Some, None); if (ReferenceEquals(Interlocked.CompareExchange(ref Items, nitems, oitems), oitems)) { AnnounceChange(oitems, nitems, key, change); return default; } else { sw.SpinOnce(); } } } /// /// Atomically adds a range of items to the map. /// /// Null is not allowed for a Key or a Value /// Range of tuples to add /// Throws ArgumentException if any of the keys already exist /// Throws ArgumentNullException the keys or values are null public Unit AddRange(IEnumerable<(K Key, V Value)> range) { SpinWait sw = default; var srange = toSeq(range); if (srange.IsEmpty) return default; while (true) { var oitems = Items; var onChange = Change; var (nitems, changes) = onChange == null ? (oitems.AddRange(srange), null) : oitems.AddRangeWithLog(srange); if (ReferenceEquals(Interlocked.CompareExchange(ref Items, nitems, oitems), oitems)) { AnnounceChanges(oitems, nitems, changes); return default; } else { sw.SpinOnce(); } } } /// /// Atomically adds a range of items to the map. If any of the keys exist already /// then they're ignored. /// /// Null is not allowed for a Key or a Value /// Range of tuples to add /// Throws ArgumentNullException the keys or values are null public Unit TryAddRange(IEnumerable<(K Key, V Value)> range) { SpinWait sw = default; var srange = toSeq(range); if (srange.IsEmpty) return default; while (true) { var oitems = Items; var onChange = Change; var (nitems, changes) = onChange == null ? (oitems.TryAddRange(srange), null) : oitems.TryAddRangeWithLog(srange); if(ReferenceEquals(oitems, nitems)) { return default; } if (ReferenceEquals(Interlocked.CompareExchange(ref Items, nitems, oitems), oitems)) { AnnounceChanges(oitems, nitems, changes); return default; } else { sw.SpinOnce(); } } } /// /// Atomically adds a range of items to the map. If any of the keys exist already /// then they're ignored. /// /// Null is not allowed for a Key or a Value /// Range of KeyValuePairs to add /// Throws ArgumentNullException the keys or values are null public Unit TryAddRange(IEnumerable> range) { SpinWait sw = default; var srange = toSeq(range); if (srange.IsEmpty) return default; while (true) { var oitems = Items; var onChange = Change; var (nitems, changes) = onChange == null ? (oitems.TryAddRange(srange), null) : oitems.TryAddRangeWithLog(srange); if(ReferenceEquals(oitems, nitems)) { return default; } if (ReferenceEquals(Interlocked.CompareExchange(ref Items, nitems, oitems), oitems)) { AnnounceChanges(oitems, nitems, changes); return default; } else { sw.SpinOnce(); } } } /// /// Atomically adds a range of items to the map. If any of the keys exist already /// then they're replaced. /// /// Null is not allowed for a Key or a Value /// Range of tuples to add /// Throws ArgumentNullException the keys or values are null /// New Map with the items added public Unit AddOrUpdateRange(IEnumerable<(K Key, V Value)> range) { SpinWait sw = default; var srange = toSeq(range); if (srange.IsEmpty) return default; while (true) { var oitems = Items; var onChange = Change; var (nitems, changes) = onChange == null ? (oitems.AddOrUpdateRange(srange), null) : oitems.AddOrUpdateRangeWithLog(srange); if (ReferenceEquals(Interlocked.CompareExchange(ref Items, nitems, oitems), oitems)) { AnnounceChanges(oitems, nitems, changes); return default; } else { sw.SpinOnce(); } } } /// /// Atomically adds a range of items to the map. If any of the keys exist already /// then they're replaced. /// /// Null is not allowed for a Key or a Value /// Range of KeyValuePairs to add /// Throws ArgumentNullException the keys or values are null public Unit AddOrUpdateRange(IEnumerable> range) { SpinWait sw = default; var srange = toSeq(range); if (srange.IsEmpty) return default; while (true) { var oitems = Items; var onChange = Change; var (nitems, changes) = onChange == null ? (oitems.AddOrUpdateRange(srange), null) : oitems.AddOrUpdateRangeWithLog(srange); if (ReferenceEquals(Interlocked.CompareExchange(ref Items, nitems, oitems), oitems)) { AnnounceChanges(oitems, nitems, changes); return default; } else { sw.SpinOnce(); } } } /// /// Atomically removes an item from the map /// If the key doesn't exists, the request is ignored. /// /// Key public Unit Remove(K key) { SpinWait sw = default; while (true) { var oitems = Items; var onChange = Change; var (nitems, change) = onChange == null ? (oitems.Remove(key), null) : oitems.RemoveWithLog(key); if(ReferenceEquals(oitems, nitems)) { return default; } if (ReferenceEquals(Interlocked.CompareExchange(ref Items, nitems, oitems), oitems)) { AnnounceChange(oitems, nitems, key, change); return default; } else { sw.SpinOnce(); } } } /// /// Retrieve a value from the map by key /// /// Key to find /// Found value [Pure] public Option Find(K value) => Items.Find(value); /// /// Retrieve a value from the map by key as an enumerable /// /// Key to find /// Found value [Pure] public Seq FindSeq(K key) => Items.FindAll(key); /// /// Retrieve a value from the map by key and pattern match the /// result. /// /// Key to find /// Found value [Pure] public R Find(K key, Func Some, Func None) => Items.Find(key, Some, None); /// /// Try to find the key in the map, if it doesn't exist, add a new /// item by invoking the delegate provided. /// /// Key to find /// Delegate to get the value /// Added value /// Any functions passed as arguments may be run multiple times if there are multiple threads competing /// to update this data structure. Therefore the functions must spend as little time performing the injected /// behaviours as possible to avoid repeated attempts public V FindOrAdd(K key, Func None) { SpinWait sw = default; while (true) { var oitems = Items; var (nitems, value, change) = Items.FindOrAddWithLog(key, None); if(ReferenceEquals(oitems, nitems)) { return value; } if (ReferenceEquals(Interlocked.CompareExchange(ref Items, nitems, oitems), oitems)) { AnnounceChange(oitems, nitems, key, change); return value; } else { sw.SpinOnce(); } } } /// /// Try to find the key in the map, if it doesn't exist, add a new /// item provided. /// /// Key to find /// Delegate to get the value /// Added value public V FindOrAdd(K key, V value) { SpinWait sw = default; while (true) { var oitems = Items; var (nitems, nvalue, change) = Items.FindOrAddWithLog(key, value); if(ReferenceEquals(oitems, nitems)) { return nvalue; } if (ReferenceEquals(Interlocked.CompareExchange(ref Items, nitems, oitems), oitems)) { AnnounceChange(oitems, nitems, key, change); return nvalue; } else { sw.SpinOnce(); } } } /// /// Try to find the key in the map, if it doesn't exist, add a new /// item by invoking the delegate provided. /// /// Key to find /// Delegate to get the value /// Added value /// Any functions passed as arguments may be run multiple times if there are multiple threads competing /// to update this data structure. Therefore the functions must spend as little time performing the injected /// behaviours as possible to avoid repeated attempts public Option FindOrMaybeAdd(K key, Func> None) { SpinWait sw = default; while (true) { var oitems = Items; var (nitems, nvalue, change) = Items.FindOrMaybeAddWithLog(key, None); if(ReferenceEquals(oitems, nitems)) { return nvalue; } if (ReferenceEquals(Interlocked.CompareExchange(ref Items, nitems, oitems), oitems)) { AnnounceChange(oitems, nitems, key, change); return nvalue; } else { sw.SpinOnce(); } } } /// /// Try to find the key in the map, if it doesn't exist, add a new /// item by invoking the delegate provided. /// /// Key to find /// Delegate to get the value /// Added value public Option FindOrMaybeAdd(K key, Option None) { SpinWait sw = default; while (true) { var oitems = Items; var (nitems, nvalue, change) = Items.FindOrMaybeAddWithLog(key, None); if(ReferenceEquals(oitems, nitems)) { return nvalue; } if (ReferenceEquals(Interlocked.CompareExchange(ref Items, nitems, oitems), oitems)) { AnnounceChange(oitems, nitems, key, change); return nvalue; } else { sw.SpinOnce(); } } } /// /// Atomically updates an existing item /// /// Null is not allowed for a Key or a Value /// Key /// Value /// Throws ArgumentNullException the key or value are null public Unit SetItem(K key, V value) { SpinWait sw = default; while (true) { var oitems = Items; var onChange = Change; var (nitems, change) = onChange == null ? (oitems.SetItem(key, value), null) : oitems.SetItemWithLog(key, value); if (ReferenceEquals(Interlocked.CompareExchange(ref Items, nitems, oitems), oitems)) { AnnounceChange(oitems, nitems, key, change); return default; } else { sw.SpinOnce(); } } } /// /// Retrieve a value from the map by key, map it to a new value, /// put it back. /// /// Key to set /// Throws ArgumentException if the item isn't found /// Throws Exception if Some returns null /// Any functions passed as arguments may be run multiple times if there are multiple threads competing /// to update this data structure. Therefore the functions must spend as little time performing the injected /// behaviours as possible to avoid repeated attempts public Unit SetItem(K key, Func Some) { SpinWait sw = default; while (true) { var oitems = Items; var onChange = Change; var(nitems, change) = onChange == null ? (oitems.SetItem(key, Some), null) : oitems.SetItemWithLog(key, Some); if (ReferenceEquals(Interlocked.CompareExchange(ref Items, nitems, oitems), oitems)) { AnnounceChange(oitems, nitems, key, change); return default; } else { sw.SpinOnce(); } } } /// /// Atomically updates an existing item, unless it doesn't exist, in which case /// it is ignored /// /// Null is not allowed for a Key or a Value /// Key /// Value /// Throws ArgumentNullException the value is null public Unit TrySetItem(K key, V value) { SpinWait sw = default; while (true) { var oitems = Items; var onChange = Change; var (nitems, change) = onChange == null ? (oitems.TrySetItem(key, value), null) : oitems.TrySetItemWithLog(key, value); if(ReferenceEquals(oitems, nitems)) { return default; } if (ReferenceEquals(Interlocked.CompareExchange(ref Items, nitems, oitems), oitems)) { AnnounceChange(oitems, nitems, key, change); return default; } else { sw.SpinOnce(); } } } /// /// Atomically sets an item by first retrieving it, applying a map, and then putting it back. /// Silently fails if the value doesn't exist /// /// Key to set /// delegate to map the existing value to a new one before setting /// Throws Exception if Some returns null /// Throws ArgumentNullException the key or value are null /// Any functions passed as arguments may be run multiple times if there are multiple threads competing /// to update this data structure. Therefore the functions must spend as little time performing the injected /// behaviours as possible to avoid repeated attempts public Unit TrySetItem(K key, Func Some) { SpinWait sw = default; while (true) { var oitems = Items; var onChange = Change; var (nitems, change) = onChange == null ? (oitems.TrySetItem(key, Some), null) : oitems.TrySetItemWithLog(key, Some); if(ReferenceEquals(oitems, nitems)) { return default; } if (ReferenceEquals(Interlocked.CompareExchange(ref Items, nitems, oitems), oitems)) { AnnounceChange(oitems, nitems, key, change); return default; } else { sw.SpinOnce(); } } } /// /// Checks for existence of a key in the map /// /// Key to check /// True if an item with the key supplied is in the map [Pure] public bool ContainsKey(K key) => Items.ContainsKey(key); /// /// Checks for existence of a key in the map /// /// Key to check /// True if an item with the key supplied is in the map [Pure] public bool Contains(K key, V value) => Items.Contains(key, value); /// /// Checks for existence of a value in the map /// /// Value to check /// True if an item with the value supplied is in the map [Pure] public bool Contains(V value) => Items.Contains(value); /// /// Checks for existence of a value in the map /// /// Value to check /// True if an item with the value supplied is in the map [Pure] public bool Contains(V value) where EqV : Eq => Items.Contains(value); /// /// Checks for existence of a key in the map /// /// Key to check /// True if an item with the key supplied is in the map [Pure] public bool Contains(K key, V value) where EqV : Eq => Items.Contains(key, value); /// /// Clears all items from the map /// public Unit Clear() { SpinWait sw = default; while (true) { var oitems = Items; var onChange = Change; var (nitems, changes) = onChange == null ? (oitems.Clear(), null) : oitems.ClearWithLog(); if (ReferenceEquals(Interlocked.CompareExchange(ref Items, nitems, oitems), oitems)) { AnnounceChanges(oitems, nitems, changes); return default; } else { sw.SpinOnce(); } } } /// /// Atomically adds a range of items to the map /// /// Range of KeyValuePairs to add /// Throws ArgumentException if any of the keys already exist public Unit AddRange(IEnumerable> pairs) { SpinWait sw = default; var spairs = toSeq(pairs); if (spairs.IsEmpty) return default; while (true) { var oitems = Items; var onChange = Change; var (nitems, changes) = onChange == null ? (oitems.AddRange(spairs), null) : oitems.AddRangeWithLog(spairs); if (ReferenceEquals(Interlocked.CompareExchange(ref Items, nitems, oitems), oitems)) { AnnounceChanges(oitems, nitems, changes); return default; } else { sw.SpinOnce(); } } } /// /// Atomically sets a series of items using the KeyValuePairs provided /// /// Items to set /// Throws ArgumentException if any of the keys aren't in the map public Unit SetItems(IEnumerable> items) { SpinWait sw = default; var sitems = toSeq(items); if (sitems.IsEmpty) return default; while (true) { var oitems = this.Items; var onChange = Change; var (nitems, changes) = onChange == null ? (oitems.SetItems(sitems), null) : oitems.SetItemsWithLog(sitems); if (ReferenceEquals(Interlocked.CompareExchange(ref this.Items, nitems, oitems), oitems)) { AnnounceChanges(oitems, nitems, changes); return default; } else { sw.SpinOnce(); } } } /// /// Atomically sets a series of items using the Tuples provided. /// /// Items to set /// Throws ArgumentException if any of the keys aren't in the map public Unit SetItems(IEnumerable<(K Key, V Value)> items) { SpinWait sw = default; var sitems = toSeq(items); if (sitems.IsEmpty) return default; while (true) { var oitems = this.Items; var onChange = Change; var (nitems, changes) = onChange == null ? (oitems.SetItems(sitems), null) : oitems.SetItemsWithLog(sitems); if (ReferenceEquals(Interlocked.CompareExchange(ref this.Items, nitems, oitems), oitems)) { AnnounceChanges(oitems, nitems, changes); return default; } else { sw.SpinOnce(); } } } /// /// Atomically sets a series of items using the KeyValuePairs provided. If any of the /// items don't exist then they're silently ignored. /// /// Items to set public Unit TrySetItems(IEnumerable> items) { SpinWait sw = default; var sitems = toSeq(items); if (sitems.IsEmpty) return default; while (true) { var oitems = this.Items; var onChange = Change; var (nitems, changes) = onChange == null ? (oitems.TrySetItems(sitems), null) : oitems.TrySetItemsWithLog(sitems); if (ReferenceEquals(oitems, nitems)) { return default; } if (ReferenceEquals(Interlocked.CompareExchange(ref this.Items, nitems, oitems), oitems)) { AnnounceChanges(oitems, nitems, changes); return default; } else { sw.SpinOnce(); } } } /// /// Atomically sets a series of items using the Tuples provided If any of the /// items don't exist then they're silently ignored. /// /// Items to set public Unit TrySetItems(IEnumerable<(K Key, V Value)> items) { SpinWait sw = default; var sitems = toSeq(items); if (sitems.IsEmpty) return default; while (true) { var oitems = this.Items; var onChange = Change; var (nitems, changes) = onChange == null ? (oitems.TrySetItems(sitems), null) : oitems.TrySetItemsWithLog(sitems); if (ReferenceEquals(oitems, nitems)) { return default; } if (ReferenceEquals(Interlocked.CompareExchange(ref this.Items, nitems, oitems), oitems)) { AnnounceChanges(oitems, nitems, changes); return default; } else { sw.SpinOnce(); } } } /// /// Atomically sets a series of items using the keys provided to find the items /// and the Some delegate maps to a new value. If the items don't exist then /// they're silently ignored. /// /// Keys of items to set /// Function map the existing item to a new one /// Any functions passed as arguments may be run multiple times if there are multiple threads competing /// to update this data structure. Therefore the functions must spend as little time performing the injected /// behaviours as possible to avoid repeated attempts public Unit TrySetItems(IEnumerable keys, Func Some) { SpinWait sw = default; var skeys = toSeq(keys); if (skeys.IsEmpty) return default; while (true) { var oitems = this.Items; var onChange = Change; var (nitems, changes) = onChange == null ? (oitems.TrySetItems(skeys, Some), null) : oitems.TrySetItemsWithLog(skeys, Some); if (ReferenceEquals(oitems, nitems)) { return default; } if (ReferenceEquals(Interlocked.CompareExchange(ref this.Items, nitems, oitems), oitems)) { AnnounceChanges(oitems, nitems, changes); return default; } else { sw.SpinOnce(); } } } /// /// Atomically removes a set of keys from the map /// /// Keys to remove public Unit RemoveRange(IEnumerable keys) { SpinWait sw = default; var skeys = toSeq(keys); if (skeys.IsEmpty) return default; while (true) { var oitems = this.Items; var onChange = Change; var (nitems, changes) = onChange == null ? (oitems.RemoveRange(skeys), null) : oitems.RemoveRangeWithLog(skeys); if (ReferenceEquals(oitems, nitems)) { return default; } if (ReferenceEquals(Interlocked.CompareExchange(ref this.Items, nitems, oitems), oitems)) { AnnounceChanges(oitems, nitems, changes); return default; } else { sw.SpinOnce(); } } } /// /// Returns true if a Key/Value pair exists in the map /// /// Pair to find /// True if exists, false otherwise [Pure] public bool Contains(KeyValuePair pair) => Items.Contains(pair.Key, pair.Value); /// /// Enumerable of map keys /// [Pure] public Iterable Keys => Items.Keys; /// /// Enumerable of map values /// [Pure] public Iterable Values => Items.Values; /// /// Convert the map to an IDictionary /// /// This is effectively a zero cost operation, not even a single allocation [Pure] public IReadOnlyDictionary ToDictionary() => this; /// /// Convert to a HashMap /// /// This is effectively a zero cost operation, not even a single allocation [Pure] public HashMap ToHashMap() => new (Items); /// /// GetEnumerator - IEnumerable interface /// public IEnumerator<(K Key, V Value)> GetEnumerator() => // ReSharper disable once NotDisposedResourceIsReturned Items.GetEnumerator(); /// /// GetEnumerator - IEnumerable interface /// IEnumerator IEnumerable.GetEnumerator() => // ReSharper disable once NotDisposedResourceIsReturned Items.GetEnumerator(); [Pure] public Seq<(K Key, V Value)> ToSeq() => toSeq(AsIterable()); /// /// Format the collection as `[(key: value), (key: value), (key: value), ...]` /// The ellipsis is used for collections over 50 items /// To get a formatted string with all the items, use `ToFullString` /// or `ToFullArrayString`. /// [Pure] public override string ToString() => CollectionFormat.ToShortArrayString(AsIterable().Map(kv => $"({kv.Key}: {kv.Value})"), Count); /// /// Format the collection as `(key: value), (key: value), (key: value), ...` /// [Pure] public string ToFullString(string separator = ", ") => CollectionFormat.ToFullString(AsIterable().Map(kv => $"({kv.Key}: {kv.Value})"), separator); /// /// Format the collection as `[(key: value), (key: value), (key: value), ...]` /// [Pure] public string ToFullArrayString(string separator = ", ") => CollectionFormat.ToFullArrayString(AsIterable().Map(kv => $"({kv.Key}: {kv.Value})"), separator); [Pure] public Iterable<(K Key, V Value)> AsIterable() => IterableExtensions.AsIterable(Items); /// /// Implicit conversion from an untyped empty list /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator AtomHashMap(SeqEmpty _) => Empty; /// /// Equality of keys and values with `EqDefault〈V〉` used for values /// [Pure] public static bool operator ==(AtomHashMap lhs, AtomHashMap rhs) => lhs.Equals(rhs); /// /// Equality of keys and values with `EqDefault〈V〉` used for values /// [Pure] public static bool operator ==(AtomHashMap lhs, HashMap rhs) => lhs?.Items.Equals(rhs.Value) ?? false; /// /// Equality of keys and values with `EqDefault〈V〉` used for values /// [Pure] public static bool operator ==(HashMap lhs, AtomHashMap rhs) => lhs.Value.Equals(rhs.Items); /// /// In-equality of keys and values with `EqDefault〈V〉` used for values /// [Pure] public static bool operator !=(AtomHashMap lhs, AtomHashMap rhs) => !(lhs == rhs); /// /// In-equality of keys and values with `EqDefault〈V〉` used for values /// [Pure] public static bool operator !=(AtomHashMap lhs, HashMap rhs) => !(lhs == rhs); /// /// In-equality of keys and values with `EqDefault〈V〉` used for values /// [Pure] public static bool operator !=(HashMap lhs, AtomHashMap rhs) => !(lhs == rhs); public Unit Append(AtomHashMap rhs) { if (rhs.IsEmpty) return default; SpinWait sw = default; while (true) { var oitems = Items; var onChange = Change; var (nitems, changes) = onChange == null ? (oitems.Append(rhs.Items), null) : oitems.AppendWithLog(rhs.Items); if (ReferenceEquals(Interlocked.CompareExchange(ref Items, nitems, oitems), oitems)) { AnnounceChanges(oitems, nitems, changes); return default; } else { sw.SpinOnce(); } } } public Unit Append(HashMap rhs) { if (rhs.IsEmpty) return default; SpinWait sw = default; while (true) { var oitems = this.Items; var onChange = Change; var (nitems, changes) = onChange == null ? (oitems.Append(rhs.Value), null) : oitems.AppendWithLog(rhs.Value); if (ReferenceEquals(Interlocked.CompareExchange(ref this.Items, nitems, oitems), oitems)) { AnnounceChanges(oitems, nitems, changes); return default; } else { sw.SpinOnce(); } } } public Unit Subtract(AtomHashMap rhs) { if (rhs.IsEmpty) return default; SpinWait sw = default; while (true) { var oitems = this.Items; var onChange = Change; var (nitems, changes) = onChange == null ? (oitems.Subtract(rhs.Items), null) : oitems.SubtractWithLog(rhs.Items); if (ReferenceEquals(Interlocked.CompareExchange(ref this.Items, nitems, oitems), oitems)) { AnnounceChanges(oitems, nitems, changes); return default; } else { sw.SpinOnce(); } } } public Unit Subtract(HashMap rhs) { if (rhs.IsEmpty) return default; SpinWait sw = default; while (true) { var oitems = this.Items; var onChange = Change; var (nitems, changes) = onChange == null ? (oitems.Subtract(rhs.Value), null) : oitems.SubtractWithLog(rhs.Value); if (ReferenceEquals(Interlocked.CompareExchange(ref this.Items, nitems, oitems), oitems)) { AnnounceChanges(oitems, nitems, changes); return default; } else { sw.SpinOnce(); } } } /// /// Returns True if 'other' is a proper subset of this set /// /// True if 'other' is a proper subset of this set [Pure] public bool IsProperSubsetOf(IEnumerable<(K Key, V Value)> other) => Items.IsProperSubsetOf(other); /// /// Returns True if 'other' is a proper subset of this set /// /// True if 'other' is a proper subset of this set [Pure] public bool IsProperSubsetOf(IEnumerable other) => Items.IsProperSubsetOf(other); /// /// Returns True if 'other' is a proper superset of this set /// /// True if 'other' is a proper superset of this set [Pure] public bool IsProperSupersetOf(IEnumerable<(K Key, V Value)> other) => Items.IsProperSupersetOf(other); /// /// Returns True if 'other' is a proper superset of this set /// /// True if 'other' is a proper superset of this set [Pure] public bool IsProperSupersetOf(IEnumerable other) => Items.IsProperSupersetOf(other); /// /// Returns True if 'other' is a superset of this set /// /// True if 'other' is a superset of this set [Pure] public bool IsSubsetOf(IEnumerable<(K Key, V Value)> other) => Items.IsSubsetOf(other); /// /// Returns True if 'other' is a superset of this set /// /// True if 'other' is a superset of this set [Pure] public bool IsSubsetOf(IEnumerable other) => Items.IsSubsetOf(other); /// /// Returns True if 'other' is a superset of this set /// /// True if 'other' is a superset of this set [Pure] public bool IsSubsetOf(HashMap other) => Items.IsSubsetOf(other.Value); /// /// Returns True if 'other' is a superset of this set /// /// True if 'other' is a superset of this set [Pure] public bool IsSupersetOf(IEnumerable<(K Key, V Value)> other) => Items.IsSupersetOf(other); /// /// Returns True if 'other' is a superset of this set /// /// True if 'other' is a superset of this set [Pure] public bool IsSupersetOf(IEnumerable rhs) => Items.IsSupersetOf(rhs); /// /// Returns the elements that are in both this and other /// public Unit Intersect(IEnumerable rhs) { SpinWait sw = default; var srhs = toSeq(rhs); while (true) { var oitems = this.Items; var onChange = Change; var (nitems, changes) = onChange == null ? (oitems.Intersect(srhs), null) : oitems.IntersectWithLog(srhs); if (ReferenceEquals(Interlocked.CompareExchange(ref this.Items, nitems, oitems), oitems)) { AnnounceChanges(oitems, nitems, changes); return default; } else { sw.SpinOnce(); } } } /// /// Returns the elements that are in both this and other /// public Unit Intersect(IEnumerable<(K Key, V Value)> rhs) { SpinWait sw = default; var srhs = new TrieMap, K, V>(rhs); while (true) { var oitems = this.Items; var onChange = Change; var (nitems, changes) = onChange == null ? (oitems.Intersect(srhs), null) : oitems.IntersectWithLog(srhs); if (ReferenceEquals(Interlocked.CompareExchange(ref this.Items, nitems, oitems), oitems)) { AnnounceChanges(oitems, nitems, changes); return default; } else { sw.SpinOnce(); } } } /// /// Returns the elements that are in both this and other /// public Unit Intersect(IEnumerable<(K Key, V Value)> rhs, WhenMatched Merge) { SpinWait sw = default; var srhs = new TrieMap, K, V>(rhs); while (true) { var oitems = this.Items; var onChange = Change; var (nitems, changes) = onChange == null ? (oitems.Intersect(srhs, Merge), null) : oitems.IntersectWithLog(srhs, Merge); if (ReferenceEquals(Interlocked.CompareExchange(ref this.Items, nitems, oitems), oitems)) { AnnounceChanges(oitems, nitems, changes); return default; } else { sw.SpinOnce(); } } } /// /// Returns the elements that are in both this and other /// public Unit Intersect(HashMap rhs, WhenMatched Merge) { SpinWait sw = default; while (true) { var oitems = this.Items; var onChange = Change; var (nitems, changes) = onChange == null ? (oitems.Intersect(rhs.Value, Merge), null) : oitems.IntersectWithLog(rhs.Value, Merge); if (ReferenceEquals(Interlocked.CompareExchange(ref this.Items, nitems, oitems), oitems)) { AnnounceChanges(oitems, nitems, changes); return default; } else { sw.SpinOnce(); } } } /// /// Returns True if other overlaps this set /// [Pure] public bool Overlaps(IEnumerable<(K Key, V Value)> other) => Items.Overlaps(other); /// /// Returns True if other overlaps this set /// [Pure] public bool Overlaps(IEnumerable other) => Items.Overlaps(other); /// /// Returns this - rhs. Only the items in this that are not in /// rhs will be returned. /// public Unit Except(IEnumerable rhs) { SpinWait sw = default; var srhs = toSeq(rhs); while (true) { var oitems = this.Items; var onChange = Change; var (nitems, changes) = onChange == null ? (oitems.Except(srhs), null) : oitems.ExceptWithLog(srhs); if (ReferenceEquals(Interlocked.CompareExchange(ref this.Items, nitems, oitems), oitems)) { AnnounceChanges(oitems, nitems, changes); return default; } else { sw.SpinOnce(); } } } /// /// Returns this - other. Only the items in this that are not in /// other will be returned. /// public Unit Except(IEnumerable<(K Key, V Value)> rhs) { SpinWait sw = default; var srhs = toSeq(rhs); while (true) { var oitems = this.Items; var onChange = Change; var (nitems, changes) = onChange == null ? (oitems.Except(srhs), null) : oitems.ExceptWithLog(srhs); if (ReferenceEquals(Interlocked.CompareExchange(ref this.Items, nitems, oitems), oitems)) { AnnounceChanges(oitems, nitems, changes); return default; } else { sw.SpinOnce(); } } } /// /// Only items that are in one set or the other will be returned. /// If an item is in both, it is dropped. /// public Unit SymmetricExcept(HashMap rhs) { SpinWait sw = default; while (true) { var oitems = this.Items; var onChange = Change; var (nitems, changes) = onChange == null ? (oitems.SymmetricExcept(rhs), null) : oitems.SymmetricExceptWithLog(rhs); if (ReferenceEquals(Interlocked.CompareExchange(ref this.Items, nitems, oitems), oitems)) { AnnounceChanges(oitems, nitems, changes); return default; } else { sw.SpinOnce(); } } } /// /// Only items that are in one set or the other will be returned. /// If an item is in both, it is dropped. /// public Unit SymmetricExcept(IEnumerable<(K Key, V Value)> rhs) { SpinWait sw = default; var srhs = toSeq(rhs); while (true) { var oitems = this.Items; var onChange = Change; var (nitems, changes) = onChange == null ? (oitems.SymmetricExcept(srhs), null) : oitems.SymmetricExceptWithLog(srhs); if (ReferenceEquals(Interlocked.CompareExchange(ref this.Items, nitems, oitems), oitems)) { AnnounceChanges(oitems, nitems, changes); return default; } else { sw.SpinOnce(); } } } /// /// Finds the union of two sets and produces a new set with /// the results /// /// Other set to union with /// A set which contains all items from both sets public Unit Union(IEnumerable<(K, V)> rhs) { SpinWait sw = default; var srhs = toSeq(rhs); if (srhs.IsEmpty) return unit; while (true) { var oitems = this.Items; var onChange = Change; var (nitems, changes) = onChange == null ? (oitems.Union(srhs), null) : oitems.UnionWithLog(srhs); if (ReferenceEquals(Interlocked.CompareExchange(ref this.Items, nitems, oitems), oitems)) { AnnounceChanges(oitems, nitems, changes); return default; } else { sw.SpinOnce(); } } } /// /// Union two maps. /// /// /// The `WhenMatched` merge function is called when keys are present in both map to allow resolving to a /// sensible value. /// public Unit Union(IEnumerable<(K Key, V Value)> rhs, WhenMatched Merge) { SpinWait sw = default; var srhs = toSeq(rhs); if (srhs.IsEmpty) return unit; while (true) { var oitems = this.Items; var onChange = Change; var (nitems, changes) = onChange == null ? (oitems.Union(srhs, Merge), null) : oitems.UnionWithLog(srhs, Merge); if (ReferenceEquals(Interlocked.CompareExchange(ref this.Items, nitems, oitems), oitems)) { AnnounceChanges(oitems, nitems, changes); return default; } else { sw.SpinOnce(); } } } /// /// Union two maps. /// /// /// The `WhenMatched` merge function is called when keys are present in both map to allow resolving to a /// sensible value. /// /// /// The `WhenMissing` function is called when there is a key in the right-hand side, but not the left-hand-side. /// This allows the `V2` value-type to be mapped to the target `V` value-type. /// public Unit Union(IEnumerable<(K Key, W Value)> rhs, WhenMissing MapRight, WhenMatched Merge) { SpinWait sw = default; var srhs = toSeq(rhs); if (srhs.IsEmpty) return unit; while (true) { var oitems = this.Items; var onChange = Change; var (nitems, changes) = onChange == null ? (oitems.Union(srhs, MapRight, Merge), null) : oitems.UnionWithLog(srhs, MapRight, Merge); if (ReferenceEquals(Interlocked.CompareExchange(ref this.Items, nitems, oitems), oitems)) { AnnounceChanges(oitems, nitems, changes); return default; } else { sw.SpinOnce(); } } } /// /// Equality of keys and values with `EqDefault〈V〉` used for values /// [Pure] public override bool Equals(object? obj) => obj is AtomHashMap hm && Equals(hm); /// /// Equality of keys and values with `EqDefault〈V〉` used for values /// [Pure] public bool Equals(AtomHashMap? other) => other is not null && Items.Equals(other.Items); /// /// Equality of keys and values with `EqDefault〈V〉` used for values /// [Pure] public bool Equals(HashMap other) => Items.Equals(other.Value); /// /// Equality of keys only /// [Pure] public bool EqualsKeys(AtomHashMap other) => Items.Equals>(other.Items); /// /// Equality of keys only /// [Pure] public bool EqualsKeys(HashMap other) => Items.Equals>(other.Value); [Pure] public override int GetHashCode() => Items.GetHashCode(); /// /// Atomically maps the map to a new map /// /// Mapped items in a new map [Pure] public AtomHashMap Select(Func f) => new (Items.Map(f)); /// /// Atomically maps the map to a new map /// /// Mapped items in a new map [Pure] public AtomHashMap Select(Func f) => new (Items.Map(f)); /// /// Atomically filter out items that return false when a predicate is applied /// /// Predicate /// New map with items filtered [Pure] [EditorBrowsable(EditorBrowsableState.Never)] public AtomHashMap Where(Func pred) => new (Items.Filter(pred)); /// /// Atomically filter out items that return false when a predicate is applied /// /// Predicate /// New map with items filtered [Pure] [EditorBrowsable(EditorBrowsableState.Never)] public AtomHashMap Where(Func pred) => new (Items.Filter(pred)); /// /// Return true if all items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] public bool ForAll(Func pred) { foreach (var (key, value) in AsIterable()) { if (!pred(key, value)) return false; } return true; } /// /// Return true if all items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] public bool ForAll(Func<(K Key, V Value), bool> pred) => AsIterable().Map(kv => (kv.Key, kv.Value)).ForAll(pred); /// /// Return true if all items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] public bool ForAll(Func pred) => Values.ForAll(pred); /// /// Return true if *any* items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied public bool Exists(Func pred) { foreach (var (key, value) in AsIterable()) { if (pred(key, value)) return true; } return false; } /// /// Return true if *any* items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] public bool Exists(Func<(K Key, V Value), bool> pred) => AsIterable().Map(kv => (kv.Key, kv.Value)).Exists(pred); /// /// Return true if *any* items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] public bool Exists(Func, bool> pred) => AsIterable().Map(kv => new KeyValuePair(kv.Key, kv.Value)).Exists(pred); /// /// Return true if *any* items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] public bool Exists(Func pred) => Values.Exists(pred); /// /// Atomically iterate through all key/value pairs in the map (in order) and execute an /// action on each /// /// Action to execute /// Unit public Unit Iter(Action action) { foreach (var (key, value) in this) { action(key, value); } return unit; } /// /// Atomically iterate through all values in the map (in order) and execute an /// action on each /// /// Action to execute /// Unit public Unit Iter(Action action) { foreach (var item in this) { action(item.Value); } return unit; } /// /// Atomically iterate through all key/value pairs (as tuples) in the map (in order) /// and execute an action on each /// /// Action to execute /// Unit public Unit Iter(Action<(K Key, V Value)> action) { foreach (var item in this) { action(item); } return unit; } /// /// Atomically iterate through all key/value pairs in the map (in order) and execute an /// action on each /// /// Action to execute /// Unit public Unit Iter(Action> action) { foreach (var item in this) { action(new KeyValuePair(item.Key, item.Value)); } return unit; } /// /// Atomically folds all items in the map (in order) using the folder function provided. /// /// State type /// Initial state /// Fold function /// Folded state [Pure] public S Fold(S state, Func folder) => AsIterable().Fold(state, (s, x) => folder(s, x.Key, x.Value)); /// /// Atomically folds all items in the map (in order) using the folder function provided. /// /// State type /// Initial state /// Fold function /// Folded state [Pure] public S Fold(S state, Func folder) => Values.Fold(state, folder); [MethodImpl(MethodImplOptions.AggressiveInlining)] void AnnounceChange(TrieMap, K, V> prev, TrieMap, K, V> current, K key, Change? change) { if (change?.HasChanged ?? false) { Change?.Invoke(new HashMapPatch(prev, current, key, change)); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] void AnnounceChanges(TrieMap, K, V> prev, TrieMap, K, V> current, TrieMap, K, Change>? changes) { if (changes is not null) { Change?.Invoke(new HashMapPatch(prev, current, changes)); } } ///////////////////////////////////////////////////////////////////////////////////////////// // // IReadOnlyDictionary [Pure] public IDictionary ToDictionary( Func<(K Key, V Value), KR> keySelector, Func<(K Key, V Value), VR> valueSelector) where KR : notnull => AsIterable().ToDictionary(keySelector, valueSelector); [Pure] public bool TryGetValue(K key, out V value) { var v = Find(key); if (v.IsSome) { value = (V)v; return true; } else { value = default!; return false; } } [Pure] IEnumerator> IEnumerable>.GetEnumerator() => AsIterable() .Select(p => new KeyValuePair(p.Key, p.Value)) // ReSharper disable once NotDisposedResourceIsReturned .GetEnumerator() ; [Pure] IEnumerable IReadOnlyDictionary.Keys => Keys; [Pure] IEnumerable IReadOnlyDictionary.Values => Values; [Pure] public IReadOnlyDictionary ToReadOnlyDictionary() => this; } ================================================ FILE: LanguageExt.Core/Concurrency/AtomHashMap/Events.cs ================================================ using LanguageExt.Traits; namespace LanguageExt { /// /// Event sent from the `AtomHashMap` type whenever an operation successfully modifies the underlying data /// /// Key type /// Value type public delegate void AtomHashMapChangeEvent(HashMapPatch Patch); /// /// Event sent from the `AtomHashMap` type whenever an operation successfully modifies the underlying data /// /// Key type /// Value type public delegate void AtomHashMapChangeEvent(HashMapPatch Patch) where EqK : Eq; } ================================================ FILE: LanguageExt.Core/Concurrency/AtomQue/AtomQue.Extensions.cs ================================================ using System; using System.Threading; using System.Collections; using System.Collections.Generic; using static LanguageExt.Prelude; using LanguageExt.ClassInstances; using System.Diagnostics.Contracts; using System.Runtime.CompilerServices; using LanguageExt.Traits; namespace LanguageExt; public static class AtomQueExtensions { public static AtomQue As(this K ma) => (AtomQue)ma; } ================================================ FILE: LanguageExt.Core/Concurrency/AtomQue/AtomQue.Trait.Implementation.cs ================================================ using System; using System.Linq; using LanguageExt.Traits; namespace LanguageExt; public class AtomQue : Foldable { static S Foldable.FoldWhile( Func> f, Func<(S State, A Value), bool> predicate, S state, K ta) { foreach (var item in ta.As()) { if (predicate((state, item))) return state; state = f(item)(state); } return state; } static S Foldable.FoldBackWhile(Func> f, Func<(S State, A Value), bool> predicate, S state, K ta) { foreach (var item in ta.As().Reverse()) { if (predicate((state, item))) return state; state = f(state)(item); } return state; } static Fold Foldable.FoldStep(K ta, S initialState) { var iter = ta.As().GetEnumerator(); return go(initialState); Fold go(S state) => iter.MoveNext() ? Fold.Loop(state, iter.Current, go) : Fold.Done(state); } static Fold Foldable.FoldStepBack(K ta, S initialState) { var iter = ta.As().Reverse().GetEnumerator(); return go(initialState); Fold go(S state) => iter.MoveNext() ? Fold.Loop(state, iter.Current, go) : Fold.Done(state); } } ================================================ FILE: LanguageExt.Core/Concurrency/AtomQue/AtomQue.cs ================================================ using System; using System.Threading; using System.Collections; using System.Collections.Generic; using static LanguageExt.Prelude; using LanguageExt.ClassInstances; using System.Diagnostics.Contracts; using System.Runtime.CompilerServices; using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt; /// /// Event thrown when an item is dequeued from an `AtomQue` /// /// Item that was dequeued public delegate void AtomDequeuedEvent(A value); /// /// Event thrown when an item is enqueued to an `AtomQue` /// /// Item that was enqueued public delegate void AtomEnqueuedEvent(A value); /// /// Atoms provide a way to manage shared, synchronous, independent state without /// locks. `AtomQue` wraps the language-ext `Que`, and makes sure all operations are atomic and thread-safe /// without resorting to locking. /// /// /// See the [concurrency section](https://github.com/louthy/language-ext/wiki/Concurrency) of the wiki for more info. /// /// Item value type public class AtomQue : IEquatable>, IEquatable>, IEnumerable, K { QueInternal items; public event AtomDequeuedEvent? Dequeued; public event AtomEnqueuedEvent? Enqueued; internal AtomQue() => items = QueInternal.Empty; internal AtomQue(IEnumerable items) => this.items = new QueInternal(items); internal AtomQue(Que items) => this.items = new QueInternal(items); /// /// Impure iteration of the bound value in the structure /// /// /// Returns the original unmodified structure /// public Unit Do(Action f) { this.Iter(f); return default; } /// /// Reference version for use in pattern-matching /// /// /// /// Empty collection = null /// Singleton collection = A /// More = (A, Seq〈A〉) -- head and tail /// /// var res = list.Case switch /// { /// /// (var x, var xs) => ..., /// A value => ..., /// _ => ... /// } /// /// [Pure] public object? Case => IsEmpty ? null : toSeq(items).Case; /// /// Is the queue empty /// [Pure] public bool IsEmpty { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => items.IsEmpty; } /// /// Number of items in the queue /// [Pure] public int Count { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => items.Count; } /// /// Alias of Count /// [Pure] public int Length { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => items.Count; } /// /// Clears the queue atomically /// public Unit Clear() { SpinWait sw = default; while (true) { var oitems = items; if (oitems.IsEmpty) return default; var nitems = new QueInternal(); if (ReferenceEquals(Interlocked.CompareExchange(ref items, nitems, oitems), oitems)) { if (Dequeued != null) { foreach (var x in oitems) { Dequeued?.Invoke(x); } } return default; } else { sw.SpinOnce(); } } } /// /// Look at the item at the front of the queue, if it exists. /// /// The item at the front of the queue, if it exists. `None` otherwise. [Pure] public Option Peek() { var xs = items; return xs.IsEmpty ? None : xs.Peek(); } /// /// Removes the item from the front of the queue atomically /// /// The item that was at the front of the queue (if it existed, `None` otherwise) public Option Dequeue() { SpinWait sw = default; while (true) { var oitems = items; if (oitems.IsEmpty) return default; var top = oitems.Peek(); var nitems = oitems.Dequeue(); if (ReferenceEquals(Interlocked.CompareExchange(ref items, nitems, oitems), oitems)) { Dequeued?.Invoke(top); return top; } else { sw.SpinOnce(); } } } /// /// Removes the item from the front of the queue atomically /// /// If the queue is empty /// The item that was at the front of the queue, or an `InvalidOperationException` exception public A DequeueUnsafe() { SpinWait sw = default; while (true) { var oitems = items; if (oitems.IsEmpty) throw Exceptions.SequenceEmpty; var top = oitems.Peek(); var nitems = oitems.Dequeue(); if (ReferenceEquals(Interlocked.CompareExchange(ref items, nitems, oitems), oitems)) { Dequeued?.Invoke(top); return top; } else { sw.SpinOnce(); } } } /// /// Add an item to the end of the queue atomically /// /// Value to add to the queue public Unit Enqueue(A value) { SpinWait sw = default; while (true) { var oitems = items; var nitems = oitems.Enqueue(value); if (ReferenceEquals(Interlocked.CompareExchange(ref items, nitems, oitems), oitems)) { Enqueued?.Invoke(value); return default; } else { sw.SpinOnce(); } } } [Pure] public Seq ToSeq() => items.ToSeq(); [Pure] public Iterable AsIterable() => items.AsIterable(); [Pure] public IEnumerator GetEnumerator() => // ReSharper disable once NotDisposedResourceIsReturned AsIterable().GetEnumerator(); [Pure] IEnumerator IEnumerable.GetEnumerator() => // ReSharper disable once NotDisposedResourceIsReturned AsIterable().GetEnumerator(); [Pure] public Unit Append(Que rhs) { SpinWait sw = default; while (true) { var oitems = items; var nitems = oitems.Combine(rhs.Value); if (ReferenceEquals(Interlocked.CompareExchange(ref items, nitems, oitems), oitems)) { if (Enqueued != null) { foreach (var item in rhs.Value) { Enqueued?.Invoke(item); } } return default; } else { sw.SpinOnce(); } } } [Pure] public Unit Append(AtomQue rhs) { var ritems = rhs.items; SpinWait sw = default; while (true) { var oitems = items; var nitems = oitems.Combine(ritems); if (ReferenceEquals(Interlocked.CompareExchange(ref items, nitems, oitems), oitems)) { if (Enqueued != null) { foreach (var item in ritems) { Enqueued?.Invoke(item); } } return default; } else { sw.SpinOnce(); } } } [Pure] public static bool operator ==(AtomQue lhs, AtomQue rhs) => lhs.Equals(rhs); [Pure] public static bool operator ==(AtomQue lhs, Que rhs) => lhs.Equals(rhs); [Pure] public static bool operator ==(Que lhs, AtomQue rhs) => rhs.Equals(lhs); [Pure] public static bool operator !=(AtomQue lhs, AtomQue rhs) => !(lhs == rhs); [Pure] public static bool operator !=(Que lhs, AtomQue rhs) => !(lhs == rhs); [Pure] public static bool operator !=(AtomQue lhs, Que rhs) => !(lhs == rhs); [Pure] public override int GetHashCode() => items.GetHashCode(); [Pure] public override bool Equals(object? obj) => obj switch { AtomQue q => this == q, Que q => this == q, _ => false }; [Pure] public bool Equals(AtomQue? other) => other is not null && GetHashCode() == other.GetHashCode() && EqEnumerable.Equals(items, other.items); [Pure] public bool Equals(Que other) => GetHashCode() == other.GetHashCode() && EqEnumerable.Equals(items, other.Value); } ================================================ FILE: LanguageExt.Core/Concurrency/AtomSeq/AtomSeq.cs ================================================ using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics.Contracts; using System.Linq; using System.Runtime.CompilerServices; using System.Threading; using LanguageExt.ClassInstances; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; /// /// Atoms provide a way to manage shared, synchronous, independent state without /// locks. `AtomSeq` wraps the language-ext `Seq`, and makes sure all operations are atomic and thread-safe /// without resorting to locking /// /// /// See the [concurrency section](https://github.com/louthy/language-ext/wiki/Concurrency) of the wiki for more info. /// /// Item value type public class AtomSeq : IComparable>, IEquatable>, IComparable>, IEquatable>, IEnumerable, IComparable { /// /// Empty sequence /// public static AtomSeq Empty => new (SeqEmptyInternal.Default); /// /// Internal representation of the sequence (SeqStrict|SeqLazy|SeqEmptyInternal) /// volatile ISeqInternal items; /// /// Constructor from lazy sequence /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public AtomSeq(IEnumerable ma) : this(new SeqLazy(ma)) { } /// /// Constructor /// [MethodImpl(MethodImplOptions.AggressiveInlining)] internal AtomSeq(ISeqInternal items) => this.items = items; /// /// Convert to an immutable sequence /// /// This is effectively a zero cost operation, not even a single allocation [Pure] public Seq ToSeq() => new (items); /// /// Reference version for use in pattern-matching /// [Pure] public object? Case { get { var xs = items; return xs.IsEmpty ? null : xs.Tail.IsEmpty ? xs.Head : (xs.Head, xs.Tail); } } public void Deconstruct(out A head, out Seq tail) { var xs = items; head = xs.Head; tail = new Seq(xs.Tail); } /// /// Indexer /// public A this[int index] { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => items[index]; } /// /// Atomically swap the underlying Seq. Allows for multiple operations on the Seq in an entirely /// transactional and atomic way. /// /// Swap function, maps the current state of the AtomSeq to a new state /// Any functions passed as arguments may be run multiple times if there are multiple threads competing /// to update this data structure. Therefore the functions must spend as little time performing the injected /// behaviours as possible to avoid repeated attempts public IO SwapIO(Func, Seq> swap) => IO.lift(_ => Swap(swap)); /// /// Atomically swap the underlying Seq. Allows for multiple operations on the Seq in an entirely /// transactional and atomic way. /// /// Swap function, maps the current state of the AtomSeq to a new state /// Any functions passed as arguments may be run multiple times if there are multiple threads competing /// to update this data structure. Therefore the functions must spend as little time performing the injected /// behaviours as possible to avoid repeated attempts public Unit Swap(Func, Seq> swap) { SpinWait sw = default; while (true) { var oitems = items; var nitems = swap(new Seq(oitems)).Value; if(ReferenceEquals(oitems, nitems)) { // no change return default; } if (ReferenceEquals(Interlocked.CompareExchange(ref items, nitems, oitems), oitems)) { return default; } else { sw.SpinOnce(); } } } /// /// Atomically swap the underlying Seq. Allows for multiple operations on the Seq in an entirely /// transactional and atomic way. /// /// Swap function, maps the current state of the AtomSeq to a new state /// Any functions passed as arguments may be run multiple times if there are multiple threads competing /// to update this data structure. Therefore the functions must spend as little time performing the injected /// behaviours as possible to avoid repeated attempts internal Unit SwapInternal(Func, ISeqInternal> swap) { SpinWait sw = default; while (true) { var oitems = items; var nitems = swap(oitems); if(ReferenceEquals(oitems, nitems)) { // no change return default; } if (ReferenceEquals(Interlocked.CompareExchange(ref items, nitems, oitems), oitems)) { return default; } else { sw.SpinOnce(); } } } /// /// Add an item to the end of the sequence /// /// /// Forces evaluation of the entire lazy sequence so the item /// can be appended /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public Unit Add(A value) { SpinWait sw = default; while (true) { var oitems = items; var nitems = oitems.Add(value); if (ReferenceEquals(Interlocked.CompareExchange(ref items, nitems, oitems), oitems)) { return default; } else { sw.SpinOnce(); } } } /// /// Add a range of items to the end of the sequence /// /// /// Forces evaluation of the entire lazy sequence so the items /// can be appended. /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Unit Concat(IEnumerable items) => items switch { Lst lst => Concat(lst), Set set => Concat(set), HashSet hset => Concat(hset), Arr arr => Concat(arr), Stck stck => Concat(stck), IReadOnlyList rolist => Concat(rolist), _ => Concat(toSeq(items)) }; /// /// Add a range of items to the end of the sequence /// /// /// Forces evaluation of the entire lazy sequence so the items /// can be appended. /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Unit Concat(Lst items) { if (items.Count == 0) { return default; } var arr = items.Value.ToArray(); return Concat(Seq.FromArray(arr)); } /// /// Add a range of items to the end of the sequence /// /// /// Forces evaluation of the entire lazy sequence so the items /// can be appended. /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Unit Concat(Set items) { if (items.Count == 0) { return default; } var arr = items.Value.ToArray(); return Concat(Seq.FromArray(arr)); } /// /// Add a range of items to the end of the sequence /// /// /// Forces evaluation of the entire lazy sequence so the items /// can be appended. /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Unit Concat(HashSet items) { if (items.Count == 0) { return default; } var arr = items.ToArray(); return Concat(Seq.FromArray(arr)); } /// /// Add a range of items to the end of the sequence /// /// /// Forces evaluation of the entire lazy sequence so the items /// can be appended. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public Unit Concat(Stck items) { if (items.Count == 0) { return default; } var arr = items.ToArray(); return Concat(Seq.FromArray(arr)); } /// /// Add a range of items to the end of the sequence /// /// /// Forces evaluation of the entire lazy sequence so the items /// can be appended. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public Unit Concat(IReadOnlyCollection items) { if (items.Count == 0) { return default; } var arr = items.ToArray(); return Concat(Seq.FromArray(arr)); } /// /// Add a range of items to the end of the sequence /// /// /// Forces evaluation of the entire lazy sequence so the items /// can be appended. /// public Unit Concat(Seq rhs) { SpinWait sw = default; while (true) { var oitems = items; var nitems = oitems.Type switch { SeqType.Empty => // lhs is empty, so just return rhs rhs.Value, SeqType.Lazy => rhs.Value.Type switch { // lhs lazy, rhs empty // return lhs SeqType.Empty => oitems, // lhs lazy, rhs lazy // return SeqConcat SeqType.Lazy => new SeqConcat(Seq(oitems, rhs.Value)), // lhs lazy, rhs strict // force lhs to be strict and concat the two SeqType.Strict => ((SeqStrict)oitems.Strict()).Append((SeqStrict)rhs.Value), // lhs lazy, rhs concat // prepend rhs with lhs SeqType.Concat => ((SeqConcat)rhs.Value).ConsSeq(oitems), _ => throw new NotSupportedException() }, SeqType.Strict => rhs.Value.Type switch { // lhs strict, rhs empty // return lhs SeqType.Empty => oitems, // lhs strict, rhs lazy // return SeqConcat SeqType.Lazy => new SeqConcat(Seq(oitems, rhs.Value)), // lhs strict, rhs strict // append the two SeqType.Strict => ((SeqStrict)oitems).Append((SeqStrict)rhs.Value), // lhs strict, rhs concat // prepend rhs with lhs SeqType.Concat => ((SeqConcat)rhs.Value).ConsSeq(oitems), _ => throw new NotSupportedException() }, SeqType.Concat => rhs.Value.Type switch { // lhs concat, rhs empty // return lhs SeqType.Empty => oitems, // lhs concat, rhs lazy || lhs concat, rhs strict // add rhs to concat SeqType.Lazy => ((SeqConcat)oitems).AddSeq(rhs.Value), SeqType.Strict => ((SeqConcat)oitems).AddSeq(rhs.Value), // lhs concat, rhs concat // add rhs to concat SeqType.Concat => ((SeqConcat)oitems).AddSeqRange(((SeqConcat)rhs.Value).ms), _ => throw new NotSupportedException() }, _ => throw new NotSupportedException() }; if (ReferenceEquals(oitems, nitems)) { // no change return default; } if (ReferenceEquals(Interlocked.CompareExchange(ref items, nitems, oitems), oitems)) { return default; } else { sw.SpinOnce(); } } } /// /// Prepend an item to the sequence /// internal Unit Cons(A value) { SpinWait sw = default; while (true) { var oitems = items; var nitems = oitems.Cons(value); if (ReferenceEquals(Interlocked.CompareExchange(ref items, nitems, oitems), oitems)) { return default; } else { sw.SpinOnce(); } } } /// /// Head item in the sequence. NOTE: If `IsEmpty` is true then Head /// is undefined. Call HeadOrNone() if for maximum safety. /// public A Head { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => items.Head; } /// /// Tail of the sequence /// public Seq Tail { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Seq(items.Tail); } /// /// Get all items except the last one /// public Seq Init { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Seq(items.Init); } /// /// Head of the sequence if this node isn't the empty node /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Option HeadOrNone() { var xs = items; return xs.IsEmpty ? None : Some(xs.Head); } /// /// Last item in sequence. Throws if no items in sequence /// public A Last { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => items.Last; } /// /// Last item in sequence. /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Option LastOrNone() { var xs = items; return xs.IsEmpty ? None : Some(xs.Last); } /// /// Last item in sequence. /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Either LastOrLeft(L Left) { var xs = items; return xs.IsEmpty ? Either.Left(Left) : Either.Right(xs.Last); } /// /// Last item in sequence. /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Either LastOrLeft(Func Left) { var xs = items; return xs.IsEmpty ? Either.Left(Left()) : Either.Right(xs.Last); } /// /// Head of the sequence if this node isn't the empty node or left /// /// /// Left case /// Head of the sequence or left [Pure] public Either HeadOrLeft(L left) { var xs = items; return xs.IsEmpty ? Left(left) : Right(xs.Head); } /// /// Head of the sequence if this node isn't the empty node /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Either HeadOrLeft(Func Left) { var xs = items; return xs.IsEmpty ? Left(Left()) : Right(xs.Head); } /// /// Returns true if the sequence is empty /// /// /// For lazy streams this will have to peek at the first /// item. So, the first item will be consumed. /// public bool IsEmpty { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => items.IsEmpty; } /// /// Returns the number of items in the sequence /// /// Number of items in the sequence public int Count { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => items.Count; } /// /// Alias of `Count` /// /// Number of items in the sequence public int Length { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => items.Count; } /// /// Stream as an enumerable /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Iterable AsIterable() => items.AsIterable(); /// /// Match empty sequence, or multi-item sequence /// /// Return value type /// Match for an empty list /// Match for a non-empty /// Result of match function invoked [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public B Match(Func Empty, Func, B> Tail) { var xs = items; return xs.IsEmpty ? Empty() : Tail(xs.Head, new Seq(xs.Tail)); } /// /// Match empty sequence, or one item sequence, or multi-item sequence /// /// Return value type /// Match for an empty list /// Match for a non-empty /// Result of match function invoked [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public B Match( Func Empty, Func Head, Func, B> Tail) { var xs = items; return xs.IsEmpty ? Empty() : xs.Tail.IsEmpty ? Head(xs.Head) : Tail(xs.Head, new Seq(xs.Tail)); } /// /// Match empty sequence, or multi-item sequence /// /// Return value type /// Match for an empty list /// Match for a non-empty /// Result of match function invoked [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public B Match( Func Empty, Func, B> Seq) { var xs = items; return xs.IsEmpty ? Empty() : Seq(new Seq(xs)); } /// /// Match empty sequence, or one item sequence, or multi-item sequence /// /// Return value type /// Match for an empty list /// Match for a non-empty /// Result of match function invoked [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public B Match( Func Empty, Func Head, Func, B> Tail) { var xs = items; return xs.IsEmpty ? Empty() : xs.Tail.IsEmpty ? Head(xs.Head) : Tail(new Seq(xs.Tail)); } /// /// Impure iteration of the bound values in the structure /// /// /// Returns the original unmodified structure /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public Unit Iter(Action f) => items.Iter(f); /// /// Map the sequence using the function provided /// /// /// Mapping function /// Mapped sequence [Pure] public Seq Map(Func f) { return new Seq(new SeqLazy(Yield(items))); IEnumerable Yield(ISeqInternal items) { foreach (var item in items) { yield return f(item); } } } /// /// Map the sequence using the function provided /// /// /// Mapping function /// Mapped sequence /// Any functions passed as arguments may be run multiple times if there are multiple threads competing /// to update this data structure. Therefore the functions must spend as little time performing the injected /// behaviours as possible to avoid repeated attempts [Pure] public Unit MapInPlace(Func f) { SpinWait sw = default; while (true) { var oitems = items; var nitems = new SeqLazy(oitems.Select(f)); if (ReferenceEquals(Interlocked.CompareExchange(ref items, nitems, oitems), oitems)) { return default; } else { sw.SpinOnce(); } } } /// /// Map the sequence using the function provided /// /// /// Mapping function /// Mapped sequence [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Seq Select(Func f) => Map(f); /// /// Monadic bind (flatmap) of the sequence /// /// Bound return value type /// Bind function /// Flatmapped sequence [Pure] public Seq Bind(Func> f) { static IEnumerable Yield(ISeqInternal ma, Func> bnd) { foreach (var a in ma) { foreach (var b in bnd(a)) { yield return b; } } } return new Seq(Yield(items, f)); } /// /// Monadic bind (flatmap) of the sequence /// /// Bound return value type /// Bind function /// Flat-mapped sequence /// Any functions passed as arguments may be run multiple times if there are multiple threads competing /// to update this data structure. Therefore the functions must spend as little time performing the injected /// behaviours as possible to avoid repeated attempts public Unit BindInPlace(Func> f) { SpinWait sw = default; while (true) { var oitems = items; var nitems = new Seq(oitems).Bind(f); if (ReferenceEquals(Interlocked.CompareExchange(ref items, nitems.Value, oitems), oitems)) { return default; } else { sw.SpinOnce(); } } } /// /// Monadic bind (flatmap) of the sequence /// /// Bound return value type /// Bind function /// Flatmapped sequence [Pure] public Seq SelectMany(Func> bind, Func project) { static IEnumerable Yield(ISeqInternal ma, Func> bnd, Func prj) { foreach (var a in ma) { foreach (var b in bnd(a)) { yield return prj(a, b); } } } return new Seq(Yield(items, bind, project)); } /// /// Filter the items in the sequence /// /// Predicate to apply to the items /// Filtered sequence [Pure] public Seq Filter(Func f) { return new Seq(new SeqLazy(Yield(items, f))); static IEnumerable Yield(ISeqInternal items, Func f) { foreach (var item in items) { if (f(item)) { yield return item; } } } } /// /// Filter the items in the sequence /// /// Predicate to apply to the items /// Filtered sequence /// Any functions passed as arguments may be run multiple times if there are multiple threads competing /// to update this data structure. Therefore the functions must spend as little time performing the injected /// behaviours as possible to avoid repeated attempts public Unit FilterInPlace(Func f) { SpinWait sw = default; while (true) { var oitems = items; var nitems = new SeqLazy(oitems.Where(f)); if(ReferenceEquals(oitems, nitems)) { // no change return default; } if (ReferenceEquals(Interlocked.CompareExchange(ref items, nitems, oitems), oitems)) { return default; } else { sw.SpinOnce(); } } } /// /// Filter the items in the sequence /// /// Predicate to apply to the items /// Filtered sequence [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Seq Where(Func f) => Filter(f); /// /// Fold the sequence from the first item to the last /// /// State type /// Initial state /// Fold function /// Aggregated state [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public S Fold(S state, Func f) => items.Fold(state, f); /// /// Fold the sequence from the last item to the first. For /// sequences that are not lazy and are less than 5000 items /// long, FoldBackRec is called instead, because it is faster. /// /// State type /// Initial state /// Fold function /// Aggregated state [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public S FoldBack(S state, Func f) => items.FoldBack(state, f); /// /// Returns true if the supplied predicate returns true for any /// item in the sequence. False otherwise. /// /// Predicate to apply /// True if the supplied predicate returns true for any /// item in the sequence. False otherwise. [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Exists(Func f) => items.Exists(f); /// /// Returns true if the supplied predicate returns true for all /// items in the sequence. False otherwise. If there is an /// empty sequence then true is returned. /// /// Predicate to apply /// True if the supplied predicate returns true for all /// items in the sequence. False otherwise. If there is an /// empty sequence then true is returned. [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool ForAll(Func f) => items.ForAll(f); /// /// Returns true if the sequence has items in it /// /// True if the sequence has items in it [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Any() => !IsEmpty; /// /// Inject a value in between each item in the sequence /// /// Sequence to inject values into /// Item to inject /// Bound type /// A sequence with the values injected [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Unit Intersperse(A value) { SpinWait sw = default; while (true) { var oitems = items; var nitems = new SeqLazy(oitems.Intersperse(value)); if(ReferenceEquals(oitems, nitems)) { // no change return default; } if (ReferenceEquals(Interlocked.CompareExchange(ref items, nitems, oitems), oitems)) { return default; } else { sw.SpinOnce(); } } } /// /// Get the hash code for all of the items in the sequence, or 0 if empty /// /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public override int GetHashCode() => items.GetHashCode(); [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public int CompareTo(object? obj) => obj switch { AtomSeq s => CompareTo(s), Seq s => CompareTo(s), IEnumerable e => CompareTo(toSeq(e)), _ => 1 }; /// /// Format the collection as `[a, b, c, ...]` /// The elipsis is used for collections over 50 items /// To get a formatted string with all the items, use `ToFullString` /// or `ToFullArrayString`. /// [Pure] public override string ToString() => items is SeqLazy lz ? CollectionFormat.ToShortArrayString(lz) : CollectionFormat.ToShortArrayString(items, Count); /// /// Format the collection as `a, b, c, ...` /// [Pure] public string ToFullString(string separator = ", ") => CollectionFormat.ToFullString(items, separator); /// /// Format the collection as `[a, b, c, ...]` /// [Pure] public string ToFullArrayString(string separator = ", ") => CollectionFormat.ToFullArrayString(items, separator); /// /// Ordering operator /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator >(AtomSeq x, AtomSeq y) => x.CompareTo(y) > 0; /// /// Ordering operator /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator >=(AtomSeq x, AtomSeq y) => x.CompareTo(y) >= 0; /// /// Ordering operator /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator <(AtomSeq x, AtomSeq y) => x.CompareTo(y) < 0; /// /// Ordering operator /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator <=(AtomSeq x, AtomSeq y) => x.CompareTo(y) <= 0; /// /// Equality operator /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(AtomSeq x, AtomSeq y) => x.Equals(y); /// /// Non-equality operator /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(AtomSeq x, AtomSeq y) => !(x == y); /// /// Equality test /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public override bool Equals(object? obj) => obj switch { AtomSeq s => Equals(s), Seq s => Equals(s), IEnumerable e => Equals(toSeq(e)), _ => false }; /// /// Equality test /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(Seq rhs) => Equals>(rhs); /// /// Equality test /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(AtomSeq? rhs) => rhs is not null && Equals>(rhs); /// /// Equality test /// [Pure] public bool Equals(Seq rhs) where EqA : Eq { var lhs = items; // Differing lengths? if(lhs.Count != rhs.Count) return false; // If the hash code has been calculated on both sides then // check for differences if (lhs.GetHashCode() != rhs.GetHashCode()) { return false; } // Iterate through both sides using var iterA = lhs.GetEnumerator(); using var iterB = rhs.GetEnumerator(); while (iterA.MoveNext() && iterB.MoveNext()) { if (!EqA.Equals(iterA.Current, iterB.Current)) { return false; } } return true; } /// /// Equality test /// [Pure] public bool Equals(AtomSeq rhs) where EqA : Eq { var lhs = items; // Differing lengths? if(lhs.Count != rhs.Count) return false; // If the hash code has been calculated on both sides then // check for differences if (lhs.GetHashCode() != rhs.GetHashCode()) { return false; } // Iterate through both sides using var iterA = lhs.GetEnumerator(); using var iterB = rhs.GetEnumerator(); while (iterA.MoveNext() && iterB.MoveNext()) { if (!EqA.Equals(iterA.Current, iterB.Current)) { return false; } } return true; } /// /// Skip count items /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public Unit Skip(int amount) { SpinWait sw = default; while (true) { var oitems = items; var nitems = new SeqLazy(oitems.Skip(amount)); if(ReferenceEquals(oitems, nitems)) { // no change return default; } if (ReferenceEquals(Interlocked.CompareExchange(ref items, nitems, oitems), oitems)) { return default; } else { sw.SpinOnce(); } } } /// /// Take count items /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public Unit Take(int amount) { SpinWait sw = default; while (true) { var oitems = items; var nitems = new SeqLazy(oitems.Take(amount)); if(ReferenceEquals(oitems, nitems)) { // no change return default; } if (ReferenceEquals(Interlocked.CompareExchange(ref items, nitems, oitems), oitems)) { return default; } else { sw.SpinOnce(); } } } /// /// Iterate the sequence, yielding items if they match the predicate /// provided, and stopping as soon as one doesn't /// /// A new sequence with the first items that match the /// predicate /// Any functions passed as arguments may be run multiple times if there are multiple threads competing /// to update this data structure. Therefore the functions must spend as little time performing the injected /// behaviours as possible to avoid repeated attempts public Unit TakeWhile(Func pred) { SpinWait sw = default; while (true) { var oitems = items; var nitems = new SeqLazy(oitems.TakeWhile(pred)); if(ReferenceEquals(oitems, nitems)) { // no change return default; } if (ReferenceEquals(Interlocked.CompareExchange(ref items, nitems, oitems), oitems)) { return default; } else { sw.SpinOnce(); } } } /// /// Iterate the sequence, yielding items if they match the predicate /// provided, and stopping as soon as one doesn't. An index value is /// also provided to the predicate function. /// /// A new sequence with the first items that match the /// predicate /// Any functions passed as arguments may be run multiple times if there are multiple threads competing /// to update this data structure. Therefore the functions must spend as little time performing the injected /// behaviours as possible to avoid repeated attempts public Unit TakeWhile(Func pred) { SpinWait sw = default; while (true) { var oitems = items; var nitems = new SeqLazy(oitems.TakeWhile(pred)); if(ReferenceEquals(oitems, nitems)) { // no change return default; } if (ReferenceEquals(Interlocked.CompareExchange(ref items, nitems, oitems), oitems)) { return default; } else { sw.SpinOnce(); } } } /// /// Returns all initial segments of the sequence, shortest first /// /// /// Including the empty sequence /// /// /// /// Seq("a", "b", "c").Inits /// /// > Seq(Seq(), Seq("a"), Seq("a", "b"), Seq("a", "b", "c")) /// /// /// Initial segments of the sequence public Seq> Inits => [Seq()] + NonEmptyInits; /// /// Returns all initial segments of the sequence, shortest first. /// /// /// Not including the empty sequence /// /// /// /// Seq("a", "b", "c").Inits /// /// > Seq(Seq("a"), Seq("a", "b"), Seq("a", "b", "c")) /// /// /// Initial segments of the sequence public Seq> NonEmptyInits => ToSeq().NonEmptyInits; /// /// Returns all final segments of the argument, longest first. /// /// /// Including the empty sequence /// /// /// /// Seq("a", "b", "c").Tails /// /// > Seq(Seq("a", "b", "c"), Seq("a", "b"), Seq("a"), Seq()) /// /// /// Initial segments of the sequence public Seq> Tails => ToSeq().Tails; /// /// Returns all final segments of the argument, longest first. /// /// /// Not including the empty sequence /// /// /// /// Seq("a", "b", "c").Tails /// /// > Seq(Seq("a", "b", "c"), Seq("a", "b"), Seq("a")) /// /// /// Initial segments of the sequence public Seq> NonEmptyTails => ToSeq().NonEmptyTails; /// /// Compare to another sequence /// [Pure] public int CompareTo(Seq rhs) => CompareTo>(rhs); /// /// Compare to another sequence /// [Pure] public int CompareTo(AtomSeq? rhs) => rhs is null ? 1 : CompareTo>(rhs); /// /// Compare to another sequence /// [Pure] public int CompareTo(Seq rhs) where OrdA : Ord { var lhs = items; // Differing lengths? var cmp = lhs.Count.CompareTo(rhs.Count); if (cmp != 0) return cmp; // Iterate through both sides using var iterA = lhs.GetEnumerator(); using var iterB = rhs.GetEnumerator(); while (iterA.MoveNext() && iterB.MoveNext()) { cmp = OrdA.Compare(iterA.Current, iterB.Current); if (cmp != 0) return cmp; } return 0; } /// /// Compare to another sequence /// [Pure] public int CompareTo(AtomSeq rhs) where OrdA : Ord { var lhs = items; // Differing lengths? var cmp = lhs.Count.CompareTo(rhs.Count); if (cmp != 0) return cmp; // Iterate through both sides using var iterA = lhs.GetEnumerator(); using var iterB = rhs.GetEnumerator(); while (iterA.MoveNext() && iterB.MoveNext()) { cmp = OrdA.Compare(iterA.Current, iterB.Current); if (cmp != 0) return cmp; } return 0; } /// /// Force all items lazy to stream /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public Unit Strict() => ignore(items.Strict()); [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public IEnumerator GetEnumerator() => // ReSharper disable once NotDisposedResourceIsReturned items.GetEnumerator(); [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] IEnumerator IEnumerable.GetEnumerator() => // ReSharper disable once NotDisposedResourceIsReturned items.GetEnumerator(); [Pure] public Seq Cast() => ToSeq().Cast(); } public static class AtomSeqExtensions { /// /// Last item in sequence. /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Validation LastOrInvalid(this AtomSeq ma, F Fail) where F : Monoid { var xs = ma.ToSeq(); return xs.IsEmpty ? Validation.Fail(Fail) : Validation.Success((A)xs.Last); } /// /// Head of the sequence if this node isn't the empty node or fail /// /// Head of the sequence or fail [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Validation HeadOrInvalid(this AtomSeq ma, F Fail) where F : Monoid { var xs = ma.ToSeq(); return xs.IsEmpty ? Validation.Fail(Fail) : Pure((A)xs.Head); } /// /// Last item in sequence. /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Validation LastOrInvalid(this AtomSeq ma, Func Fail) where F : Monoid { var xs = ma.ToSeq(); return xs.IsEmpty ? Validation.Fail(Fail()) : Pure((A)xs.Last); } /// /// Head of the sequence if this node isn't the empty node or fail /// /// Head of the sequence or fail [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Validation HeadOrInvalid(this AtomSeq ma, Func Fail) where F : Monoid { var xs = ma.ToSeq(); return xs.IsEmpty ? Validation.Fail(Fail()) : Pure((A)xs.Head); } /// /// Last item in sequence. /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Validation LastOrInvalid(this AtomSeq ma) where F : Monoid { var xs = ma.ToSeq(); return xs.IsEmpty ? Fail(F.Empty) : Pure((A)xs.Last); } /// /// Head of the sequence if this node isn't the empty node or fail /// /// Head of the sequence or fail [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Validation HeadOrInvalid(this AtomSeq ma) where F : Monoid { var xs = ma.ToSeq(); return xs.IsEmpty ? Fail(F.Empty) : Pure((A)xs.Head); } } ================================================ FILE: LanguageExt.Core/Concurrency/Conflict.cs ================================================ namespace LanguageExt; /// /// Trait that defines how to deal with a conflict between two values /// /// Value type public interface Conflict { public static abstract (long TimeStamp, Option Value) Resolve((long TimeStamp, Option Value) Current, (long TimeStamp, Option Value) Proposed); } /// /// Last-write-wins conflict resolver /// public struct LastWriteWins : Conflict { public static (long TimeStamp, Option Value) Resolve((long TimeStamp, Option Value) Current, (long TimeStamp, Option Value) Proposed) => Proposed.TimeStamp >= Current.TimeStamp ? Proposed : Current; } /// /// First-write-wins conflict resolver /// public struct FirstWriteWins : Conflict { public static (long TimeStamp, Option Value) Resolve((long TimeStamp, Option Value) Current, (long TimeStamp, Option Value) Proposed) => Current.TimeStamp <= Proposed.TimeStamp ? Current : Proposed; } ================================================ FILE: LanguageExt.Core/Concurrency/Prelude.Concurrency.cs ================================================ using System; using System.Runtime.CompilerServices; namespace LanguageExt; public static partial class Prelude { /// /// Generates a new reference that can be used within a `sync` transaction /// /// `Refs` ensure safe shared use of mutable storage locations via a software transactional /// memory (STM) system. `Refs` are bound to a single storage location for their lifetime, /// and only allow mutation of that location to occur within a transaction. /// /// /// /// Transactions (within a `sync(() => ...)`) should be easy to understand if you’ve ever used database /// transactions - they ensure that all actions on Refs are atomic, consistent, and isolated. /// /// * **Atomic** - means that every change to Refs made within a transaction occurs or none do. /// * **Consistent** - means that each new value can be checked with a validator function before allowing /// the transaction to commit. /// * **Isolated** - means that no transaction sees the effects of any other transaction while it is /// running. /// /// Another feature common to STMs is that, should a transaction have a conflict while running, /// it is automatically retried. The language-ext STM uses multi-version concurrency control for /// snapshot and serialisable isolation. /// /// In practice, this means: /// /// All reads of Refs will see a consistent snapshot of the _Ref world_ as of the starting point /// of the transaction (its 'read point'). The transaction will see any changes it has made. /// This is called the in-transaction-value. /// /// All changes made to Refs during a transaction will appear to occur at a single point in the /// _Ref world_ timeline (its 'write point'). /// /// No changes will have been made by any other transactions to any Refs that have been modified /// by this transaction. /// /// * Readers will never block writers, or other readers. /// /// * Writers will never block readers. /// /// I/O and other activities with side-effects should be avoided in transactions, since transactions /// will be retried. /// /// If a constraint on the validity of a value of a Ref that is being changed depends upon the /// simultaneous value of a Ref that is not being changed, that second Ref can be protected from /// modification by running the `sync` transaction with `Isolation.Serialisable`. /// /// The language-ext STM is designed to work with the persistent collections (`Map`, `HashMap`, /// `Seq`, `Lst`, `Set, `HashSet` etc.), and it is strongly recommended that you use the language-ext /// collections as the values of your Refs. Since all work done in an STM transaction is speculative, /// it is imperative that there be a low cost to making copies and modifications. Persistent collections /// have free copies (just use the original, it can’t be changed), and 'modifications' share structure /// efficiently. In any case: /// /// The values placed in Refs must be, or be considered, **immutable**. Otherwise, this library can’t help you. /// /// /// See the [concurrency section](https://github.com/louthy/language-ext/wiki/Concurrency) of the wiki for more info. /// /// Initial value of the ref /// Validator that is called on the ref value just /// before any transaction is committed (within a `sync`) [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Ref Ref(A value, Func? validator = null) => STM.NewRef(value, validator); /// /// Snapshot isolation requires that nothing outside the transaction has written to any of the values that are /// *written-to within the transaction*. If anything does write to the values used within the transaction, then /// the transaction is rolled back and retried (using the latest 'world' state). /// /// /// Serialisable isolation requires that nothing outside the transaction has written to any of the values that /// are *read-from or written-to within the transaction*. If anything does read from or written to the values used /// within the transaction, then it is rolled back and retried (using the latest 'world' state). /// /// It is the strictest form of isolation, and the most likely to conflict; but protects against cross read/write /// inconsistencies. For example, if you have: /// /// var x = Ref(1); /// var y = Ref(2); /// /// snapshot(() => x.Value = y.Value + 1); /// /// Then something writing to `y` mid-way through the transaction would *not* cause the transaction to fail. /// Because `y` was only read-from, not written to. However, this: /// /// var x = Ref(1); /// var y = Ref(2); /// /// serial(() => x.Value = y.Value + 1); /// /// ... would fail if something wrote to `y`. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static R atomic(Func op, Isolation isolation = Isolation.Snapshot) => STM.DoTransaction(op, isolation); /// /// Run the op within a new transaction /// If a transaction is already running, then this becomes part of the parent transaction /// /// /// Snapshot isolation requires that nothing outside the transaction has written to any of the values that are /// *written-to within the transaction*. If anything does write to the values used within the transaction, then /// the transaction is rolled back and retried (using the latest 'world' state). /// /// /// Serialisable isolation requires that nothing outside the transaction has written to any of the values that /// are *read-from or written-to within the transaction*. If anything does read from or written to the values used /// within the transaction, then it is rolled back and retried (using the latest 'world' state). /// /// It is the strictest form of isolation, and the most likely to conflict; but protects against cross read/write /// inconsistencies. For example, if you have: /// /// var x = Ref(1); /// var y = Ref(2); /// /// snapshot(() => x.Value = y.Value + 1); /// /// Then something writing to `y` mid-way through the transaction would *not* cause the transaction to fail. /// Because `y` was only read-from, not written to. However, this: /// /// var x = Ref(1); /// var y = Ref(2); /// /// serial(() => x.Value = y.Value + 1); /// /// ... would fail if something wrote to `y`. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Unit atomic(Action op, Isolation isolation = Isolation.Snapshot) => STM.DoTransaction(() => { op(); return unit; }, isolation); /// /// Run the op within a new transaction /// If a transaction is already running, then this becomes part of the parent transaction /// /// /// Snapshot isolation requires that nothing outside the transaction has written to any of the values that are /// *written-to within the transaction*. If anything does write to the values used within the transaction, then /// the transaction is rolled back and retried (using the latest 'world' state). /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static R snapshot(Func op) => STM.DoTransaction(op, Isolation.Snapshot); /// /// Run the op within a new transaction /// If a transaction is already running, then this becomes part of the parent transaction /// /// /// Snapshot isolation requires that nothing outside the transaction has written to any of the values that are /// *written-to within the transaction*. If anything does write to the values used within the transaction, then /// the transaction is rolled back and retried (using the latest 'world' state). /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Unit snapshot(Action op) => STM.DoTransaction(() => { op(); return unit; }, Isolation.Snapshot); /// /// Run the op within a new transaction /// If a transaction is already running, then this becomes part of the parent transaction /// /// /// Serialisable isolation requires that nothing outside the transaction has written to any of the values that /// are *read-from or written-to within the transaction*. If anything does read from or written to the values used /// within the transaction, then it is rolled back and retried (using the latest 'world' state). /// /// It is the strictest form of isolation, and the most likely to conflict; but protects against cross read/write /// inconsistencies. For example, if you have: /// /// var x = Ref(1); /// var y = Ref(2); /// /// snapshot(() => x.Value = y.Value + 1); /// /// Then something writing to `y` mid-way through the transaction would *not* cause the transaction to fail. /// Because `y` was only read-from, not written to. However, this: /// /// var x = Ref(1); /// var y = Ref(2); /// /// serial(() => x.Value = y.Value + 1); /// /// ... would fail if something wrote to `y`. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static R serial(Func op) => STM.DoTransaction(op, Isolation.Snapshot); /// /// Run the op within a new transaction /// If a transaction is already running, then this becomes part of the parent transaction /// /// /// Serialisable isolation requires that nothing outside the transaction has written to any of the values that /// are *read-from or written-to within the transaction*. If anything does read from or written to the values used /// within the transaction, then it is rolled back and retried (using the latest 'world' state). /// /// It is the strictest form of isolation, and the most likely to conflict; but protects against cross read/write /// inconsistencies. For example, if you have: /// /// var x = Ref(1); /// var y = Ref(2); /// /// snapshot(() => x.Value = y.Value + 1); /// /// Then something writing to `y` mid-way through the transaction would *not* cause the transaction to fail. /// Because `y` was only read-from, not written to. However, this: /// /// var x = Ref(1); /// var y = Ref(2); /// /// serial(() => x.Value = y.Value + 1); /// /// ... would fail if something wrote to `y`. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Unit serial(Action op) => STM.DoTransaction(() => { op(); return unit; }, Isolation.Snapshot); /// /// Swap the old value for the new returned by `f` /// Must be run within a `sync` transaction /// /// `Ref` to process /// Function to update the `Ref` /// The value returned from `f` [MethodImpl(MethodImplOptions.AggressiveInlining)] public static A swap(Ref r, Func f) => r.Swap(f); /// /// Must be called in a transaction. Sets the in-transaction-value of /// ref to: /// /// `f(in-transaction-value-of-ref)` /// /// and returns the in-transaction-value when complete. /// /// At the commit point of the transaction, `f` is run *AGAIN* with the /// most recently committed value: /// /// `f(most-recently-committed-value-of-ref)` /// /// Thus `f` should be commutative, or, failing that, you must accept /// last-one-in-wins behavior. /// /// Commute allows for more concurrency than just setting the Ref's value /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static A commute(Ref r, Func f) => r.Commute(f); /// /// Atoms provide a way to manage shared, synchronous, independent state without /// locks. /// /// Initial value of the atom /// The constructed Atom /// /// The intended use of atom is to hold one an immutable data structure. You change /// the value by applying a function to the old value. This is done in an atomic /// manner by `Swap`. /// /// Internally, `Swap` reads the current value, applies the function to it, and /// attempts to `CompareExchange` it in. Since another thread may have changed the /// value in the intervening time, it may have to retry, and does so in a spin loop. /// /// The net effect is that the value will always be the result of the application /// of the supplied function to a current value, atomically. However, because the /// function might be called multiple times, it must be free of side effects. /// /// Atoms are an efficient way to represent some state that will never need to be /// coordinated with any other, and for which you wish to make synchronous changes. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Atom Atom(A value) => LanguageExt.Atom.New(value); /// /// Atoms provide a way to manage shared, synchronous, independent state without /// locks. /// /// Initial value of the atom /// Function to run on the value after each state change. /// /// If the function returns false for any proposed new state, then the `swap` /// function will return `false`, else it will return `true` on successful setting /// of the atom's state /// /// The constructed Atom or None if the validation faled for the initial /// `value` /// /// The intended use of atom is to hold one an immutable data structure. You change /// the value by applying a function to the old value. This is done in an atomic /// manner by `Swap`. /// /// Internally, `Swap` reads the current value, applies the function to it, and /// attempts to `CompareExchange` it in. Since another thread may have changed the /// value in the intervening time, it may have to retry, and does so in a spin loop. /// /// The net effect is that the value will always be the result of the application /// of the supplied function to a current value, atomically. However, because the /// function might be called multiple times, it must be free of side effects. /// /// Atoms are an efficient way to represent some state that will never need to be /// coordinated with any other, and for which you wish to make synchronous changes. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Option> Atom(A value, Func validator) => LanguageExt.Atom.New(value, validator); /// /// Atoms provide a way to manage shared, synchronous, independent state without /// locks. /// /// Metadata to be passed to the validation function /// Initial value of the atom /// The constructed Atom /// /// The intended use of atom is to hold one an immutable data structure. You change /// the value by applying a function to the old value. This is done in an atomic /// manner by `Swap`. /// /// Internally, `Swap` reads the current value, applies the function to it, and /// attempts to `CompareExchange` it in. Since another thread may have changed the /// value in the intervening time, it may have to retry, and does so in a spin loop. /// /// The net effect is that the value will always be the result of the application /// of the supplied function to a current value, atomically. However, because the /// function might be called multiple times, it must be free of side effects. /// /// Atoms are an efficient way to represent some state that will never need to be /// coordinated with any other, and for which you wish to make synchronous changes. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Atom Atom(M metadata, A value) => LanguageExt.Atom.New(metadata, value); /// /// Atoms provide a way to manage shared, synchronous, independent state without /// locks. /// /// Metadata to be passed to the validation function /// Initial value of the atom /// Function to run on the value after each state change. /// /// If the function returns false for any proposed new state, then the `swap` /// function will return `false`, else it will return `true` on successful setting /// of the atom's state /// /// The constructed Atom or None if the validation faled for the initial /// `value` /// /// The intended use of atom is to hold one an immutable data structure. You change /// the value by applying a function to the old value. This is done in an atomic /// manner by `Swap`. /// /// Internally, `Swap` reads the current value, applies the function to it, and /// attempts to `CompareExchange` it in. Since another thread may have changed the /// value in the intervening time, it may have to retry, and does so in a spin loop. /// /// The net effect is that the value will always be the result of the application /// of the supplied function to a current value, atomically. However, because the /// function might be called multiple times, it must be free of side effects. /// /// Atoms are an efficient way to represent some state that will never need to be /// coordinated with any other, and for which you wish to make synchronous changes. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Option> Atom(M metadata, A value, Func validator) => LanguageExt.Atom.New(metadata, value, validator); /// /// Atomically updates the value by passing the old value to `f` and updating /// the atom with the result. Note: `f` may be called multiple times, so it /// should be free of side effects. /// /// Function to update the atom /// /// If the swap operation succeeded then a snapshot of the value that was set is returned. /// If the swap operation fails (which can only happen due to its validator returning false), /// then a snapshot of the current value within the Atom is returned. /// If there is no validator for the Atom then the return value is always the snapshot of /// the successful `f` function. /// public static A swap(Atom ma, Func f) => ma.Swap(f); /// /// Atomically updates the value by passing the old value to `f` and updating /// the atom with the result. Note: `f` may be called multiple times, so it /// should be free of side effects. /// /// Function to update the atom /// /// * If `f` returns `None` then no update occurs and the result of the call /// to `Swap` will be the latest (unchanged) value of `A`. /// * If the swap operation fails, due to its validator returning false, then a snapshot of /// the current value within the Atom is returned. /// * If the swap operation succeeded then a snapshot of the value that was set is returned. /// * If there is no validator for the Atom then the return value is always the snapshot of /// the successful `f` function. /// public static A swap(Atom ma, Func> f) => ma.SwapMaybe(f); /// /// Atomically updates the value by passing the old value to `f` and updating /// the atom with the result. Note: `f` may be called multiple times, so it /// should be free of side effects. /// /// Function to update the atom /// /// If the swap operation succeeded then a snapshot of the value that was set is returned. /// If the swap operation fails (which can only happen due to its validator returning false), /// then a snapshot of the current value within the Atom is returned. /// If there is no validator for the Atom then the return value is always the snapshot of /// the successful `f` function. /// public static A swap(Atom ma, Func f) => ma.Swap(f); /// /// Atomically updates the value by passing the old value to `f` and updating /// the atom with the result. Note: `f` may be called multiple times, so it /// should be free of side effects. /// /// Function to update the atom /// /// * If `f` returns `None` then no update occurs and the result of the call /// to `Swap` will be the latest (unchanged) value of `A`. /// * If the swap operation fails, due to its validator returning false, then a snapshot of /// the current value within the Atom is returned. /// * If the swap operation succeeded then a snapshot of the value that was set is returned. /// * If there is no validator for the Atom then the return value is always the snapshot of /// the successful `f` function. /// public static A swap(Atom ma, Func> f) => ma.Swap(f); } ================================================ FILE: LanguageExt.Core/Concurrency/README.md ================================================ We prefer to work with immutable types in functional-programming. However, it's not always possible, and sometimes we need some shared mutable state. With the immutable types in this library you'd need to protect the updates with locks: // Some global HashSet set = HashSet(1, 2, 3); object sync = new(); lock(sync) { set = set.Add(4); } This in unsatisfactory, and so this module is all about lock-free atomic operations. `Atom` allows you to protect any value. `AtomHashMap` and `AtomSeq` are `HashMap` and `Seq` wrapped up into a lock-free mutable structure. Snapshots of those are free! The above code can be written: AtomHashSet set = AtomHashSet(1, 2, 3); set.Add(4); Finally, there's the Software Transactional Memory (STM) system. Which allows for transactional changes to multiple `Ref` values. `Ref` just wrap up access to a value, and allows the state changes to be tracked by the `STM`. See the [concurrency section](https://github.com/louthy/language-ext/wiki/Concurrency) of the wiki for more info. ================================================ FILE: LanguageExt.Core/Concurrency/STM/CommuteRef.cs ================================================ using System; using LanguageExt.ClassInstances; namespace LanguageExt; /// /// A proxy for `Ref`, returned by `commute`. This allows the transaction system to know that the /// result is a commutative and therefore give you a result based on the live state rather than /// the transaction. /// /// /// See the [concurrency section](https://github.com/louthy/language-ext/wiki/Concurrency) of the wiki for more info. /// public readonly struct CommuteRef { internal CommuteRef(Ref r) => Ref = r; internal readonly Ref Ref; public A Value { get => Ref.Value; set => Ref.Value = value; } public static implicit operator A(CommuteRef r) => r.Value; public override string ToString() => Value?.ToString() ?? "[null]"; public override int GetHashCode() => Value?.GetHashCode() ?? 0; public override bool Equals(object? obj) => obj is A val && Equals(val); public bool Equals(A other) => EqDefault.Equals(other, Value); public A Swap(Func f) => Ref.Swap(f); public IO SwapIO(Func f) => Ref.SwapIO(f); public CommuteRef Commute(Func f) => Ref.Commute(f); } ================================================ FILE: LanguageExt.Core/Concurrency/STM/Isolation.cs ================================================ namespace LanguageExt; /// /// `sync` transaction isolation level. Used to enforce ACI properties /// of ACID on `Ref`s. /// /// /// See the [concurrency section](https://github.com/louthy/language-ext/wiki/Concurrency) of the wiki for more info. /// public enum Isolation { /// /// Snapshot isolation requires that nothing outside of the transaction has written to any of the values that are /// *written-to within the transaction*. If anything does write to the values used within the transaction, then /// the transaction is rolled back and retried (using the latest 'world' state). /// Snapshot, /// /// Serialisable isolation requires that nothing outside of the transaction has written to any of the values that /// are *read-from or written-to within the transaction*. If anything does write to the values that are used /// within the transaction, then it is rolled back and retried (using the latest 'world' state). /// /// It is the most strict form of isolation, and the most likely to conflict; but protects against cross read/write /// inconsistencies. For example, if you have: /// /// var x = Ref(1); /// var y = Ref(2); /// /// snapshot(() => x.Value = y.Value + 1); /// /// Then something writing to `y` mid-way through the transaction would not cause the transaction to fail. /// Because `y` was only read-from, not written to. However, this: /// /// var x = Ref(1); /// var y = Ref(2); /// /// serial(() => x.Value = y.Value + 1); /// /// ... would fail if something wrote to `y`. /// Serialisable } ================================================ FILE: LanguageExt.Core/Concurrency/STM/Ref.cs ================================================ using System; using System.Threading.Tasks; using LanguageExt.ClassInstances; using static LanguageExt.Prelude; namespace LanguageExt; /// /// Refs ensure safe shared use of mutable storage locations via a software transactional /// memory (STM) system. Refs are bound to a single storage location for their lifetime, and /// only allow mutation of that location to occur within a transaction. /// /// /// See the [concurrency section](https://github.com/louthy/language-ext/wiki/Concurrency) of the wiki for more info. /// public sealed class Ref : IEquatable { internal readonly long Id; public event AtomChangedEvent? Change; /// /// Internal ctor /// internal Ref(long id) => Id = id; /// /// Destructor /// ~Ref() => STM.Finalise(Id); /// /// Change handler /// internal void OnChange(A value) => Change?.Invoke(value); /// /// Value accessor (read and write) /// public A Value { get => (A)STM.Read(Id); set => STM.Write(Id, value!); } /// /// Value accessor (read and write) /// /// /// /// * Gets will return a freshly constructed `IO` monad that can be repeatedly /// evaluated to get the latest state of the `Ref`. /// /// * Sets pass an `IO` monad that will be mapped to an operation that will set /// the value of the `Ref` each time it's evaluated. /// /// public IO ValueIO { get => IO.lift(_ => (A)STM.Read(Id)); set => value.Map(v => { STM.Write(Id, v!); return unit; }); } /// /// Implicit conversion operator /// /// public static implicit operator A(Ref value) => value.Value; /// /// ToString for the bound value /// public override string ToString() => Value?.ToString() ?? "[null]"; /// /// Hash code of the bound value /// public override int GetHashCode() => Value?.GetHashCode() ?? 0; /// /// Equality /// public override bool Equals(object? obj) => obj is A val && Equals(val); /// /// Equality /// public bool Equals(A? other) => EqDefault.Equals(other, Value); /// /// Swap the old value for the new returned by `f` /// Must be run within a `sync` transaction /// /// Swap function /// The value returned from `f` public A Swap(Func f) { var v = f(Value); Value = v; return v; } /// /// Swap the old value for the new returned by `f` /// Must be run within a `sync` transaction /// /// Swap function /// The value returned from `f` public IO SwapIO(Func f) => IO.lift(_ => { var fv = f(Value); Value = fv; return fv; }); /// /// Must be called in a transaction. Sets the in-transaction-value of /// ref to: /// /// `f(in-transaction-value-of-ref)` /// /// and returns the in-transaction-value when complete. /// /// At the commit point of the transaction, `f` is run *AGAIN* with the /// most recently committed value: /// /// `f(most-recently-committed-value-of-ref)` /// /// Thus `f` should be commutative, or, failing that, you must accept /// last-one-in-wins behavior. /// /// Commute allows for more concurrency than just setting the Ref's value /// public CommuteRef Commute(Func f) { STM.Commute(Id, f); return new CommuteRef(this); } } ================================================ FILE: LanguageExt.Core/Concurrency/STM/STM.cs ================================================ using System; using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; using LanguageExt.ClassInstances; using static LanguageExt.Prelude; // ReSharper disable MemberHidesStaticFromOuterClass namespace LanguageExt; /// /// Software transactional memory using Multi-Version Concurrency Control (MVCC) /// /// /// See the [concurrency section](https://github.com/louthy/language-ext/wiki/Concurrency) of the wiki for more info. /// public static class STM { static long refIdNext; static readonly AtomHashMap state; static readonly AsyncLocal transaction; static STM() { state = AtomHashMap(); transaction = new AsyncLocal(); } static void OnChange(TrieMap> patch) { foreach (var change in patch) { if (change.Value is EntryMappedTo update) { update.To.OnChange(update.To.UntypedValue); } } } /// /// Generates a new reference that can be used within a sync transaction /// internal static Ref NewRef(A value, Func? validator = null) { var id = Interlocked.Increment(ref refIdNext); var r = new Ref(id); var v = new RefState(0, value, validator, r); state.Add(id, v); return r; } /// /// Run the op within a new transaction /// If a transaction is already running, then this becomes part of the parent transaction /// internal static R DoTransaction(Func op, Isolation isolation) => transaction.Value == null ? RunTransaction(op, isolation) : op(); /// /// Run the op within a new transaction /// If a transaction is already running, then this becomes part of the parent transaction /// internal static ValueTask DoTransaction(Func> op, Isolation isolation) => transaction.Value == null ? RunTransaction(op, isolation) : op(); /// /// Run the op within a new transaction /// If a transaction is already running, then this becomes part of the parent transaction /// internal static Eff DoTransaction(Eff op, Isolation isolation) => transaction.Value == null ? RunTransaction(op, isolation) : op; /// /// Run the op within a new transaction /// If a transaction is already running, then this becomes part of the parent transaction /// internal static Eff DoTransaction(Eff op, Isolation isolation) => transaction.Value == null ? RunTransaction(op, isolation) : op; /// /// Run the op within a new transaction /// If a transaction is already running, then this becomes part of the parent transaction /// internal static R DoTransaction(Func> op, Isolation isolation) => transaction.Value == null ? RunTransaction(op, isolation) : op().Value; /// /// Runs the transaction /// static R RunTransaction(Func op, Isolation isolation) { SpinWait sw = default; while (true) { // Create a new transaction with a snapshot of the current state var t = new Transaction(state.Items); transaction.Value = t; try { // Try to do the operations of the transaction return ValidateAndCommit(t, isolation, op(), long.MinValue); } catch (ConflictException) { // Conflict found, so retry } finally { // Clear the current transaction on the way out transaction.Value = null!; // Announce changes OnChange(t.changes); } // Wait one tick before trying again sw.SpinOnce(); } } /// /// Runs the transaction /// static Eff RunTransaction(Eff op, Isolation isolation) => getState().Bind( sta => Eff.Lift( env => { SpinWait sw = default; while (true) { // Create a new transaction with a snapshot of the current state var t = new Transaction(state.Items); transaction.Value = t; try { // Try to do the operations of the transaction var res = op.Run(env, sta.EnvIO); return res.IsFail ? res : ValidateAndCommit(t, isolation, res.SuccValue, Int64.MinValue); } catch (ConflictException) { // Conflict found, so retry } finally { // Clear the current transaction on the way out transaction.Value = null; // Announce changes OnChange(t.changes); } // Wait one tick before trying again sw.SpinOnce(); } })); /// /// Runs the transaction /// static Eff RunTransaction(Eff op, Isolation isolation) => lift(() => { SpinWait sw = default; while (true) { // Create a new transaction with a snapshot of the current state var t = new Transaction(state.Items); transaction.Value = t; try { // Try to do the operations of the transaction var res = op.Run(); return res.IsFail ? res : ValidateAndCommit(t, isolation, res.SuccValue, Int64.MinValue); } catch (ConflictException) { // Conflict found, so retry } finally { // Clear the current transaction on the way out transaction.Value = null; // Announce changes OnChange(t.changes); } // Wait one tick before trying again sw.SpinOnce(); } }); /// /// Runs the transaction /// static async ValueTask RunTransaction(Func> op, Isolation isolation) { SpinWait sw = default; while (true) { // Create a new transaction with a snapshot of the current state var t = new Transaction(state.Items); transaction.Value = t; try { // Try to do the operations of the transaction return ValidateAndCommit(t, isolation, await op().ConfigureAwait(false), long.MinValue); } catch (ConflictException) { // Conflict found, so retry } finally { // Clear the current transaction on the way out transaction.Value = Transaction.None; // Announce changes OnChange(t.changes); } // Wait one tick before trying again sw.SpinOnce(); } } /// /// Runs the transaction /// static R RunTransaction(Func> op, Isolation isolation) { SpinWait sw = default; while (true) { // Create a new transaction with a snapshot of the current state var t = new Transaction(state.Items); transaction.Value = t; try { var cref = op(); // Try to do the operations of the transaction return ValidateAndCommit(t, isolation, (R)t.state[cref.Ref.Id].UntypedValue, cref.Ref.Id); } catch (ConflictException) { // Conflict found, so retry } finally { // Clear the current transaction on the way out transaction.Value = Transaction.None; // Announce changes OnChange(t.changes); } // Spin, backing off, then yield the thread to avoid deadlock sw.SpinOnce(); } } static R ValidateAndCommit(Transaction t, Isolation isolation, R result, long returnRefId) { // No writing, so no validation or commit needed var writes = t.writes.Count; var commutes = t.commutes.Count; var anyWrites = writes > 0; var anyCommutes = commutes > 0; if (!anyWrites && !anyCommutes) { return result; } // Attempt to apply the changes atomically state.SwapInternal(s => { if (isolation == Isolation.Serialisable) { ValidateReads(t, s); } s = anyWrites ? CommitWrites(t, s) : s; (s, result) = anyCommutes ? CommitCommutes(t, s, returnRefId, result) : (s, result); return s; }); // Changes applied successfully return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] static void ValidateReads(Transaction t, TrieMap s) { var tlocal = t; var slocal = tlocal.state; // Check if something else wrote to what we were reading foreach (var read in tlocal.reads) { if (s[read].Version != slocal[read].Version) { throw new ConflictException(); } } } static TrieMap CommitWrites(Transaction t, TrieMap s) { // Check if something else wrote to what we were writing var tlocal = t; var slocal = tlocal.state; foreach (var write in tlocal.writes) { var newState = slocal[write]; if (!newState.Validate(newState)) { throw new RefValidationFailedException(); } if (s[write].Version == newState.Version) { s = s.SetItem(write, newState.Inc()); } else { throw new ConflictException(); } } return s; } static (TrieMap, R) CommitCommutes(Transaction t, TrieMap s, long returnRefId, R result) { // Run the commutative operations foreach (var commute in t.commutes) { var exist = s[commute.Id]; // Re-run the commute function with what's live now var nver = exist.MapAndInc(commute.Fun); // Validate the result if (!nver.Validate(nver)) { throw new RefValidationFailedException(); } // Save to live state s = s.SetItem(commute.Id, nver); // If it matches our return type, then make it the result if (returnRefId == commute.Id) { result = (R)nver.UntypedValue; } } return (s, result); } /// /// Read the value for the reference ID provided /// If within a transaction then the in-transaction value is returned, otherwise it's /// the current latest value /// [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static object Read(long id) => transaction.Value == null ? state.Items[id].UntypedValue : transaction.Value.ReadValue(id); /// /// Write the value for the reference ID provided /// Must be run within a transaction /// [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static void Write(long id, object value) { if (transaction.Value == null) { throw new InvalidOperationException("Refs can only be written to from within a `sync` transaction"); } transaction.Value.WriteValue(id, value); } /// /// Must be called in a transaction. Sets the in-transaction-value of /// ref to: /// /// `f(in-transaction-value-of-ref)` /// /// and returns the in-transaction-value when complete. /// /// At the commit point of the transaction, `f` is run *AGAIN* with the /// most recently committed value: /// /// `f(most-recently-committed-value-of-ref)` /// /// Thus `f` should be commutative, or, failing that, you must accept /// last-one-in-wins behavior. /// /// Commute allows for more concurrency than just setting the items /// [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static A Commute(long id, Func f) { if (transaction.Value == null) { throw new InvalidOperationException("Refs can only commute from within a transaction"); } return (A)transaction.Value.Commute(id, CastCommute(f)); } /// /// Must be called in a transaction. Sets the in-transaction-value of /// ref to: /// /// `f(in-transaction-value-of-ref)` /// /// and returns the in-transaction-value when complete. /// /// At the commit point of the transaction, `f` is run *AGAIN* with the /// most recently committed value: /// /// `f(most-recently-committed-value-of-ref)` /// /// Thus `f` should be commutative, or, failing that, you must accept /// last-one-in-wins behavior. /// /// Commute allows for more concurrency than just setting the items /// [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static A Commute(long id, X x, Func f) => Commute(id, (a => f(x, a))); /// /// Must be called in a transaction. Sets the in-transaction-value of /// ref to: /// /// `f(in-transaction-value-of-ref)` /// /// and returns the in-transaction-value when complete. /// /// At the commit point of the transaction, `f` is run *AGAIN* with the /// most recently committed value: /// /// `f(most-recently-committed-value-of-ref)` /// /// Thus `f` should be commutative, or, failing that, you must accept /// last-one-in-wins behavior. /// /// Commute allows for more concurrency than just setting the items /// [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static A Commute(long id, X x, Y y, Func f) => Commute(id, (a => f(x, y, a))); /// /// Make sure Refs are cleaned up /// [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static void Finalise(long id) => state.Remove(id); /// /// Conflict exception for internal use /// class ConflictException : Exception; /// /// Wraps a (A -> A) predicate as (object -> object) /// [MethodImpl(MethodImplOptions.AggressiveInlining)] static Func CastCommute(Func f) => obj => f((A)obj)!; /// /// Get the currently running TransactionId /// public static long TransactionId { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => transaction.Value?.transactionId ?? throw new InvalidOperationException("Transaction not running"); } /// /// Transaction snapshot /// class Transaction { static long transactionIdNext; public readonly long transactionId; public TrieMap state; public TrieMap> changes; public readonly System.Collections.Generic.HashSet reads = new(); public readonly System.Collections.Generic.HashSet writes = new(); public readonly System.Collections.Generic.List<(long Id, Func Fun)> commutes = new(); public static readonly Transaction None = new (TrieMap.Empty); [MethodImpl(MethodImplOptions.AggressiveInlining)] public Transaction(TrieMap state) { this.state = state; changes = TrieMap>.EmptyForMutating; transactionId = Interlocked.Increment(ref transactionIdNext); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public object ReadValue(long id) { reads.Add(id); return state[id].UntypedValue; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteValue(long id, object value) { var oldState = state[id]; var newState = oldState.SetValue(value); state = state.SetItem(id, newState); writes.Add(id); var change = changes.Find(id); if (change.IsSome) { var last = (EntryMapped)change.Value!; changes = changes.AddOrUpdateInPlace(id, Change.Mapped(last.From, newState)); } else { changes = changes.AddOrUpdateInPlace(id, Change.Mapped(oldState, newState)); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public object Commute(long id, Func f) { var oldState = state[id]; var newState = oldState.Map(f); state = state.SetItem(id, newState); commutes.Add((id, f)); return newState.UntypedValue; } } /// /// The state of a Ref /// Includes the value and the version /// abstract record RefState(long Version) { public abstract bool Validate(RefState refState); public abstract RefState SetValue(object value); public abstract RefState SetValueAndInc(object value); public abstract RefState Inc(); public abstract RefState Map(Func f); public abstract RefState MapAndInc(Func f); public abstract void OnChange(object value); public abstract object UntypedValue { get; } } record RefState(long Version, A Value, Func? Validator, Ref Ref) : RefState(Version) { public override bool Validate(RefState refState) => Validator?.Invoke(((RefState)refState).Value) ?? true; public override RefState SetValue(object value) => this with {Value = (A)value}; public override RefState SetValueAndInc(object value) => this with {Version = Version + 1,Value = (A)value}; public override RefState Inc() => this with {Version = Version + 1}; public override RefState Map(Func f) => this with {Value = (A)f(Value!)}; public override RefState MapAndInc(Func f) => this with {Version = Version + 1, Value = (A)f(Value!)}; public override void OnChange(object value) => Ref.OnChange((A)value); public override object UntypedValue => Value!; } } ================================================ FILE: LanguageExt.Core/Concurrency/Signals/CountdownSignal.cs ================================================ using System; using System.Threading; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; /// /// IO signalling, usually used to signal for cross-thread synchronisation. /// public class CountdownSignal(CountdownEvent handle) : IDisposable where M : Monad { /// /// The initial value of the counter /// public int Initial => handle.InitialCount; /// /// The current value of the counter /// public K Count => M.LiftIOMaybe(IO.lift(() => handle.CurrentCount)); /// /// True if the counter has complete its countdown /// public K Complete => M.LiftIOMaybe(IO.lift(() => handle.IsSet)); /// /// Triggers a single countdown of the counter in the signal /// /// True if the counter reached zero. public K Trigger() => M.LiftIOMaybe(IO.lift(handle.Signal)); /// /// Triggers a single countdown of the counter in the signal /// /// True if the counter reached zero. public bool TriggerUnsafe() => handle.Signal(); /// /// Triggers `n` signals to the counter in the signal /// /// /// This is marked unsafe because it's not in an `IO` operation and therefore is impure. /// /// True if the counter reached zero. public K Trigger(int count) => M.LiftIOMaybe(IO.lift(handle.Signal)); /// /// Triggers `n` signals to the counter in the signal /// /// /// This is marked unsafe because it's not in an `IO` operation and therefore is impure. /// /// True if the counter reached zero. public bool TriggerUnsafe(int count) => handle.Signal(); /// /// Wait for signal to signal /// /// Monad `M` with the `Wait` operation lifted into it via `liftIO` public K Wait() => M.LiftIOMaybe(IO.lift(e => { handle.Wait(e.Token); return unit; })); public void Dispose() => handle.Dispose(); } ================================================ FILE: LanguageExt.Core/Concurrency/Signals/Signal.Module.cs ================================================ using System.Threading; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; /// /// IO signalling constructors /// public static class Signal { /// Represents a thread synchronisation event that, when signaled, resets automatically after releasing a /// single waiting thread. /// /// The internal `EventWaitHandle` is wrapped with a `use` operator so that its handle is tracked by the `IO` /// resource-management system and auto-cleaned up if not done manually with `release` or using `bracket`. /// /// /// to set the initial state to signaled; /// to set the initial state to non-signaled. /// public static K> autoReset(bool signaled = false) where M : Monad => M.LiftIOMaybe(use(() => new Signal(new AutoResetEvent(false)))); /// Represents a thread synchronisation event that, when signaled, must be reset manually. /// /// The internal `EventWaitHandle` is wrapped with a `use` operator so that its handle is tracked by the `IO` /// resource-management system and auto-cleaned up if not done manually with `release` or using `bracket`. /// /// /// to set the initial state signaled; /// to set the initial state to non-signaled. /// public static K> manualReset(bool signaled = false) where M : Monad => M.LiftIOMaybe(use(() => new Signal(new ManualResetEvent(false)))); /// Represents a synchronisation primitive that is signaled when its count reaches zero. /// /// The internal `EventWaitHandle` is wrapped with a `use` operator so that its handle is tracked by the `IO` /// resource-management system and auto-cleaned up if not done manually with `release` or using `bracket`. /// public static K> countdown(int count) where M : Monad => M.LiftIOMaybe(use(() => new CountdownSignal(new CountdownEvent(count)))); } ================================================ FILE: LanguageExt.Core/Concurrency/Signals/Signal.cs ================================================ using System; using System.Threading; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; /// /// IO signalling, usually used to signal for cross-thread synchronisation. /// public class Signal(EventWaitHandle handle) : IDisposable where M : Monad { /// /// Set the signal /// /// /// True if the operation succeeds /// public K Trigger() => M.LiftIOMaybe(IO.lift(_ => handle.Set())); /// /// Set the signal /// /// /// This is marked unsafe because it's not in an `IO` operation and therefore is impure. /// /// /// True if the operation succeeds /// public bool TriggerUnsafe() => handle.Set(); /// /// Wait for signal to signal /// /// public K Wait() => M.LiftIOMaybe(IO.liftAsync(e => handle.WaitOneAsync(e.Token))); /// /// Wait for signal to signal /// /// public K Wait(TimeSpan timeout) => M.LiftIOMaybe(IO.liftAsync(e => handle.WaitOneAsync(timeout, e.Token))); /// /// Wait for signal to signal /// /// public K Wait(int timeoutMilliseconds) => M.LiftIOMaybe(IO.liftAsync(e => handle.WaitOneAsync(timeoutMilliseconds, e.Token))); public void Dispose() => handle.Dispose(); } ================================================ FILE: LanguageExt.Core/Concurrency/Task/Task.Extensions.cs ================================================ using LanguageExt.ClassInstances; using System; using System.Collections.Generic; using System.Diagnostics.Contracts; using System.Linq; using System.Threading; using System.Threading.Tasks; using LanguageExt.Common; using static LanguageExt.Prelude; namespace LanguageExt; public static class TaskExtensions { public static bool CompletedSuccessfully(this Task ma) => ma is { IsCompleted: true, IsFaulted: false, IsCanceled: false }; [Pure] public static Task AsFailedTask(this Exception ex) { var tcs = new TaskCompletionSource(); tcs.SetException(ex); return tcs.Task; } /// /// Convert a value to a Task that completes immediately /// [Pure] public static Task AsTask(this A self) => Task.FromResult(self); /// /// Convert a ValueTask to a Task /// [Pure] public static Task ToRef(this ValueTask self) => self.AsTask(); /// /// Flatten the nested Task type /// [Pure] public static async Task Flatten(this Task> self) { var t = await self.ConfigureAwait(false); var u = await t.ConfigureAwait(false); return u; } /// /// Flatten the nested Task type /// [Pure] public static async Task Flatten(this Task>> self) { var t = await self.ConfigureAwait(false); var u = await t.ConfigureAwait(false); var v = await u.ConfigureAwait(false); return v; } /// /// Standard LINQ Select implementation for Task /// [Pure] public static async Task Select(this Task self, Func map) => map(await self.ConfigureAwait(false)); /// /// Standard LINQ Where implementation for Task /// [Pure] public static async Task Where(this Task self, Func pred) { var resT = await self.ConfigureAwait(false); var res = pred(resT); if (!res) { throw new OperationCanceledException(); } return resT; } /// /// Standard LINQ SelectMany implementation for Task /// [Pure] public async static Task SelectMany(this Task self, Func> bind) => await bind(await self.ConfigureAwait(false)).ConfigureAwait(false); /// /// Standard LINQ SelectMany implementation for Task /// [Pure] public static async Task SelectMany(this Task self, Func> bind, Func project) { var resT = await self.ConfigureAwait(false); var resU = await bind(resT).ConfigureAwait(false); return project(resT, resU); } /// /// Get the Count of a Task T. Returns either 1 or 0 if cancelled or faulted. /// [Pure] public static async Task Count(this Task self) { try { await self.ConfigureAwait(false); return 1; } catch (Exception) { return 0; } } /// /// Monadic bind operation for Task /// [Pure] public static Task Bind(this Task self, Func> bind) => self.SelectMany(bind); /// /// Returns false if the Task is cancelled or faulted, otherwise /// it returns the result of pred(Result) /// [Pure] public static async Task Exists(this Task self, Func pred) => pred(await self.ConfigureAwait(false)); /// /// Returns false if the Task is cancelled or faulted, otherwise /// it returns the result of pred(Result) /// [Pure] public static async Task ExistsAsync(this Task self, Func> pred) => await pred(await self.ConfigureAwait(false)).ConfigureAwait(false); /// /// Returns false if the Task is cancelled or faulted, otherwise /// it returns the result of pred(Result) /// [Pure] public static async Task ForAll(this Task self, Func pred) => pred(await self.ConfigureAwait(false)); /// /// Returns false if the Task is cancelled or faulted, otherwise /// it returns the result of pred(Result) /// [Pure] public static async Task ForAllAsync(this Task self, Func> pred) => await pred(await self.ConfigureAwait(false)).ConfigureAwait(false); /// /// Filters the task. This throws a BottomException when pred(Result) /// returns false /// [Pure] public static Task Filter(this Task self, Func pred) => self.Where(pred); /// /// Folds the Task. Returns folder(state,Result) if not faulted or /// cancelled. Returns state otherwise. /// [Pure] public static async Task Fold(this Task self, S state, Func folder) => folder(state, await self.ConfigureAwait(false)); /// /// Folds the Task. Returns folder(state,Result) if not faulted or /// cancelled. Returns state otherwise. /// [Pure] public static async Task FoldAsync(this Task self, S state, Func> folder) => await folder(state, await self.ConfigureAwait(false)).ConfigureAwait(false); /// /// Iterates the Task. Invokes f(Result) if not faulted or cancelled /// public static async Task Iter(this Task self, Action f) { f(await self.ConfigureAwait(false)); return unit; } /// /// Impure iteration of the bound value in the structure /// /// /// Returns the original unmodified structure /// public static Task Do(this Task ma, Action f) => ma.Map(x => { f(x); return x; }); /// /// Returns map(Result) if not faulted or cancelled. /// [Pure] public static async Task Map(this Task self, Func map) => map(await self.ConfigureAwait(false)); /// /// Returns map(Result) if not faulted or cancelled. /// [Pure] public static async Task MapAsync(this Task self, Func> map) => await map(await self.ConfigureAwait(false)).ConfigureAwait(false); [Pure] public static async Task Join(this Task source, Task inner, Func outerKeyMap, Func innerKeyMap, Func project) { await Task.WhenAll(source, inner).ConfigureAwait(false); if (!EqDefault.Equals(outerKeyMap(source.Result), innerKeyMap(inner.Result))) { throw new OperationCanceledException(); } return project(source.Result, inner.Result); } [Pure] public static async Task GroupJoin(this Task source, Task inner, Func outerKeyMap, Func innerKeyMap, Func, V> project) { T t = await source.ConfigureAwait(false); return project(t, inner.Where(u => EqDefault.Equals(outerKeyMap(t), innerKeyMap(u)))); } [Pure] public static async Task Plus(this Task ma, Task mb) { try { return await ma.ConfigureAwait(false); } catch { return await mb.ConfigureAwait(false); } } [Pure] public static async Task PlusFirst(this Task ma, Task mb) => await (await Task.WhenAny(ma, mb).ConfigureAwait(false)).ConfigureAwait(false); public static async Task Cast(this Task source) { if (source == null) throw new ArgumentNullException(nameof(source)); await source.ConfigureAwait(false); return source.GetType() switch { var taskTy when taskTy.IsGenericType && taskTy.GenericTypeArguments.Length == 1 && taskTy.GenericTypeArguments[0] == typeof(A) => (A)((dynamic)source).Result, _ => default! }; } public static async Task ToUnit(this Task source) { await source.ConfigureAwait(false); return unit; } /// /// Tasks a lazy sequence of tasks and iterates them in a 'measured way'. A default window size of /// `Sys.DefaultAsyncSequenceConcurrency` tasks is used, which means there are `Environment.ProcessorCount / 2` /// 'await streams' (by default). An await stream essentially awaits one task from the sequence, and on /// completion goes and gets the next task from the lazy sequence and awaits that too. This continues until the /// end of the lazy sequence, or forever for infinite streams. /// internal static Task> WindowMap(this IEnumerable> ma, Func f, CancellationToken token) => WindowMap(ma, SysInfo.DefaultAsyncSequenceParallelism, f, token); /// /// Tasks a lazy sequence of tasks and maps them in a 'measured way'. A default window size of /// `windowSize` tasks is used, which means there are `windowSize` 'await streams'. An await stream /// essentially awaits one task from the sequence, and on completion goes and gets the next task from /// the lazy sequence and awaits that too. This continues until the end of the lazy sequence, or forever /// for infinite streams. Therefore there are at most `windowSize` tasks running concurrently. /// internal static async Task> WindowMap( this IEnumerable> ma, int windowSize, Func f, CancellationToken token) { var sync = new object(); using var wait = new CountdownEvent(windowSize); using var iter = ma.GetEnumerator(); var index = -1; var results = new List(); var errors = new List(); for (var i = 0; i < windowSize; i++) { #pragma warning disable CS4014 // call is not awaited Task.Run(go, token); #pragma warning restore CS4014 } Option<(int Index, Task)> next() { lock (sync) { index++; try { if (iter.MoveNext()) { results.Add(default!); return Some((index, iter.Current.Map(f))); } else { return default; } } catch (Exception e) { errors.Add(e); return default; } } } void go() { try { while (!token.IsCancellationRequested) { var otask = next(); if (otask.IsNone) return; var (ix, task) = ((int, Task))otask; SpinWait sw = default; while (!task.IsCompleted && !token.IsCancellationRequested) { sw.SpinOnce(); } lock (sync) { if (token.IsCancellationRequested) { throw new OperationCanceledException(); } else if (task.IsCanceled) { errors.Add(task.Exception is not null ? task.Exception : new OperationCanceledException()); } else if (task.IsFaulted) { if (task.Exception is not null) errors.Add(task.Exception); } else if (task.IsCompleted) { results[ix] = task.Result; } } } } catch (Exception e) { lock (sync) { errors.Clear(); errors.Add(e); } } finally { wait.Signal(); } } await wait.WaitHandle.WaitOneAsync(token).ConfigureAwait(false); if (errors.Count > 0) { var allErrors = errors .SelectMany(e => e is AggregateException ae ? ae.InnerExceptions.ToArray() : new[] { e }) .ToArray(); if (allErrors.Length > 1) { // Throw an aggregate of all exceptions throw new AggregateException(allErrors); } else if (allErrors.Length == 1) { // Throw an aggregate of all exceptions allErrors[0].Rethrow(); return default!; } } return results; } } ================================================ FILE: LanguageExt.Core/Concurrency/Task/Task.Prelude.cs ================================================ using LanguageExt; using LanguageExt.ClassInstances; using System; using System.Diagnostics.Contracts; using System.Threading.Tasks; using LanguageExt.Common; using static LanguageExt.Prelude; namespace LanguageExt; public static partial class Prelude { /// /// Convert a value to a Task that completes immediately /// [Pure] public static Task TaskSucc(A self) => Task.FromResult(self); /// /// Convert a value to a Task that completes immediately /// [Pure] public static Task TaskFail(Exception ex) => Task.FromException(ex); /// /// Flatten the nested Task type /// [Pure] public static Task flatten(Task> self) => self.Flatten(); /// /// Flatten the nested Task type /// [Pure] public static Task flatten(Task>> self) => self.Flatten(); /// /// Get the Count of a Task T. Returns either 1 or 0 if cancelled or faulted. /// [Pure] public static Task count(Task self) => self.Count(); /// /// Monadic bind operation for Task /// [Pure] public static Task bind(Task self, Func> bind) => self.Bind(bind); /// /// Returns false if the Task is cancelled or faulted, otherwise /// it returns the result of pred(Result) /// [Pure] public static Task exists(Task self, Func pred) => self.Exists(pred); /// /// Returns false if the Task is cancelled or faulted, otherwise /// it returns the result of pred(Result) /// [Pure] public static Task existsAsync(Task self, Func> pred) => self.ExistsAsync(pred); /// /// Returns false if the Task is cancelled or faulted, otherwise /// it returns the result of pred(Result) /// [Pure] public static Task forall(Task self, Func pred) => self.ForAll(pred); /// /// Returns false if the Task is cancelled or faulted, otherwise /// it returns the result of pred(Result) /// [Pure] public static Task forallAsync(Task self, Func> pred) => self.ForAllAsync(pred); /// /// Filters the task. This throws a BottomException when pred(Result) /// returns false /// [Pure] public static Task filter(Task self, Func pred) => self.Filter(pred); /// /// Iterates the Task. Invokes f(Result) if not faulted or cancelled /// public static Task iter(Task self, Action f) => self.Iter(f); /// /// Returns map(Result) if not faulted or cancelled. /// [Pure] public static Task map(Task self, Func map) => self.Map(map); /// /// Returns map(Result) if not faulted or cancelled. /// [Pure] public static Task mapAsync(Task self, Func> map) => self.MapAsync(map); [Pure] public static Task plus(this Task ma, Task mb) => ma.Plus(mb); [Pure] public static Task plusFirst(this Task ma, Task mb) => ma.PlusFirst(mb); /// /// Returns the first successful computation /// /// Bound value /// The first computation to run /// The rest of the computations to run /// The first computation that succeeds [Pure] public static Task choice(Task ma, params Task[] tail) => choice(Cons(ma, tail)); /// /// Returns the first successful computation /// /// Bound value /// Sequence of computations to run /// The first computation that succeeds [Pure] public static async Task choice(Seq> xs) { foreach (var x in xs) { try { return await x.ConfigureAwait(false); } catch { // ignore } } throw new BottomException(); } /// /// Apply /// /// Function to apply the applicative to /// Applicative to apply /// Applicative of type FB derived from Applicative of B [Pure] public static async Task apply(Task> fab, Task fa) { await Task.WhenAll(fab, fa).ConfigureAwait(false); return fab.Result(fa.Result); } /// /// Apply /// /// Function to apply the applicative to /// Applicative to apply /// Applicative of type FB derived from Applicative of B [Pure] public static Task apply(Func fab, Task fa) => fa.Map(fab); /// /// Apply /// /// Function to apply the applicative to /// Applicative a to apply /// Applicative b to apply /// Applicative of type FC derived from Applicative of C [Pure] public static async Task apply(Task> fabc, Task fa, Task fb) { await Task.WhenAll(fabc, fa, fb).ConfigureAwait(false); return fabc.Result(fa.Result, fb.Result); } /// /// Apply /// /// Function to apply the applicative to /// Applicative a to apply /// Applicative b to apply /// Applicative of type FC derived from Applicative of C [Pure] public static async Task apply(Func fabc, Task fa, Task fb) { await Task.WhenAll(fa, fb).ConfigureAwait(false); return fabc(fa.Result, fb.Result); } /// /// Apply /// /// Function to apply the applicative to /// Applicative to apply /// Applicative of type f(b -> c) derived from Applicative of Func〈B, C〉 [Pure] public static async Task> apply(Task> fabc, Task fa) { await Task.WhenAll(fabc, fa).ConfigureAwait(false); return curry(fabc.Result)(fa.Result); } /// /// Apply /// /// Function to apply the applicative to /// Applicative to apply /// Applicative of type f(b -> c) derived from Applicative of Func〈B, C〉 [Pure] public static Task> apply(Func fabc, Task fa) => fa.Map(curry(fabc)); /// /// Apply /// /// Function to apply the applicative to /// Applicative to apply /// Applicative of type f(b -> c) derived from Applicative of Func〈B, C〉 [Pure] public static async Task> apply(Task>> fabc, Task fa) { await Task.WhenAll(fabc, fa).ConfigureAwait(false); return fabc.Result(fa.Result); } /// /// Apply /// /// Function to apply the applicative to /// Applicative to apply /// Applicative of type f(b -> c) derived from Applicative of Func〈B, C〉 [Pure] public static Task> apply(Func> fabc, Task fa) => fa.Map(fabc); } ================================================ FILE: LanguageExt.Core/Concurrency/Task/Tasks.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics.Contracts; using System.Threading; using System.Threading.Tasks; using static LanguageExt.Prelude; namespace LanguageExt; internal static class Tasks { [Pure] public static async Task ForAll(IEnumerable> fs, Func pred, CancellationToken token = default) { var ra = await fs.WindowMap(pred, default).ConfigureAwait(false); return ra.AsIterable().ForAll(identity); } [Pure] public static async Task ForAll(IEnumerable> fs, Func pred, int windowSize, CancellationToken token = default) { var ra = await fs.WindowMap(windowSize, pred, default).ConfigureAwait(false); return ra.AsIterable().ForAll(identity); } [Pure] public static async Task Exists(IEnumerable> fs, Func pred, CancellationToken token = default) { var ra = await fs.WindowMap(pred, default).ConfigureAwait(false); return ra.AsIterable().Exists(identity); } [Pure] public static async Task Exists(IEnumerable> fs, Func pred, int windowSize, CancellationToken token = default) { var ra = await fs.WindowMap(windowSize, pred, default).ConfigureAwait(false); return ra.AsIterable().Exists(identity); } } ================================================ FILE: LanguageExt.Core/Concurrency/ValueTask/ValueTask.Extensions.cs ================================================ using LanguageExt.ClassInstances; using System; using System.Collections.Generic; using System.Diagnostics.Contracts; using System.Linq; using System.Threading; using System.Threading.Tasks; using LanguageExt.Common; using static LanguageExt.Prelude; namespace LanguageExt; public static class ValueTaskExtensions { public static bool CompletedSuccessfully(this ValueTask ma) => ma is { IsCompleted: true, IsFaulted: false, IsCanceled: false }; [Pure] public static ValueTask AsFailedValueTask(this Exception ex) => new (ex.AsFailedTask()); /// /// Convert a value to a Task that completes immediately /// [Pure] public static ValueTask AsValueTask(this A self) => new (self); /// /// Convert a Task to a ValueTask /// [Pure] public static ValueTask ToValue(this Task self) => new (self); /// /// Flatten the nested Task type /// [Pure] public static async ValueTask Flatten(this ValueTask> self) { var t = await self.ConfigureAwait(false); var u = await t.ConfigureAwait(false); return u; } /// /// Flatten the nested Task type /// [Pure] public static async ValueTask Flatten(this ValueTask>> self) { var t = await self.ConfigureAwait(false); var u = await t.ConfigureAwait(false); var v = await u.ConfigureAwait(false); return v; } /// /// Standard LINQ Select implementation for Task /// [Pure] public static async ValueTask Select(this ValueTask self, Func map) => map(await self.ConfigureAwait(false)); /// /// Standard LINQ Where implementation for Task /// [Pure] public static async ValueTask Where(this ValueTask self, Func pred) { var resT = await self.ConfigureAwait(false); var res = pred(resT); if (!res) { throw new OperationCanceledException(); } return resT; } /// /// Standard LINQ SelectMany implementation for Task /// [Pure] public async static ValueTask SelectMany(this ValueTask self, Func> bind) => await bind(await self.ConfigureAwait(false)).ConfigureAwait(false); /// /// Standard LINQ SelectMany implementation for Task /// [Pure] public static async ValueTask SelectMany(this ValueTask self, Func> bind, Func project) { var resT = await self.ConfigureAwait(false); var resU = await bind(resT).ConfigureAwait(false); return project(resT, resU); } /// /// Get the Count of a Task T. Returns either 1 or 0 if cancelled or faulted. /// [Pure] public static async ValueTask Count(this ValueTask self) { try { await self.ConfigureAwait(false); return 1; } catch (Exception) { return 0; } } /// /// Monadic bind operation for Task /// [Pure] public static ValueTask Bind(this ValueTask self, Func> bind) => self.SelectMany(bind); /// /// Returns false if the Task is cancelled or faulted, otherwise /// it returns the result of pred(Result) /// [Pure] public static async ValueTask Exists(this ValueTask self, Func pred) => pred(await self.ConfigureAwait(false)); /// /// Returns false if the Task is cancelled or faulted, otherwise /// it returns the result of pred(Result) /// [Pure] public static async ValueTask ExistsAsync(this ValueTask self, Func> pred) => await pred(await self.ConfigureAwait(false)).ConfigureAwait(false); /// /// Returns false if the Task is cancelled or faulted, otherwise /// it returns the result of pred(Result) /// [Pure] public static async ValueTask ForAll(this ValueTask self, Func pred) => pred(await self.ConfigureAwait(false)); /// /// Returns false if the Task is cancelled or faulted, otherwise /// it returns the result of pred(Result) /// [Pure] public static async ValueTask ForAllAsync(this ValueTask self, Func> pred) => await pred(await self.ConfigureAwait(false)).ConfigureAwait(false); /// /// Filters the task. This throws a BottomException when pred(Result) /// returns false /// [Pure] public static ValueTask Filter(this ValueTask self, Func pred) => self.Where(pred); /// /// Folds the Task. Returns folder(state,Result) if not faulted or /// cancelled. Returns state otherwise. /// [Pure] public static async ValueTask Fold(this ValueTask self, S state, Func folder) => folder(state, await self.ConfigureAwait(false)); /// /// Folds the Task. Returns folder(state,Result) if not faulted or /// cancelled. Returns state otherwise. /// [Pure] public static async ValueTask FoldAsync(this ValueTask self, S state, Func> folder) => await folder(state, await self.ConfigureAwait(false)).ConfigureAwait(false); /// /// Iterates the Task. Invokes f(Result) if not faulted or cancelled /// public static async ValueTask Iter(this ValueTask self, Action f) { f(await self.ConfigureAwait(false)); return unit; } /// /// Impure iteration of the bound value in the structure /// /// /// Returns the original unmodified structure /// public static ValueTask Do(this ValueTask ma, Action f) => ma.Map(x => { f(x); return x; }); /// /// Returns map(Result) if not faulted or cancelled. /// [Pure] public static async ValueTask Map(this ValueTask self, Func map) => map(await self.ConfigureAwait(false)); /// /// Returns map(Result) if not faulted or cancelled. /// [Pure] public static async ValueTask MapAsync(this ValueTask self, Func> map) => await map(await self.ConfigureAwait(false)).ConfigureAwait(false); [Pure] public static async ValueTask Join(this ValueTask source, ValueTask inner, Func outerKeyMap, Func innerKeyMap, Func project) { await Task.WhenAll(source.AsTask(), inner.AsTask()).ConfigureAwait(false); if (!EqDefault.Equals(outerKeyMap(source.Result), innerKeyMap(inner.Result))) { throw new OperationCanceledException(); } return project(source.Result, inner.Result); } [Pure] public static async ValueTask GroupJoin(this ValueTask source, ValueTask inner, Func outerKeyMap, Func innerKeyMap, Func, V> project) { T t = await source.ConfigureAwait(false); return project(t, inner.Where(u => EqDefault.Equals(outerKeyMap(t), innerKeyMap(u)))); } [Pure] public static async ValueTask Plus(this ValueTask ma, ValueTask mb) { try { return await ma.ConfigureAwait(false); } catch { return await mb.ConfigureAwait(false); } } [Pure] public static async ValueTask PlusFirst(this ValueTask ma, ValueTask mb) => await ma.AsTask().PlusFirst(mb.AsTask()); /// /// Cast a ValueTask to a ValueTask〈A〉 (may throw if underlying value doesn't exist) /// public static ValueTask Cast(this ValueTask source) => new(source.AsTask().Cast()); public static async ValueTask ToUnit(this ValueTask source) { await source.ConfigureAwait(false); return unit; } /// /// Tasks a lazy sequence of tasks and iterates them in a 'measured way'. A default window size of /// `Sys.DefaultAsyncSequenceConcurrency` tasks is used, which by default means there are /// `Sys.DefaultAsyncSequenceConcurrency / 2` 'await streams'. An await stream essentially awaits one /// task from the sequence, and on completion goes and gets the next task from the lazy sequence and /// awaits that too. This continues until the end of the lazy sequence, or forever for infinite streams. /// public static ValueTask WindowIter(this IEnumerable> ma, Action f) => WindowIter(ma, SysInfo.DefaultAsyncSequenceParallelism, f); /// /// Tasks a lazy sequence of tasks and iterates them in a 'measured way'. A default window size of /// `windowSize` tasks is used, which means there are `windowSize` 'await streams'. An await stream /// essentially awaits one task from the sequence, and on completion goes and gets the next task from /// the lazy sequence and awaits that too. This continues until the end of the lazy sequence, or forever /// for infinite streams. Therefore there are at most `windowSize` tasks running concurrently. /// public static async ValueTask WindowIter(this IEnumerable> ma, int windowSize, Action f) { var sync = new object(); using var iter = ma.GetEnumerator(); (bool Success, ValueTask Task) GetNext() { lock (sync) { return iter.MoveNext() ? (true, iter.Current) : default; } } var tasks = new List>(); for (var i = 0; i < windowSize; i++) { var (s, outerTask) = GetNext(); if (!s) break; tasks.Add(outerTask.Bind(async oa => { f(oa); while (true) { var next = GetNext(); if (!next.Success) return unit; var a = await next.Task.ConfigureAwait(false); f(a); } })); } await Task.WhenAll(tasks.Select(t => t.AsTask())).ConfigureAwait(false); return unit; } /// /// Tasks a lazy sequence of tasks and iterates them in a 'measured way'. A default window size of /// `Sys.DefaultAsyncSequenceConcurrency` tasks is used, which means there are `Environment.ProcessorCount / 2` /// 'await streams' (by default). An await stream essentially awaits one task from the sequence, and on /// completion goes and gets the next task from the lazy sequence and awaits that too. This continues until the /// end of the lazy sequence, or forever for infinite streams. /// internal static ValueTask> WindowMap( this IEnumerable> ma, Func f, CancellationToken token) => WindowMap(ma, SysInfo.DefaultAsyncSequenceParallelism, f, token); /// /// Tasks a lazy sequence of tasks and maps them in a 'measured way'. A default window size of /// `windowSize` tasks is used, which means there are `windowSize` 'await streams'. An await stream /// essentially awaits one task from the sequence, and on completion goes and gets the next task from /// the lazy sequence and awaits that too. This continues until the end of the lazy sequence, or forever /// for infinite streams. Therefore there are at most `windowSize` tasks running concurrently. /// internal static async ValueTask> WindowMap( this IEnumerable> ma, int windowSize, Func f, CancellationToken token) => await ma.Select(va => va.AsTask()).WindowMap(windowSize, f, token); } ================================================ FILE: LanguageExt.Core/Concurrency/ValueTask/ValueTask.Prelude.cs ================================================ using System; using System.Diagnostics.Contracts; using System.Threading.Tasks; using LanguageExt.Common; namespace LanguageExt; public static partial class Prelude { /// /// Convert a value to a Task that completes immediately /// [Pure] public static ValueTask ValueTaskSucc(A self) => new (self); /// /// Convert a value to a Task that completes immediately /// [Pure] public static ValueTask ValueTaskFail(Exception ex) => Task.FromException(ex).ToValue(); /// /// Flatten the nested Task type /// [Pure] public static ValueTask flatten(ValueTask> self) => self.Flatten(); /// /// Flatten the nested Task type /// [Pure] public static ValueTask flatten(ValueTask>> self) => self.Flatten(); /// /// Get the Count of a Task T. Returns either 1 or 0 if cancelled or faulted. /// [Pure] public static ValueTask count(ValueTask self) => self.Count(); /// /// Monadic bind operation for Task /// [Pure] public static ValueTask bind(ValueTask self, Func> bind) => self.Bind(bind); /// /// Returns false if the Task is cancelled or faulted, otherwise /// it returns the result of pred(Result) /// [Pure] public static ValueTask exists(ValueTask self, Func pred) => self.Exists(pred); /// /// Returns false if the Task is cancelled or faulted, otherwise /// it returns the result of pred(Result) /// [Pure] public static ValueTask existsAsync(ValueTask self, Func> pred) => self.ExistsAsync(pred); /// /// Returns false if the Task is cancelled or faulted, otherwise /// it returns the result of pred(Result) /// [Pure] public static ValueTask forall(ValueTask self, Func pred) => self.ForAll(pred); /// /// Returns false if the Task is cancelled or faulted, otherwise /// it returns the result of pred(Result) /// [Pure] public static ValueTask forallAsync(ValueTask self, Func> pred) => self.ForAllAsync(pred); /// /// Filters the task. This throws a BottomException when pred(Result) /// returns false /// [Pure] public static ValueTask filter(ValueTask self, Func pred) => self.Filter(pred); /// /// Iterates the Task. Invokes f(Result) if not faulted or cancelled /// public static ValueTask iter(ValueTask self, Action f) => self.Iter(f); /// /// Returns map(Result) if not faulted or cancelled. /// [Pure] public static ValueTask map(ValueTask self, Func map) => self.Map(map); /// /// Returns map(Result) if not faulted or cancelled. /// [Pure] public static ValueTask mapAsync(ValueTask self, Func> map) => self.MapAsync(map); [Pure] public static ValueTask plus(ValueTask ma, ValueTask mb) => ma.Plus(mb); [Pure] public static ValueTask plusFirst(ValueTask ma, ValueTask mb) => ma.PlusFirst(mb); /// /// Returns the first successful computation /// /// Bound value /// The first computation to run /// The rest of the computations to run /// The first computation that succeeds [Pure] public static ValueTask choice(ValueTask ma, params ValueTask[] tail) => choice(Cons(ma, tail)); /// /// Returns the first successful computation /// /// Bound value /// Sequence of computations to run /// The first computation that succeeds [Pure] public static async ValueTask choice(Seq> xs) { foreach (var x in xs) { try { return await x.ConfigureAwait(false); } catch { // ignore } } throw new BottomException(); } /// /// Apply /// /// Function to apply the applicative to /// Applicative to apply /// Applicative of type FB derived from Applicative of B [Pure] public static async ValueTask apply(ValueTask> fab, ValueTask fa) { await Task.WhenAll(fab.AsTask(), fa.AsTask()).ConfigureAwait(false); return fab.Result(fa.Result); } /// /// Apply /// /// Function to apply the applicative to /// Applicative to apply /// Applicative of type FB derived from Applicative of B [Pure] public static ValueTask apply(Func fab, ValueTask fa) => fa.Map(fab); /// /// Apply /// /// Function to apply the applicative to /// Applicative a to apply /// Applicative b to apply /// Applicative of type FC derived from Applicative of C [Pure] public static async ValueTask apply(ValueTask> fabc, ValueTask fa, ValueTask fb) { await Task.WhenAll(fabc.AsTask(), fa.AsTask(), fb.AsTask()).ConfigureAwait(false); return fabc.Result(fa.Result, fb.Result); } /// /// Apply /// /// Function to apply the applicative to /// Applicative a to apply /// Applicative b to apply /// Applicative of type FC derived from Applicative of C [Pure] public static async ValueTask apply(Func fabc, ValueTask fa, ValueTask fb) { await Task.WhenAll(fa.AsTask(), fb.AsTask()).ConfigureAwait(false); return fabc(fa.Result, fb.Result); } /// /// Apply /// /// Function to apply the applicative to /// Applicative to apply /// Applicative of type f(b -> c) derived from Applicative of Func〈B, C〉 [Pure] public static async ValueTask> apply(ValueTask> fabc, ValueTask fa) { await Task.WhenAll(fabc.AsTask(), fa.AsTask()).ConfigureAwait(false); return curry(fabc.Result)(fa.Result); } /// /// Apply /// /// Function to apply the applicative to /// Applicative to apply /// Applicative of type f(b -> c) derived from Applicative of Func〈B, C〉 [Pure] public static ValueTask> apply(Func fabc, ValueTask fa) => fa.Map(curry(fabc)); /// /// Apply /// /// Function to apply the applicative to /// Applicative to apply /// Applicative of type f(b -> c) derived from Applicative of Func〈B, C〉 [Pure] public static async ValueTask> apply(ValueTask>> fabc, ValueTask fa) { await Task.WhenAll(fabc.AsTask(), fa.AsTask()).ConfigureAwait(false); return fabc.Result(fa.Result); } /// /// Apply /// /// Function to apply the applicative to /// Applicative to apply /// Applicative of type f(b -> c) derived from Applicative of Func〈B, C〉 [Pure] public static ValueTask> apply(Func> fabc, ValueTask fa) => fa.Map(fabc); } ================================================ FILE: LanguageExt.Core/Concurrency/ValueTask/ValueTasks.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics.Contracts; using System.Threading; using System.Threading.Tasks; using static LanguageExt.Prelude; namespace LanguageExt; internal static class ValueTasks { [Pure] public static async ValueTask ForAll(IEnumerable> fs, Func pred, CancellationToken token = default) { var ra = await fs.WindowMap(pred, default).ConfigureAwait(false); return ra.AsIterable().ForAll(identity); } [Pure] public static async ValueTask ForAll(IEnumerable> fs, Func pred, int windowSize, CancellationToken token = default) { var ra = await fs.WindowMap(windowSize, pred, default).ConfigureAwait(false); return ra.AsIterable().ForAll(identity); } [Pure] public static async ValueTask Exists(IEnumerable> fs, Func pred, CancellationToken token = default) { var ra = await fs.WindowMap(pred, default).ConfigureAwait(false); return ra.AsIterable().Exists(identity); } [Pure] public static async ValueTask Exists(IEnumerable> fs, Func pred, int windowSize, CancellationToken token = default) { var ra = await fs.WindowMap(windowSize, pred, default).ConfigureAwait(false); return ra.AsIterable().Exists(identity); } } ================================================ FILE: LanguageExt.Core/Concurrency/VectorClock/Relation.cs ================================================ namespace LanguageExt { /// /// The relations two vector clocks may find themselves in. /// public enum Relation { Causes, CausedBy, Concurrent } } ================================================ FILE: LanguageExt.Core/Concurrency/VectorClock/VectorClock.A.cs ================================================ using System; using System.Collections.Generic; using LanguageExt.ClassInstances; using static LanguageExt.Prelude; namespace LanguageExt; /// /// /// To create a vector clock, start from `Empty` or `Single` and `Insert` /// elements into it. As a shortcut, `fromList` just inserts all the /// elements in a list, in order. /// /// /// var vc = VectorClock〈char〉.Empty; /// vc = vc.Insert('a', 1); /// vc = vc.Insert('b', 2); /// vc == VectorClock〈char〉.fromList(Seq(('a', 1), ('b', 2))) /// /// /// Note that, for different keys, the order of insertion does not /// matter: /// /// /// fromList(Seq(('a', 1), ('b', 2)) == fromList(Seq(('b', 2), ('a', 1)) /// /// /// Once you have a given vector clock, you can 'lookup' its fields, /// check that keys are 'member's, or convert it back 'toList' form. /// /// /// vc.Lookup('a') == Some(1) /// vc.Lookup('c') == None /// /// /// The main operations that you would do with a vector clock are to /// increment the entry corresponding to the current process and to /// update the process's vector clock with the 'max' of its and the /// received message's clocks. /// /// /// vc.Inc('a') == Some [('a', 2), ('b', 2)] /// VectorClock.max( [('a', 1), ('b', 2)], [('c', 3), ('b', 1)] ) == [('a', 1), ('b', 2), ('c' 3)] /// /// /// Finally, upon receiving different messages, you may wish to /// discover the relationship, if any, between them. This /// information could be useful in determining the correct order to /// process the messages. /// /// /// VectorClock.relation (fromList [('a', 1), ('b', 2)], fromList [('a', 2), ('b', 2)]) == Causes /// VectorClock.relation (fromList [('a', 2), ('b', 2)], fromList [('a', 1), ('b', 2)]) == CausedBy /// VectorClock.relation (fromList [('a', 2), ('b', 2)], fromList [('a', 1), ('b', 3)]) == Concurrent /// /// /// A vector clock is, conceptually, an associative list sorted by the /// value of the key, where each key appears only once. /// /// public record VectorClock(Seq<(A, long)> Entries) where A : IComparable { public static readonly VectorClock Empty = new(Seq<(A, long)>()); public virtual bool Equals(VectorClock? rhs) => rhs is not null && GetHashCode() == rhs.GetHashCode() && Count == rhs.Count && Entries.Zip(rhs.Entries).ForAll(p => equals, A>(p.First.Item1, p.Second.Item1)) && Entries.Zip(rhs.Entries).ForAll(p => p.First.Item2 == p.Second.Item2); public override int GetHashCode() => Entries.GetHashCode(); /// /// A vector clock with a single element /// public static VectorClock Single(A x, long y) => fromList(Seq((x, y))); /// /// Insert each entry in the list one at a time. /// public static VectorClock fromList(Seq<(A x, long y)> list) => list.Fold(Empty, (vc, pair) => vc.Insert(pair.x, pair.y)); /// /// All the entries in the vector clock. Note that this is /not/ the inverse of 'fromList' /// public Seq<(A x, long y)> ToSeq() => Entries; /// /// Is the vector clock empty? /// public bool IsEmpty => Entries.IsEmpty; /// /// The number of entries in the vector clock. /// public int Count => Entries.Count; /// /// Lookup the value for a key in the vector clock and remove the corresponding entry /// public (Option Value, VectorClock Clock) Extract(A index) { Option value = default; return (value, new VectorClock(go(Entries))); Seq<(A x, long y)> go(Seq<(A x, long y)> zs) { var res = Seq<(A x, long y)>.Empty; foreach (var z in zs) { if (equals, A>(z.x, index)) { value = z.y; } else { res = res.Add(z); } } return res; } } /// /// Lookup the value for a key in the vector clock. /// public Option Lookup(A index) { foreach (var z in Entries) { if (equals, A>(z.Item1, index)) return z.Item2; } return None; } /// /// Is a member? /// public bool Contains(A index) => Lookup(index).IsSome; /// /// Delete /// public VectorClock Remove(A index) => new (Entries.Filter(e => !equals, A>(e.Item1, index)).ToSeq()); /// /// Insert or replace the entry for a key. /// public VectorClock Insert(A index, long value) { return new VectorClock(go(Entries)); Seq<(A, long)> go(Seq<(A, long)> entries) => entries.IsEmpty ? Seq((index, value)) : entries.Head.Value switch { (var x1, _) xy when lessThan, A>(x1, index) => xy.Cons(go(entries.Tail)), (var x1, _) when equals, A>(x1, index) => (index, value).Cons(entries.Tail), var xy => (index, value).Cons(xy.Cons(entries.Tail)), }; } /// /// Increment the entry for a key by 1 /// public Option> Inc(A index) => Lookup(index).Map(y => Insert(index, y + 1)); /// /// Increment the entry for a key by 1 /// public VectorClock Inc(A index, long defaultValue) => Lookup(index).Case switch { long y => Insert(index, y + 1), _ => Insert(index, defaultValue + 1) }; /// /// Combine two vector clocks entry-by-entry /// /// a function that takes the /key/, the value of the entry in /// the left hand vector clock, if it exists, the value in the /// right hand vector clock, if it exists, and, if it wishes to /// keep a value for this /key/ in the resulting vector clock, /// returns it. /// the left hand vector clock /// he right hand vector clock /// public static VectorClock combine( Func, Option, Option> f, VectorClock vc1, VectorClock vc2) { return new VectorClock(go(vc1.Entries, vc2.Entries).Somes()); Seq> go(Seq<(A, long)> es1, Seq<(A, long)> es2) => (es1.IsEmpty, es2.IsEmpty) switch { (true, _) => es2.Map(xy => mk(xy.Item1, f(xy.Item1, None, Some(xy.Item2)))), (_, true) => es1.Map(xy => mk(xy.Item1, f(xy.Item1, Some(xy.Item2), None))), _ => compare, A>(es1.Head.Value.Item1, es2.Head.Value.Item1) switch { var c when c < 0 => mk(es1.Head.Value.Item1, f(es1.Head.Value.Item1, Some(es1.Head.Value.Item2), None)).Cons(go(es1.Tail, es2)), var c when c == 0 => mk(es1.Head.Value.Item1, f(es1.Head.Value.Item1, Some(es1.Head.Value.Item2), Some(es2.Head.Value.Item2))).Cons(go(es1.Tail, es2.Tail)), _ => mk(es2.Head.Value.Item1, f(es2.Head.Value.Item1, None, Some(es2.Head.Value.Item2))).Cons(go(es1, es2.Tail)), } }; static Option<(A, long)> mk(A x, Option v) => v.Map(y => (x, y)); } /// /// The maximum of the two vector clocks. /// public VectorClock Max(VectorClock vc2) => max(this, vc2); /// /// The maximum of the two vector clocks. /// public static VectorClock max(VectorClock vc1, VectorClock vc2) { return combine(maxEntry, vc1, vc2); static Option maxEntry(A _, Option ea, Option eb) => (ea.Case, eb.Case) switch { (null, null) => None, (long x, null) => Some(x), (null, long y) => Some(y), (long x, long y) => Some(Math.Max(x, y)), _ => None }; } /// /// The relation between the two vector clocks. /// public Relation Relation(VectorClock vc2) => relation(this, vc2); /// /// The relation between the two vector clocks. /// public static Relation relation(VectorClock vc1, VectorClock vc2) { return go(vc1.Entries, vc2.Entries); static Relation go(Seq<(A, long)> es1, Seq<(A, long)> es2) => (es1.IsEmpty, es2.IsEmpty) switch { (false, false) => equals, A>(es1.Head.Value.Item1, es2.Head.Value.Item1) ? es1.Head.Value.Item2 == es2.Head.Value.Item2 ? go(es1.Tail, es2.Tail) : es1.Head.Value.Item2 < es2.Head.Value.Item2 ? checkCauses(es1.Tail, es2.Tail) ? LanguageExt.Relation.Causes : LanguageExt.Relation.Concurrent : checkCauses(es2.Tail, es1.Tail) ? LanguageExt.Relation.CausedBy : LanguageExt.Relation.Concurrent : lessThan, A>(es1.Head.Value.Item1, es2.Head.Value.Item1) ? checkCauses(es2, es1.Tail) ? LanguageExt.Relation.CausedBy : LanguageExt.Relation.Concurrent : greaterThan, A>(es1.Head.Value.Item1, es2.Head.Value.Item1) ? checkCauses(es1, es2.Tail) ? LanguageExt.Relation.Causes : LanguageExt.Relation.Concurrent : LanguageExt.Relation.Concurrent, (true, _) => LanguageExt.Relation.Causes, (_, true) => LanguageExt.Relation.CausedBy, }; static bool checkCauses(Seq<(A, long)> es1, Seq<(A, long)> es2) => (es1.IsEmpty, es2.IsEmpty) switch { (false, false) => equals, A>(es1.Head.Value.Item1, es2.Head.Value.Item1) ? es1.Head.Value.Item2 <= es2.Head.Value.Item2 && checkCauses(es1.Tail, es2.Tail) : !lessThan, A>(es1.Head.Value.Item1, es2.Head.Value.Item1) && checkCauses(es1, es2.Tail), (true, _) => true, _ => false }; } /// /// Short-hand for relation(vc1, vc2) == Relation.Causes /// public bool Causes(VectorClock vc2) => causes(this, vc2); /// /// Short-hand for relation(vc1, vc2) == Relation.Causes /// public static bool causes(VectorClock vc1, VectorClock vc2) => relation(vc1, vc2) == LanguageExt.Relation.Causes; /// /// If vc2 causes vc1, compute the smallest vc3 /// /// Note that the /first/ parameter is the newer vector clock. public Option> Diff(VectorClock vc2) => diff(this, vc2); /// /// If vc2 causes vc1, compute the smallest vc3 /// /// Note that the /first/ parameter is the newer vector clock. public static Option> diff(VectorClock vc1, VectorClock vc2) { return vc1 == vc2 ? Some(Empty) : causes(vc2, vc1) ? Some(combine(diffOne, vc1, vc2)) : None; static Option diffOne(A _, Option ox, Option oy) => (ox.Case, oy.Case) switch { (null, null) => None, (long x, null) => Some(x), (null, long) => throw new InvalidOperationException("diff broken"), (long x, long y) => x == y ? None : Some(x), _ => throw new InvalidOperationException("diff broken") }; } bool? valid; public bool Valid { get { if (valid.HasValue) return valid.Value; var keys = Entries.Map(e => e.Item1); var sorted = keys.Sort, A>() == keys; var distinct = Entries.Distinct, TLong, A, long>, (A, long)>().Count == Entries.Count; valid = sorted && distinct; return valid.Value; } } } ================================================ FILE: LanguageExt.Core/Concurrency/VectorClock/VectorClock.OrdA.NumB.A.B.cs ================================================ using System; using System.Collections.Generic; using LanguageExt.ClassInstances; using LanguageExt.Traits; using static LanguageExt.Trait; using static LanguageExt.Prelude; namespace LanguageExt; /// /// /// To create a vector clock, start from `Empty` or `Single` and `Insert` /// elements into it. As a shortcut, `fromList` just inserts all the /// elements in a list, in order. /// /// /// var vc = VectorClock〈char〉.Empty; /// vc = vc.Insert('a', 1); /// vc = vc.Insert('b', 2); /// vc == VectorClock〈char〉.fromList(Seq(('a', 1), ('b', 2))) /// /// /// Note that, for different keys, the order of insertion does not /// matter: /// /// /// fromList(Seq(('a', 1), ('b', 2)) == fromList(Seq(('b', 2), ('a', 1)) /// /// /// Once you have a given vector clock, you can 'lookup' its fields, /// check that keys are 'member's, or convert it back 'toList' form. /// /// /// vc.Lookup('a') == Some(1) /// vc.Lookup('c') == None /// /// /// The main operations that you would do with a vector clock are to /// increment the entry corresponding to the current process and to /// update the process's vector clock with the 'max' of its and the /// received message's clocks. /// /// /// vc.Inc('a') == Some [('a', 2), ('b', 2)] /// VectorClock.max( [('a', 1), ('b', 2)], [('c', 3), ('b', 1)] ) == [('a', 1), ('b', 2), ('c' 3)] /// /// /// Finally, upon receiving different messages, you may wish to /// discover the relationship, if any, between them. This /// information could be useful in determining the correct order to /// process the messages. /// /// /// VectorClock.relation (fromList [('a', 1), ('b', 2)], fromList [('a', 2), ('b', 2)]) == Causes /// VectorClock.relation (fromList [('a', 2), ('b', 2)], fromList [('a', 1), ('b', 2)]) == CausedBy /// VectorClock.relation (fromList [('a', 2), ('b', 2)], fromList [('a', 1), ('b', 3)]) == Concurrent /// /// /// A vector clock is, conceptually, an associative list sorted by the /// value of the key, where each key appears only once. /// /// public record VectorClock(Seq<(A, B)> Entries) where OrdA : Ord where NumB : Num { /// /// Empty vector clock /// public static readonly VectorClock Empty = new(Seq<(A, B)>()); public virtual bool Equals(VectorClock? rhs) => rhs is not null && GetHashCode() == rhs.GetHashCode() && Count == rhs.Count && Entries.Zip(rhs.Entries).ForAll(p => equals(p.First.Item1, p.Second.Item1)) && Entries.Zip(rhs.Entries).ForAll(p => equals(p.First.Item2, p.Second.Item2)); public override int GetHashCode() => Entries.GetHashCode(); /// /// A vector clock with a single element /// public static VectorClock Single(A x, B y) => fromList(Seq((x, y))); /// /// Insert each entry in the list one at a time. /// public static VectorClock fromList(Seq<(A x, B y)> list) => list.Fold(Empty, (vc, pair) => vc.Insert(pair.x, pair.y)); /// /// All the entries in the vector clock. Note that this is /not/ the inverse of 'fromList' /// public Seq<(A x, B y)> ToList() => Entries; /// /// Is the vector clock empty? /// public bool IsEmpty => Entries.IsEmpty; /// /// The number of entries in the vector clock. /// public int Count => Entries.Count; /// /// Lookup the value for a key in the vector clock and remove the corresponding entry /// public (Option Value, VectorClock Clock) Extract(A index) { Option value = default; return (value, new VectorClock(go(Entries))); Seq<(A x, B y)> go(Seq<(A x, B y)> zs) { var res = Seq<(A x, B y)>.Empty; foreach (var z in zs) { if (equals(z.x, index)) { value = z.y; } else { res = res.Add(z); } } return res; } } /// /// Lookup the value for a key in the vector clock. /// public Option Lookup(A index) { foreach (var z in Entries) { if (equals(z.Item1, index)) return z.Item2; } return None; } /// /// Is a member? /// public bool Contains(A index) => Lookup(index).IsSome; /// /// Delete /// public VectorClock Remove(A index) => new (Entries.Filter(e => !OrdA.Equals(e.Item1, index)).ToSeq()); /// /// Insert or replace the entry for a key. /// public VectorClock Insert(A index, B value) { return new VectorClock(go(Entries)); Seq<(A, B)> go(Seq<(A, B)> entries) => entries.IsEmpty ? Seq((index, value)) : entries.Head.Value switch { (var x1, _) xy when lessThan(x1, index) => xy.Cons(go(entries.Tail)), var (x1, _) when equals(x1, index) => (index, value).Cons(entries.Tail), var xy => (index, value).Cons(xy.Cons(entries.Tail)) }; } /// /// Increment the entry for a key by 1 /// public Option> Inc(A index) => Lookup(index).Map(y => Insert(index, plus(y, fromInteger(1)))); /// /// Increment the entry for a key by 1 /// public VectorClock Inc(A index, B defaultValue) => Lookup(index).Case switch { B y => Insert(index, plus(y, fromInteger(1))), _ => Insert(index, plus(defaultValue, fromInteger(1))), }; /// /// Combine two vector clocks entry-by-entry /// /// a function that takes the /key/, the value of the entry in /// the left hand vector clock, if it exists, the value in the /// right hand vector clock, if it exists, and, if it wishes to /// keep a value for this /key/ in the resulting vector clock, /// returns it. /// the left hand vector clock /// he right hand vector clock /// public static VectorClock combine( Func, Option, Option> f, VectorClock vc1, VectorClock vc2) { return new VectorClock(go(vc1.Entries, vc2.Entries).Somes()); Seq> go(Seq<(A, B)> es1, Seq<(A, B)> es2) => (es1.IsEmpty, es2.IsEmpty) switch { (true, _) => es2.Map(xy => mk(xy.Item1, f(xy.Item1, None, Some(xy.Item2)))), (_, true) => es1.Map(xy => mk(xy.Item1, f(xy.Item1, Some(xy.Item2), None))), _ => compare(es1.Head.Value.Item1, es2.Head.Value.Item1) switch { < 0 => mk(es1.Head.Value.Item1, f(es1.Head.Value.Item1, Some(es1.Head.Value.Item2), None)).Cons(go(es1.Tail, es2)), 0 => mk(es1.Head.Value.Item1, f(es1.Head.Value.Item1, Some(es1.Head.Value.Item2), Some(es2.Head.Value.Item2))).Cons(go(es1.Tail, es2.Tail)), _ => mk(es2.Head.Value.Item1, f(es2.Head.Value.Item1, None, Some(es2.Head.Value.Item2))).Cons(go(es1, es2.Tail)) } }; static Option<(A, B)> mk(A x, Option v) => v.Map(y => (x, y)); } /// /// The maximum of the two vector clocks. /// public static VectorClock max(VectorClock vc1, VectorClock vc2) { return combine(maxEntry, vc1, vc2); static Option maxEntry(A _, Option ea, Option eb) => (ea.Case, eb.Case) switch { (null, null) => None, (B x, null) => Some(x), (null, B y) => Some(y), (B x, B y) => Some(Ord.max(x, y)), _ => None }; } /// /// The relation between the two vector clocks. /// public Relation Relation(VectorClock vc1, VectorClock vc2) => relation(this, vc2); /// /// The relation between the two vector clocks. /// public static Relation relation(VectorClock vc1, VectorClock vc2) { return go(vc1.Entries, vc2.Entries); static Relation go(Seq<(A, B)> es1, Seq<(A, B)> es2) => (es1.IsEmpty, es2.IsEmpty) switch { (false, false) => equals(es1.Head.Value.Item1, es2.Head.Value.Item1) ? equals(es1.Head.Value.Item2, es2.Head.Value.Item2) ? go(es1.Tail, es2.Tail) : lessThan(es1.Head.Value.Item2, es2.Head.Value.Item2) ? checkCauses(es1.Tail, es2.Tail) ? LanguageExt.Relation.Causes : LanguageExt.Relation.Concurrent : checkCauses(es2.Tail, es1.Tail) ? LanguageExt.Relation.CausedBy : LanguageExt.Relation.Concurrent : lessThan(es1.Head.Value.Item1, es2.Head.Value.Item1) ? checkCauses(es2, es1.Tail) ? LanguageExt.Relation.CausedBy : LanguageExt.Relation.Concurrent : greaterThan(es1.Head.Value.Item1, es2.Head.Value.Item1) ? checkCauses(es1, es2.Tail) ? LanguageExt.Relation.Causes : LanguageExt.Relation.Concurrent : LanguageExt.Relation.Concurrent, (true, _) => LanguageExt.Relation.Causes, (_, true) => LanguageExt.Relation.CausedBy }; static bool checkCauses(Seq<(A, B)> es1, Seq<(A, B)> es2) => (es1.IsEmpty, es2.IsEmpty) switch { (false, false) => equals(es1.Head.Value.Item1, es2.Head.Value.Item1) ? lessOrEq(es1.Head.Value.Item2, es2.Head.Value.Item2) && checkCauses(es1.Tail, es2.Tail) : !lessThan(es1.Head.Value.Item1, es2.Head.Value.Item1) && checkCauses(es1, es2.Tail), (true, _) => true, _ => false }; } /// /// Short-hand for relation(vc1, vc2) == Relation.Causes /// public bool Causes(VectorClock vc2) => causes(this, vc2); /// /// Short-hand for relation(vc1, vc2) == Relation.Causes /// public static bool causes(VectorClock vc1, VectorClock vc2) => relation(vc1, vc2) == LanguageExt.Relation.Causes; /// /// If vc2 causes vc1, compute the smallest vc3 /// /// Note that the /first/ parameter is the newer vector clock. public Option> Diff(VectorClock vc2) => diff(this, vc2); /// /// If vc2 causes vc1, compute the smallest vc3 /// /// Note that the /first/ parameter is the newer vector clock. public static Option> diff(VectorClock vc1, VectorClock vc2) { return vc1 == vc2 ? Some(Empty) : causes(vc2, vc1) ? Some(combine(diffOne, vc1, vc2)) : None; static Option diffOne(A _, Option ox, Option oy) => (ox.Case, oy.Case) switch { (null, null) => None, (B x, null) => Some(x), (null, B) => throw new InvalidOperationException("diff broken"), (B x, B y) => equals(x, y) ? None : Some(x), _ => None }; } bool? valid; public bool Valid { get { if (valid.HasValue) return valid.Value; var keys = Entries.Map(e => e.Item1); var sorted = keys.Sort() == keys; var distinct = Entries.Distinct, (A, B)>().Count == Entries.Count; valid = sorted && distinct; return valid.Value; } } } ================================================ FILE: LanguageExt.Core/Concurrency/VectorClock/VectorClock.cs ================================================ #nullable enable using System; using System.Collections.Generic; using LanguageExt.ClassInstances; using LanguageExt.Traits; using static LanguageExt.Trait; using static LanguageExt.Prelude; namespace LanguageExt { /// /// /// To create a vector clock, start from `Empty` or `Single` and `Insert` /// elements into it. As a shortcut, `fromList` just inserts all the /// elements in a list, in order. /// /// /// var vc = VectorClock〈char〉.Empty; /// vc = vc.Insert('a', 1); /// vc = vc.Insert('b', 2); /// vc == VectorClock〈char〉.fromList(Seq(('a', 1), ('b', 2))) /// /// /// Note that, for different keys, the order of insertion does not /// matter: /// /// /// fromList(Seq(('a', 1), ('b', 2)) == fromList(Seq(('b', 2), ('a', 1)) /// /// /// Once you have a given vector clock, you can 'lookup' its fields, /// check that keys are 'member's, or convert it back 'toList' form. /// /// /// vc.Lookup('a') == Some(1) /// vc.Lookup('c') == None /// /// /// The main operations that you would do with a vector clock are to /// increment the entry corresponding to the current process and to /// update the process's vector clock with the 'max' of its and the /// received message's clocks. /// /// /// vc.Inc('a') == Some [('a', 2), ('b', 2)] /// VectorClock.max( [('a', 1), ('b', 2)], [('c', 3), ('b', 1)] ) == [('a', 1), ('b', 2), ('c' 3)] /// /// /// Finally, upon receiving different messages, you may wish to /// discover the relationship, if any, between them. This /// information could be useful in determining the correct order to /// process the messages. /// /// /// VectorClock.relation (fromList [('a', 1), ('b', 2)], fromList [('a', 2), ('b', 2)]) == Causes /// VectorClock.relation (fromList [('a', 2), ('b', 2)], fromList [('a', 1), ('b', 2)]) == CausedBy /// VectorClock.relation (fromList [('a', 2), ('b', 2)], fromList [('a', 1), ('b', 3)]) == Concurrent /// /// /// A vector clock is, conceptually, an associative list sorted by the /// value of the key, where each key appears only once. /// /// public static class VectorClock { /// /// A vector clock with a single element /// public static VectorClock Single(A x, long y) where A : IComparable => VectorClock.Single(x, y); /// /// A vector clock with a single element /// public static VectorClock Single(A x, B y) where OrdA : Ord where NumB : Num => VectorClock.Single(x, y); /// /// Insert each entry in the list one at a time. /// public static VectorClock fromList(Seq<(A x, long y)> list) where A : IComparable => VectorClock.fromList(list); /// /// Insert each entry in the list one at a time. /// public static VectorClock fromList(Seq<(A x, B y)> list) where OrdA : Ord where NumB : Num => VectorClock.fromList(list); /// /// Combine two vector clocks entry-by-entry /// /// a function that takes the /key/, the value of the entry in /// the left hand vector clock, if it exists, the value in the /// right hand vector clock, if it exists, and, if it wishes to /// keep a value for this /key/ in the resulting vector clock, /// returns it. /// the left hand vector clock /// he right hand vector clock /// public static VectorClock combine( Func, Option, Option> f, VectorClock vc1, VectorClock vc2) where A : IComparable => VectorClock.combine(f, vc1, vc2); /// /// Combine two vector clocks entry-by-entry /// /// a function that takes the /key/, the value of the entry in /// the left hand vector clock, if it exists, the value in the /// right hand vector clock, if it exists, and, if it wishes to /// keep a value for this /key/ in the resulting vector clock, /// returns it. /// the left hand vector clock /// he right hand vector clock /// public static VectorClock combine( Func, Option, Option> f, VectorClock vc1, VectorClock vc2) where OrdA : Ord where NumB : Num => VectorClock.combine(f, vc1, vc2); /// /// The maximum of the two vector clocks. /// public static VectorClock max(VectorClock vc1, VectorClock vc2) where A : IComparable => VectorClock.max(vc1, vc2); /// /// The maximum of the two vector clocks. /// public static VectorClock max(VectorClock vc1, VectorClock vc2) where OrdA : Ord where NumB : Num => VectorClock.max(vc1, vc2); /// /// The relation between the two vector clocks. /// public static Relation relation(VectorClock vc1, VectorClock vc2) where A : IComparable => VectorClock.relation(vc1, vc2); /// /// The relation between the two vector clocks. /// public static Relation relation(VectorClock vc1, VectorClock vc2) where OrdA : Ord where NumB : Num => VectorClock.relation(vc1, vc2); /// /// Short-hand for relation(vc1, vc2) == Relation.Causes /// public static bool causes(VectorClock vc1, VectorClock vc2) where A : IComparable => VectorClock.causes(vc1, vc2); /// /// Short-hand for relation(vc1, vc2) == Relation.Causes /// public static bool causes(VectorClockvc1, VectorClock vc2) where OrdA : Ord where NumB : Num => VectorClock.causes(vc1, vc2); /// /// If vc2 causes vc1, compute the smallest vc3 /// /// Note that the /first/ parameter is the newer vector clock. public static Option> diff(VectorClock vc1, VectorClock vc2) where A : IComparable => VectorClock.diff(vc1, vc2); /// /// If vc2 causes vc1, compute the smallest vc3 /// /// Note that the /first/ parameter is the newer vector clock. public static Option> diff(VectorClock vc1, VectorClock vc2) where OrdA : Ord where NumB : Num => VectorClock.diff(vc1, vc2); } } #nullable disable ================================================ FILE: LanguageExt.Core/Concurrency/VersionHashMap/VersionHashMap.ConflictV.K.V.cs ================================================ using System; using System.Collections; using LanguageExt.ClassInstances; using System.Collections.Generic; using System.Diagnostics.Contracts; using System.Runtime.CompilerServices; namespace LanguageExt; /// /// /// Versioned hash-map. Each value has a vector-clock attached to it which understands the causality of updates /// from multiple actors. Actors are just unique keys that represent individual contributors. They could be client /// connections, nodes in a network, users, or whatever is appropriate to discriminate between commits. /// /// /// Deleted items are not removed from the hash-map, they are merely marked as deleted. This allows conflicts between /// writes and deletes to be resolved. /// /// /// Run `RemoveDeletedItemsOlderThan` to clean up items that have been deleted and are now just hanging around. Use /// a big enough delay that it won't conflict with other commits (this could be seconds, minutes, or longer: /// depending on the expected latency of writes to the hash-map). /// /// /// This is a mutable collection with atomic, thread-safe, updates. /// /// public class VersionHashMap : IEnumerable<(K Key, V Value)>, IEquatable> where ConflictV : Conflict { readonly VersionHashMap, string, K, V> Items; /// /// Empty version map /// public static VersionHashMap Empty { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new VersionHashMap(VersionHashMap, string, K, V>.Empty); } /// /// Constructor /// /// Trie map [MethodImpl(MethodImplOptions.AggressiveInlining)] VersionHashMap(VersionHashMap, string, K, V> items) => this.Items = items; /// /// 'this' accessor /// /// Key /// Version - this may be in a state of never existing, but won't ever fail [Pure] public Version this[K key] { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => Items.FindVersion(key); } /// /// Is the map empty /// [Pure] public bool IsEmpty { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => Items.IsEmpty; } /// /// Number of items in the map /// [Pure] public int Count { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => Items.Count; } /// /// Alias of Count /// [Pure] public int Length { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => Items.Count; } /// /// Atomically swap a key in the map. Allows for multiple operations on the hash-map in an entirely /// transactional and atomic way. /// /// Swap function, maps the current state of the value associated with the key to a new state /// Any functions passed as arguments may be run multiple times if there are multiple threads competing /// to update this data structure. Therefore the functions must spend as little time performing the injected /// behaviours as possible to avoid repeated attempts [MethodImpl(MethodImplOptions.AggressiveInlining)] public Unit SwapKey(K key, Func, Version> swap) => Items.SwapKey(key, swap); /// /// Atomically updates a new item in the map. If the key already exists, then the vector clocks, within the version /// vector, are compared to ascertain if the proposed version was caused-by, causes, or conflicts with the current /// version: /// /// * If the proposed version was caused-by the current version, then it is ignored. /// * If the proposed version causes the current version, then it is accepted and updated as the latest version /// * If the proposed version is in conflict with the current version, then values from both versions are /// passed to ConflictV.Append(v1, v2) for value resolution, and the pairwise-maximum of the two vector-clocks /// is taken to be the new 'time. /// /// /// Version to update [MethodImpl(MethodImplOptions.AggressiveInlining)] public Unit Update(Version version) => Items.Update(version); /// /// Remove items that are older than the specified time-stamp /// /// Cut off time-stamp [MethodImpl(MethodImplOptions.AggressiveInlining)] public Unit RemoveOlderThan(DateTime cutoff) => Items.RemoveOlderThan(cutoff); /// /// Remove items that are older than the specified time-stamp /// /// Cut off time-stamp [MethodImpl(MethodImplOptions.AggressiveInlining)] public Unit RemoveOlderThan(long timeStamp) => Items.RemoveOlderThan(timeStamp); /// /// Remove deleted items that are older than the specified time-stamp /// /// Cut off time-stamp [MethodImpl(MethodImplOptions.AggressiveInlining)] public Unit RemoveDeletedItemsOlderThan(DateTime cutoff) => Items.RemoveDeletedItemsOlderThan(cutoff); /// /// Remove deleted items that are older than the specified time-stamp /// /// Cut off time-stamp [MethodImpl(MethodImplOptions.AggressiveInlining)] public Unit RemoveDeletedItemsOlderThan(long timeStamp) => Items.RemoveDeletedItemsOlderThan(timeStamp); /// /// Retrieve a value from the map by key /// /// Key to find /// Found value [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Option Find(K value) => Items.Find(value); /// /// Retrieve a value from the map by key /// /// Key to find /// Found value [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Version FindVersion(K value) => Items.FindVersion(value); /// /// Enumerable of keys /// [Pure] public Iterable Keys { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => Items.Keys; } /// /// Enumerable of value /// [Pure] public Iterable Values { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => Items.Values; } /// /// Convert to a HashMap /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public HashMap ToHashMap() => Items.ToHashMap(); /// /// GetEnumerator - IEnumerable interface /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public IEnumerator<(K Key, V Value)> GetEnumerator() => // ReSharper disable once NotDisposedResourceIsReturned Items.GetEnumerator(); /// /// GetEnumerator - IEnumerable interface /// [MethodImpl(MethodImplOptions.AggressiveInlining)] IEnumerator IEnumerable.GetEnumerator() => // ReSharper disable once NotDisposedResourceIsReturned Items.GetEnumerator(); [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Seq<(K Key, V Value)> ToSeq() => Items.ToSeq(); /// /// Format the collection as `[(key: value), (key: value), (key: value), ...]` /// The ellipsis is used for collections over 50 items /// To get a formatted string with all the items, use `ToFullString` /// or `ToFullArrayString`. /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public override string ToString() => Items.ToString(); /// /// Format the collection as `(key: value), (key: value), (key: value), ...` /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public string ToFullString(string separator = ", ") => Items.ToFullString(separator); /// /// Format the collection as `[(key: value), (key: value), (key: value), ...]` /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public string ToFullArrayString(string separator = ", ") => Items.ToFullArrayString(separator); [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Iterable<(K Key, V Value)> AsIterable() => Items.AsIterable(); /// /// Returns True if 'other' is a proper subset of this set /// /// True if 'other' is a proper subset of this set [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool IsProperSubsetOf(IEnumerable<(K Key, V Value)> other) => Items.IsProperSubsetOf(other); /// /// Returns True if 'other' is a proper subset of this set /// /// True if 'other' is a proper subset of this set [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool IsProperSubsetOf(IEnumerable other) => Items.IsProperSubsetOf(other); /// /// Returns True if 'other' is a proper superset of this set /// /// True if 'other' is a proper superset of this set [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool IsProperSupersetOf(IEnumerable<(K Key, V Value)> other) => Items.IsProperSupersetOf(other); /// /// Returns True if 'other' is a proper superset of this set /// /// True if 'other' is a proper superset of this set [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool IsProperSupersetOf(IEnumerable other) => Items.IsProperSupersetOf(other); /// /// Returns True if 'other' is a superset of this set /// /// True if 'other' is a superset of this set [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool IsSubsetOf(IEnumerable<(K Key, V Value)> other) => Items.IsSubsetOf(other); /// /// Returns True if 'other' is a superset of this set /// /// True if 'other' is a superset of this set [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool IsSubsetOf(IEnumerable other) => Items.IsSubsetOf(other); /// /// Returns True if 'other' is a superset of this set /// /// True if 'other' is a superset of this set [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool IsSubsetOf(HashMap other) => Items.IsSubsetOf(other); /// /// Returns True if 'other' is a superset of this set /// /// True if 'other' is a superset of this set [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool IsSupersetOf(IEnumerable<(K Key, V Value)> other) => Items.IsSupersetOf(other); /// /// Returns True if 'other' is a superset of this set /// /// True if 'other' is a superset of this set [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool IsSupersetOf(IEnumerable rhs) => Items.IsSupersetOf(rhs); /// /// Returns the elements that are in both this and other /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public Unit Intersect(IEnumerable rhs) => Items.Intersect(rhs); /// /// Returns True if other overlaps this set /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Overlaps(IEnumerable<(K Key, V Value)> other) => Items.Overlaps(other); /// /// Returns True if other overlaps this set /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Overlaps(IEnumerable other) => Items.Overlaps(other); /// /// Returns this - rhs. Only the items in this that are not in /// rhs will be returned. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public Unit Except(IEnumerable rhs) => Items.Except(rhs); /// /// Equality of keys and values with `EqDefault〈V〉` used for values /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public override bool Equals(object? obj) => obj is VersionHashMap hm && Equals(hm); /// /// Equality of keys and values with `EqDefault〈V〉` used for values /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(VersionHashMap? other) => other is not null && Items.Equals(other.Items); [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public override int GetHashCode() => Items.GetHashCode(); /// /// Atomically filter out items that return false when a predicate is applied /// /// Predicate /// New map with items filtered [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public VersionHashMap Where(Func, bool> pred) => new VersionHashMap(Items.Filter(pred)); /// /// Atomically filter out items that return false when a predicate is applied /// /// Predicate /// New map with items filtered [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public VersionHashMap Where(Func, bool> pred) => new VersionHashMap(Items.Filter(pred)); /// /// Atomically filter out items that return false when a predicate is applied /// /// Predicate /// New map with items filtered [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public VersionHashMap Filter(Func, bool> pred) => new VersionHashMap(Items.Filter(pred)); /// /// Atomically filter out items that return false when a predicate is applied /// /// Predicate /// New map with items filtered [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public VersionHashMap Filter(Func, bool> pred) => new VersionHashMap(Items.Filter(pred)); /// /// Atomically filter out items that return false when a predicate is applied /// /// Predicate /// New map with items filtered [MethodImpl(MethodImplOptions.AggressiveInlining)] public Unit FilterInPlace(Func, bool> pred) => Items.FilterInPlace(pred); /// /// Atomically filter out items that return false when a predicate is applied /// /// Predicate /// New map with items filtered [MethodImpl(MethodImplOptions.AggressiveInlining)] public Unit FilterInPlace(Func, bool> pred) => Items.FilterInPlace(pred); /// /// Return true if all items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool ForAll(Func pred) => Items.ForAll(pred); /// /// Return true if all items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool ForAll(Func<(K Key, V Value), bool> pred) => Items.ForAll(pred); /// /// Return true if *all* items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool ForAll(Func, bool> pred) => Items.ForAll(pred); /// /// Return true if all items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool ForAll(Func pred) => Items.ForAll(pred); /// /// Return true if *any* items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Exists(Func pred) => Items.Exists(pred); /// /// Return true if *any* items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Exists(Func<(K Key, V Value), bool> pred) => Items.Exists(pred); /// /// Return true if *any* items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Exists(Func pred) => Items.Exists(pred); /// /// Atomically iterate through all key/value pairs in the map (in order) and execute an /// action on each /// /// Action to execute /// Unit [MethodImpl(MethodImplOptions.AggressiveInlining)] public Unit Iter(Action action) => Items.Iter(action); /// /// Atomically iterate through all values in the map (in order) and execute an /// action on each /// /// Action to execute /// Unit [MethodImpl(MethodImplOptions.AggressiveInlining)] public Unit Iter(Action action) => Items.Iter(action); /// /// Atomically iterate through all key/value pairs (as tuples) in the map (in order) /// and execute an action on each /// /// Action to execute /// Unit [MethodImpl(MethodImplOptions.AggressiveInlining)] public Unit Iter(Action<(K Key, V Value)> action) => Items.Iter(action); /// /// Atomically iterate through all key/value pairs in the map (in order) and execute an /// action on each /// /// Action to execute /// Unit [MethodImpl(MethodImplOptions.AggressiveInlining)] public Unit Iter(Action> action) => Items.Iter(action); /// /// Atomically folds all items in the map (in order) using the folder function provided. /// /// State type /// Initial state /// Fold function /// Folded state [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public S Fold(S state, Func folder) => Items.Fold(state, folder); /// /// Atomically folds all items in the map (in order) using the folder function provided. /// /// State type /// Initial state /// Fold function /// Folded state [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public S Fold(S state, Func folder) => Items.Fold(state, folder); } ================================================ FILE: LanguageExt.Core/Concurrency/VersionHashMap/VersionHashMap.ConflictV.OrdActor.EqK.Actor.K.V.cs ================================================ using System; using System.Threading; using System.Collections; using LanguageExt.Traits; using static LanguageExt.Prelude; using LanguageExt.ClassInstances; using System.Collections.Generic; using System.Diagnostics.Contracts; using System.Runtime.CompilerServices; namespace LanguageExt; /// /// /// Versioned hash-map. Each value has a vector-clock attached to it which understands the causality of updates /// from multiple actors. Actors are just unique keys that represent individual contributors. They could be client /// connections, nodes in a network, users, or whatever is appropriate to discriminate between commits. /// /// /// Deleted items are not removed from the hash-map, they are merely marked as deleted. This allows conflicts between /// writes and deletes to be resolved. /// /// /// Run `RemoveDeletedItemsOlderThan` to clean up items that have been deleted and are now just hanging around. Use /// a big enough delay that it won't conflict with other commits (this could be seconds, minutes, or longer: /// depending on the expected latency of writes to the hash-map). /// /// /// This is a mutable collection with atomic, thread-safe, updates. /// /// public class VersionHashMap : IEnumerable<(K Key, V Value)>, IEquatable> where OrdActor : Ord where EqK : Eq where ConflictV : Conflict { internal volatile TrieMap> Items; /// /// Empty version map /// public static VersionHashMap Empty { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new (TrieMap>.Empty); } /// /// Constructor /// /// Trie map [MethodImpl(MethodImplOptions.AggressiveInlining)] VersionHashMap(TrieMap> items) => Items = items; /// /// 'this' accessor /// /// Key /// Version - this may be in a state of never existing, but won't ever fail [Pure] public Version this[K key] { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => FindVersion(key); } /// /// Is the map empty /// [Pure] public bool IsEmpty { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => Items.IsEmpty; } /// /// Number of items in the map /// [Pure] public int Count { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => Items.Count; } /// /// Alias of Count /// [Pure] public int Length { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => Items.Count; } /// /// Atomically swap a key in the map. Allows for multiple operations on the hash-map in an entirely /// transactional and atomic way. /// /// Swap function, maps the current state of the value associated with the key to a new state /// Any functions passed as arguments may be run multiple times if there are multiple threads competing /// to update this data structure. Therefore the functions must spend as little time performing the injected /// behaviours as possible to avoid repeated attempts public Unit SwapKey(K key, Func, Version> swap) { SpinWait sw = default; while (true) { var oitems = Items; var okey = oitems.Find(key) .Map(v => v.ToVersion(key)) .IfNone(() => VersionNeverExistedVector.New(key)); var nversion = swap(okey); var nitems = oitems.AddOrMaybeUpdate(key, exists => exists.Put(nversion.ToVector()!), () => Optional(nversion.ToVector())); if(ReferenceEquals(oitems, nitems)) { // no change return default; } if (ReferenceEquals(Interlocked.CompareExchange(ref Items, nitems, oitems), oitems)) { return default; } else { sw.SpinOnce(); } } } /// /// Atomically updates a new item in the map. If the key already exists, then the vector clocks, within the version /// vector, are compared to ascertain if the proposed version was caused-by, causes, or conflicts with the current /// version: /// /// * If the proposed version was caused-by the current version, then it is ignored. /// * If the proposed version causes the current version, then it is accepted and updated as the latest version /// * If the proposed version is in conflict with the current version, then values from both versions are /// passed to ConflictV.Append(v1, v2) for value resolution, and the pairwise-maximum of the two vector-clocks /// is taken to be the new 'time. /// /// /// Version to update public Unit Update(Version nversion) { SpinWait sw = default; while (true) { var oitems = Items; var nitems = oitems.AddOrMaybeUpdate(nversion.Key, exists => exists.Put(nversion.ToVector()!), () => Optional(nversion.ToVector())); if(ReferenceEquals(oitems, nitems)) { // no change return default; } if (ReferenceEquals(Interlocked.CompareExchange(ref Items, nitems, oitems), oitems)) { return default; } else { sw.SpinOnce(); } } } /// /// Remove items that are older than the specified time-stamp /// /// Cut off time-stamp [MethodImpl(MethodImplOptions.AggressiveInlining)] public Unit RemoveOlderThan(DateTime cutoff) => RemoveOlderThan(cutoff.ToUniversalTime().Ticks); /// /// Remove items that are older than the specified time-stamp /// /// Cut off time-stamp [MethodImpl(MethodImplOptions.AggressiveInlining)] public Unit RemoveOlderThan(long timeStamp) => FilterInPlace((ts, _) => ts > timeStamp); /// /// Remove deleted items that are older than the specified time-stamp /// /// Cut off time-stamp [MethodImpl(MethodImplOptions.AggressiveInlining)] public Unit RemoveDeletedItemsOlderThan(DateTime cutoff) => RemoveDeletedItemsOlderThan(cutoff.ToUniversalTime().Ticks); /// /// Remove deleted items that are older than the specified time-stamp /// /// Cut off time-stamp [MethodImpl(MethodImplOptions.AggressiveInlining)] public Unit RemoveDeletedItemsOlderThan(long timeStamp) => FilterInPlace((ts, v) => v.IsSome || ts > timeStamp); /// /// Retrieve a value from the map by key /// /// Key to find /// Found value [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Option Find(K value) => Items.Find(value).Bind(static v => v.Value); /// /// Retrieve a value from the map by key /// /// Key to find /// Found value [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Version FindVersion(K key) => Items.Find(key).Match( Some: v => v.ToVersion(key), None: () => VersionNeverExistedVector.New(key)); /// /// Enumerable of keys /// [Pure] public Iterable Keys { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => AsIterable().Map(static x => x.Key); } /// /// Enumerable of value /// [Pure] public Iterable Values { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => AsIterable().Map(static x => x.Value); } /// /// Convert to a HashMap /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public HashMap ToHashMap() => Items.AsIterable().Choose(static x => x.Value.Value.Map(v => (x.Key, v))).ToHashMap(); /// /// GetEnumerator - IEnumerable interface /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public IEnumerator<(K Key, V Value)> GetEnumerator() => Items.AsIterable().Choose(static x => x.Value.Value.Map(v => (x.Key, v))).GetEnumerator(); /// /// GetEnumerator - IEnumerable interface /// [MethodImpl(MethodImplOptions.AggressiveInlining)] IEnumerator IEnumerable.GetEnumerator() => Items.GetEnumerator(); [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Seq<(K Key, V Value)> ToSeq() => toSeq(AsIterable()); /// /// Format the collection as `[(key: value), (key: value), (key: value), ...]` /// The ellipsis is used for collections over 50 items /// To get a formatted string with all the items, use `ToFullString` /// or `ToFullArrayString`. /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public override string ToString() => CollectionFormat.ToShortArrayString(AsIterable().Map(kv => $"({kv.Key}: {kv.Value})"), Count); /// /// Format the collection as `(key: value), (key: value), (key: value), ...` /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public string ToFullString(string separator = ", ") => CollectionFormat.ToFullString(AsIterable().Map(kv => $"({kv.Key}: {kv.Value})"), separator); /// /// Format the collection as `[(key: value), (key: value), (key: value), ...]` /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public string ToFullArrayString(string separator = ", ") => CollectionFormat.ToFullArrayString(AsIterable().Map(kv => $"({kv.Key}: {kv.Value})"), separator); [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Iterable<(K Key, V Value)> AsIterable() => Items.AsIterable().Choose(static x => x.Value.Value.Map(v => (x.Key, v))); /// /// Returns True if 'other' is a proper subset of this set /// /// True if 'other' is a proper subset of this set [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool IsProperSubsetOf(IEnumerable<(K Key, V Value)> other) => ToHashMap().IsProperSubsetOf(other); /// /// Returns True if 'other' is a proper subset of this set /// /// True if 'other' is a proper subset of this set [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool IsProperSubsetOf(IEnumerable other) => Items.IsProperSubsetOf(other); /// /// Returns True if 'other' is a proper superset of this set /// /// True if 'other' is a proper superset of this set [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool IsProperSupersetOf(IEnumerable<(K Key, V Value)> other) => ToHashMap().IsProperSupersetOf(other); /// /// Returns True if 'other' is a proper superset of this set /// /// True if 'other' is a proper superset of this set [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool IsProperSupersetOf(IEnumerable other) => Items.IsProperSupersetOf(other); /// /// Returns True if 'other' is a superset of this set /// /// True if 'other' is a superset of this set [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool IsSubsetOf(IEnumerable<(K Key, V Value)> other) => ToHashMap().IsSubsetOf(other); /// /// Returns True if 'other' is a superset of this set /// /// True if 'other' is a superset of this set [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool IsSubsetOf(IEnumerable other) => Items.IsSubsetOf(other); /// /// Returns True if 'other' is a superset of this set /// /// True if 'other' is a superset of this set [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool IsSubsetOf(HashMap other) => ToHashMap().IsSubsetOf(other.Value); /// /// Returns True if 'other' is a superset of this set /// /// True if 'other' is a superset of this set [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool IsSupersetOf(IEnumerable<(K Key, V Value)> other) => ToHashMap().IsSupersetOf(other); /// /// Returns True if 'other' is a superset of this set /// /// True if 'other' is a superset of this set [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool IsSupersetOf(IEnumerable rhs) => Items.IsSupersetOf(rhs); /// /// Returns the elements that are in both this and other /// public Unit Intersect(IEnumerable rhs) { SpinWait sw = default; var srhs = toSeq(rhs); while (true) { var oitems = this.Items; var nitems = this.Items.Intersect(srhs); if (ReferenceEquals(Interlocked.CompareExchange(ref this.Items, nitems, oitems), oitems)) { return default; } else { sw.SpinOnce(); } } } /// /// Returns True if other overlaps this set /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Overlaps(IEnumerable<(K Key, V Value)> other) => ToHashMap().Overlaps(other); /// /// Returns True if other overlaps this set /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Overlaps(IEnumerable other) => ToHashMap().Overlaps(other); /// /// Returns this - rhs. Only the items in this that are not in /// rhs will be returned. /// public Unit Except(IEnumerable rhs) { SpinWait sw = default; var srhs = toSeq(rhs); while (true) { var oitems = this.Items; var nitems = this.Items.Except(srhs); if (ReferenceEquals(Interlocked.CompareExchange(ref this.Items, nitems, oitems), oitems)) { return default; } else { sw.SpinOnce(); } } } /// /// Equality of keys and values with `EqDefault〈V〉` used for values /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public override bool Equals(object? obj) => obj is VersionHashMap hm && Equals(hm); /// /// Equality of keys and values with `EqDefault〈V〉` used for values /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(VersionHashMap? other) => other is not null && Items.Equals(other.Items); [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public override int GetHashCode() => Items.GetHashCode(); /// /// Atomically filter out items that return false when a predicate is applied /// /// Predicate /// New map with items filtered [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public VersionHashMap Where(Func, bool> pred) => new (Items.Filter(v => pred(v.TimeStamp, v.Value))); /// /// Atomically filter out items that return false when a predicate is applied /// /// Predicate /// New map with items filtered [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public VersionHashMap Where(Func, bool> pred) => new VersionHashMap(Items.Filter((k, v) => pred(k, v.TimeStamp, v.Value))); /// /// Atomically filter out items that return false when a predicate is applied /// /// Predicate /// New map with items filtered [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public VersionHashMap Filter(Func, bool> pred) => new VersionHashMap(Items.Filter(v => pred(v.TimeStamp, v.Value))); /// /// Atomically filter out items that return false when a predicate is applied /// /// Predicate /// New map with items filtered [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public VersionHashMap Filter(Func, bool> pred) => new VersionHashMap(Items.Filter((k, v) => pred(k, v.TimeStamp, v.Value))); /// /// Atomically filter out items that return false when a predicate is applied /// /// Predicate /// New map with items filtered public Unit FilterInPlace(Func, bool> pred) { SpinWait sw = default; while (true) { var oitems = Items; var nitems = oitems.Filter(v => pred(v.TimeStamp, v.Value)); if (ReferenceEquals(oitems, nitems)) { // no change return default; } if (ReferenceEquals(Interlocked.CompareExchange(ref Items, nitems, oitems), oitems)) { return default; } else { sw.SpinOnce(); } } } /// /// Atomically filter out items that return false when a predicate is applied /// /// Predicate /// New map with items filtered public Unit FilterInPlace(Func, bool> pred) { SpinWait sw = default; while (true) { var oitems = Items; var nitems = oitems.Filter((k, v) => pred(k, v.TimeStamp, v.Value)); if (ReferenceEquals(oitems, nitems)) { // no change return default; } if (ReferenceEquals(Interlocked.CompareExchange(ref Items, nitems, oitems), oitems)) { return default; } else { sw.SpinOnce(); } } } /// /// Return true if all items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool ForAll(Func pred) { foreach (var (key, value) in AsIterable()) { if (!pred(key, value)) return false; } return true; } /// /// Return true if all items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool ForAll(Func<(K Key, V Value), bool> pred) => AsIterable().Map(kv => (kv.Key, kv.Value)).ForAll(pred); /// /// Return true if *all* items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool ForAll(Func, bool> pred) => AsIterable().Map(kv => new KeyValuePair(kv.Key, kv.Value)).ForAll(pred); /// /// Return true if all items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool ForAll(Func pred) => AsIterable().Map(static x => x.Value).ForAll(pred); /// /// Return true if *any* items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Exists(Func pred) { foreach (var (key, value) in AsIterable()) { if (pred(key, value)) return true; } return false; } /// /// Return true if *any* items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Exists(Func<(K Key, V Value), bool> pred) => AsIterable().Map(kv => (kv.Key, kv.Value)).Exists(pred); /// /// Return true if *any* items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Exists(Func pred) => AsIterable().Map(static x => x.Value).Exists(pred); /// /// Atomically iterate through all key/value pairs in the map (in order) and execute an /// action on each /// /// Action to execute /// Unit [MethodImpl(MethodImplOptions.AggressiveInlining)] public Unit Iter(Action action) { foreach (var (key, value) in this) { action(key, value); } return unit; } /// /// Atomically iterate through all values in the map (in order) and execute an /// action on each /// /// Action to execute /// Unit [MethodImpl(MethodImplOptions.AggressiveInlining)] public Unit Iter(Action action) { foreach (var item in this) { action(item.Value); } return unit; } /// /// Atomically iterate through all key/value pairs (as tuples) in the map (in order) /// and execute an action on each /// /// Action to execute /// Unit [MethodImpl(MethodImplOptions.AggressiveInlining)] public Unit Iter(Action<(K Key, V Value)> action) { foreach (var item in this) { action(item); } return unit; } /// /// Atomically iterate through all key/value pairs in the map (in order) and execute an /// action on each /// /// Action to execute /// Unit [MethodImpl(MethodImplOptions.AggressiveInlining)] public Unit Iter(Action> action) { foreach (var item in this) { action(new KeyValuePair(item.Key, item.Value)); } return unit; } /// /// Atomically folds all items in the map (in order) using the folder function provided. /// /// State type /// Initial state /// Fold function /// Folded state [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public S Fold(S state, Func folder) => AsIterable().Fold(state, (s, x) => folder(s, x.Key, x.Value)); /// /// Atomically folds all items in the map (in order) using the folder function provided. /// /// State type /// Initial state /// Fold function /// Folded state [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public S Fold(S state, Func folder) => AsIterable().Map(static x => x.Value).Fold(state, folder); } ================================================ FILE: LanguageExt.Core/Concurrency/VersionHashMap/VersionHashMap.K.V.cs ================================================ using System; using System.Collections; using LanguageExt.ClassInstances; using System.Collections.Generic; using System.Diagnostics.Contracts; using System.Runtime.CompilerServices; namespace LanguageExt { /// /// /// Versioned hash-map. Each value has a vector-clock attached to it which understands the causality of updates /// from multiple actors. Actors are just unique keys that represent individual contributors. They could be client /// connections, nodes in a network, users, or whatever is appropriate to discriminate between commits. /// /// /// Deleted items are not removed from the hash-map, they are merely marked as deleted. This allows conflicts between /// writes and deletes to be resolved. /// /// /// Run `RemoveDeletedItemsOlderThan` to clean up items that have been deleted and are now just hanging around. Use /// a big enough delay that it won't conflict with other commits (this could be seconds, minutes, or longer: /// depending on the expected latency of writes to the hash-map). /// /// /// This is a mutable collection with atomic, thread-safe, updates. /// /// public class VersionHashMap : IEnumerable<(K Key, V Value)>, IEquatable> { readonly VersionHashMap, TString, EqDefault, string, K, V> Items; /// /// Empty version map /// public static VersionHashMap Empty { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new VersionHashMap(VersionHashMap, TString, EqDefault, string, K, V>.Empty); } /// /// Constructor /// /// Trie map [MethodImpl(MethodImplOptions.AggressiveInlining)] VersionHashMap(VersionHashMap, TString, EqDefault, string, K, V> items) => this.Items = items; /// /// 'this' accessor /// /// Key /// Version - this may be in a state of never existing, but won't ever fail [Pure] public Version this[K key] { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => Items.FindVersion(key); } /// /// Is the map empty /// [Pure] public bool IsEmpty { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => Items.IsEmpty; } /// /// Number of items in the map /// [Pure] public int Count { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => Items.Count; } /// /// Alias of Count /// [Pure] public int Length { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => Items.Count; } /// /// Atomically swap a key in the map. Allows for multiple operations on the hash-map in an entirely /// transactional and atomic way. /// /// Swap function, maps the current state of the value associated with the key to a new state /// Any functions passed as arguments may be run multiple times if there are multiple threads competing /// to update this data structure. Therefore the functions must spend as little time performing the injected /// behaviours as possible to avoid repeated attempts [MethodImpl(MethodImplOptions.AggressiveInlining)] public Unit SwapKey(K key, Func, Version> swap) => Items.SwapKey(key, swap); /// /// Atomically updates a new item in the map. If the key already exists, then the vector clocks, within the version /// vector, are compared to ascertain if the proposed version was caused-by, causes, or conflicts with the current /// version: /// /// * If the proposed version was caused-by the current version, then it is ignored. /// * If the proposed version causes the current version, then it is accepted and updated as the latest version /// * If the proposed version is in conflict with the current version, then values from both versions are /// passed to ConflictV.Append(v1, v2) for value resolution, and the pairwise-maximum of the two vector-clocks /// is taken to be the new 'time. /// /// /// Version to update [MethodImpl(MethodImplOptions.AggressiveInlining)] public Unit Update(Version version) => Items.Update(version); /// /// Remove items that are older than the specified time-stamp /// /// Cut off time-stamp [MethodImpl(MethodImplOptions.AggressiveInlining)] public Unit RemoveOlderThan(DateTime cutoff) => Items.RemoveOlderThan(cutoff); /// /// Remove items that are older than the specified time-stamp /// /// Cut off time-stamp [MethodImpl(MethodImplOptions.AggressiveInlining)] public Unit RemoveOlderThan(long timeStamp) => Items.RemoveOlderThan(timeStamp); /// /// Remove deleted items that are older than the specified time-stamp /// /// Cut off time-stamp [MethodImpl(MethodImplOptions.AggressiveInlining)] public Unit RemoveDeletedItemsOlderThan(DateTime cutoff) => Items.RemoveDeletedItemsOlderThan(cutoff); /// /// Remove deleted items that are older than the specified time-stamp /// /// Cut off time-stamp [MethodImpl(MethodImplOptions.AggressiveInlining)] public Unit RemoveDeletedItemsOlderThan(long timeStamp) => Items.RemoveDeletedItemsOlderThan(timeStamp); /// /// Retrieve a value from the map by key /// /// Key to find /// Found value [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Option Find(K value) => Items.Find(value); /// /// Retrieve a value from the map by key /// /// Key to find /// Found value [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Version FindVersion(K value) => Items.FindVersion(value); /// /// Enumerable of keys /// [Pure] public Iterable Keys { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => Items.Keys; } /// /// Enumerable of value /// [Pure] public Iterable Values { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => Items.Values; } /// /// Convert to a HashMap /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public HashMap ToHashMap() => Items.ToHashMap(); /// /// GetEnumerator - IEnumerable interface /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public IEnumerator<(K Key, V Value)> GetEnumerator() => Items.GetEnumerator(); /// /// GetEnumerator - IEnumerable interface /// [MethodImpl(MethodImplOptions.AggressiveInlining)] IEnumerator IEnumerable.GetEnumerator() => Items.GetEnumerator(); [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Seq<(K Key, V Value)> ToSeq() => Items.ToSeq(); /// /// Format the collection as `[(key: value), (key: value), (key: value), ...]` /// The ellipsis is used for collections over 50 items /// To get a formatted string with all the items, use `ToFullString` /// or `ToFullArrayString`. /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public override string ToString() => Items.ToString(); /// /// Format the collection as `(key: value), (key: value), (key: value), ...` /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public string ToFullString(string separator = ", ") => Items.ToFullString(separator); /// /// Format the collection as `[(key: value), (key: value), (key: value), ...]` /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public string ToFullArrayString(string separator = ", ") => Items.ToFullArrayString(separator); [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Iterable<(K Key, V Value)> AsIterable() => Items.AsIterable(); /// /// Returns True if 'other' is a proper subset of this set /// /// True if 'other' is a proper subset of this set [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool IsProperSubsetOf(IEnumerable<(K Key, V Value)> other) => Items.IsProperSubsetOf(other); /// /// Returns True if 'other' is a proper subset of this set /// /// True if 'other' is a proper subset of this set [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool IsProperSubsetOf(IEnumerable other) => Items.IsProperSubsetOf(other); /// /// Returns True if 'other' is a proper superset of this set /// /// True if 'other' is a proper superset of this set [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool IsProperSupersetOf(IEnumerable<(K Key, V Value)> other) => Items.IsProperSupersetOf(other); /// /// Returns True if 'other' is a proper superset of this set /// /// True if 'other' is a proper superset of this set [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool IsProperSupersetOf(IEnumerable other) => Items.IsProperSupersetOf(other); /// /// Returns True if 'other' is a superset of this set /// /// True if 'other' is a superset of this set [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool IsSubsetOf(IEnumerable<(K Key, V Value)> other) => Items.IsSubsetOf(other); /// /// Returns True if 'other' is a superset of this set /// /// True if 'other' is a superset of this set [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool IsSubsetOf(IEnumerable other) => Items.IsSubsetOf(other); /// /// Returns True if 'other' is a superset of this set /// /// True if 'other' is a superset of this set [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool IsSubsetOf(HashMap other) => Items.IsSubsetOf(other); /// /// Returns True if 'other' is a superset of this set /// /// True if 'other' is a superset of this set [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool IsSupersetOf(IEnumerable<(K Key, V Value)> other) => Items.IsSupersetOf(other); /// /// Returns True if 'other' is a superset of this set /// /// True if 'other' is a superset of this set [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool IsSupersetOf(IEnumerable rhs) => Items.IsSupersetOf(rhs); /// /// Returns the elements that are in both this and other /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public Unit Intersect(IEnumerable rhs) => Items.Intersect(rhs); /// /// Returns True if other overlaps this set /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Overlaps(IEnumerable<(K Key, V Value)> other) => Items.Overlaps(other); /// /// Returns True if other overlaps this set /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Overlaps(IEnumerable other) => Items.Overlaps(other); /// /// Returns this - rhs. Only the items in this that are not in /// rhs will be returned. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public Unit Except(IEnumerable rhs) => Items.Except(rhs); /// /// Equality of keys and values with `EqDefault〈V〉` used for values /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public override bool Equals(object? obj) => obj is VersionHashMap hm && Equals(hm); /// /// Equality of keys and values with `EqDefault〈V〉` used for values /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(VersionHashMap? other) => other is not null && Items.Equals(other.Items); [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public override int GetHashCode() => Items.GetHashCode(); /// /// Atomically filter out items that return false when a predicate is applied /// /// Predicate /// New map with items filtered [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public VersionHashMap Where(Func, bool> pred) => new VersionHashMap(Items.Filter(pred)); /// /// Atomically filter out items that return false when a predicate is applied /// /// Predicate /// New map with items filtered [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public VersionHashMap Where(Func, bool> pred) => new VersionHashMap(Items.Filter(pred)); /// /// Atomically filter out items that return false when a predicate is applied /// /// Predicate /// New map with items filtered [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public VersionHashMap Filter(Func, bool> pred) => new VersionHashMap(Items.Filter(pred)); /// /// Atomically filter out items that return false when a predicate is applied /// /// Predicate /// New map with items filtered [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public VersionHashMap Filter(Func, bool> pred) => new VersionHashMap(Items.Filter(pred)); /// /// Atomically filter out items that return false when a predicate is applied /// /// Predicate /// New map with items filtered [MethodImpl(MethodImplOptions.AggressiveInlining)] public Unit FilterInPlace(Func, bool> pred) => Items.FilterInPlace(pred); /// /// Atomically filter out items that return false when a predicate is applied /// /// Predicate /// New map with items filtered [MethodImpl(MethodImplOptions.AggressiveInlining)] public Unit FilterInPlace(Func, bool> pred) => Items.FilterInPlace(pred); /// /// Return true if all items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool ForAll(Func pred) => Items.ForAll(pred); /// /// Return true if all items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool ForAll(Func<(K Key, V Value), bool> pred) => Items.ForAll(pred); /// /// Return true if *all* items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool ForAll(Func, bool> pred) => Items.ForAll(pred); /// /// Return true if all items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool ForAll(Func pred) => Items.ForAll(pred); /// /// Return true if *any* items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Exists(Func pred) => Items.Exists(pred); /// /// Return true if *any* items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Exists(Func<(K Key, V Value), bool> pred) => Items.Exists(pred); /// /// Return true if *any* items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Exists(Func pred) => Items.Exists(pred); /// /// Atomically iterate through all key/value pairs in the map (in order) and execute an /// action on each /// /// Action to execute /// Unit [MethodImpl(MethodImplOptions.AggressiveInlining)] public Unit Iter(Action action) => Items.Iter(action); /// /// Atomically iterate through all values in the map (in order) and execute an /// action on each /// /// Action to execute /// Unit [MethodImpl(MethodImplOptions.AggressiveInlining)] public Unit Iter(Action action) => Items.Iter(action); /// /// Atomically iterate through all key/value pairs (as tuples) in the map (in order) /// and execute an action on each /// /// Action to execute /// Unit [MethodImpl(MethodImplOptions.AggressiveInlining)] public Unit Iter(Action<(K Key, V Value)> action) => Items.Iter(action); /// /// Atomically iterate through all key/value pairs in the map (in order) and execute an /// action on each /// /// Action to execute /// Unit [MethodImpl(MethodImplOptions.AggressiveInlining)] public Unit Iter(Action> action) => Items.Iter(action); /// /// Atomically folds all items in the map (in order) using the folder function provided. /// /// State type /// Initial state /// Fold function /// Folded state [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public S Fold(S state, Func folder) => Items.Fold(state, folder); /// /// Atomically folds all items in the map (in order) using the folder function provided. /// /// State type /// Initial state /// Fold function /// Folded state [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public S Fold(S state, Func folder) => Items.Fold(state, folder); } } ================================================ FILE: LanguageExt.Core/Concurrency/VersionVector/Version.cs ================================================ using System; using LanguageExt.Traits; using static LanguageExt.Prelude; using LanguageExt.ClassInstances; namespace LanguageExt; /// /// Wraps up a version vector, making it easier to work with and not generics hell /// /// Actor type /// Value type public abstract record Version(K Key) { /// /// Perform a write to the vector. This increases the vector-clock by 1 for the `actor` provided. /// /// Value to write public abstract Version Write(Actor actor, long timeStamp, V value); /// /// Perform a write to the vector. This increases the vector-clock by 1 for the `actor` provided. /// /// Value to write public Version Write(Actor actor, V value) => Write(actor, DateTime.UtcNow.Ticks, value); /// /// Perform a delete to the vector. This increases the vector-clock by 1 for the `actor` provided. /// /// Value to write public abstract Version Delete(Actor actor, long timeStamp); /// /// Perform a delete to the vector. This increases the vector-clock by 1 for the `actor` provided. /// /// Value to write public Version Delete(Actor actor) => Delete(actor, DateTime.UtcNow.Ticks); /// /// Get the value if there is one /// public abstract Option Value { get; } } /// /// Internal: Helper functions for mapping between the non-generics Version and the generics heavy VersionVector /// internal static class Version { public static Version ToVersion(this VersionVector vector, K key) where OrdActor : Ord where ConflictV : Conflict => vector.Value.IsSome ? new VersionValueVector(key, vector) : new VersionDeletedVector(key, vector); public static VersionVector? ToVector( this Version version) where OrdActor : Ord where ConflictV : Conflict => version switch { VersionValueVector vv => vv.Vector, VersionDeletedVector vd => vd.Vector, _ => null }; } /// /// Abstract representation of a version vector with a value /// /// Actor type /// Value type internal abstract record VersionSome(K Key, V value) : Version(Key) { /// /// Get the value if there is one /// public override Option Value => value; } /// /// Abstract representation of a version vector without a value (it either never existed or has been deleted) /// /// Actor type /// Value type internal abstract record VersionNone(K Key) : Version(Key) { /// /// Get the value if there is one /// public override Option Value => None; } /// /// Representation of a version vector that never existed /// /// Actor type /// Value type internal record VersionNeverExistedVector(K Key) : VersionNone(Key) where OrdActor : Ord where ConflictV : Conflict { public static Version New(K key) => new VersionNeverExistedVector(key); /// /// Perform a write to the vector. This increases the vector-clock by 1 for the `actor` provided. /// /// Value to write public override Version Write(Actor actor, long timeStamp, V value) => new VersionValueVector( Key, new VersionVector( value, timeStamp, VectorClock.Single(actor, 1L))); /// /// Perform a write to the vector. This increases the vector-clock by 1 for the `actor` provided. /// /// Value to write public override Version Delete(Actor actor, long timeStamp) => this; } /// /// Representation of a version vector that existed but has since had its value deleted /// /// Actor type /// Value type internal record VersionDeletedVector(K Key, VersionVector Vector) : VersionNone(Key) where OrdActor : Ord where ConflictV : Conflict { /// /// Perform a write to the vector. This increases the vector-clock by 1 for the `actor` provided. /// /// Value to write public override Version Write(Actor actor, long timeStamp, V value) => new VersionValueVector(Key, Vector.Put(actor, timeStamp, value)); /// /// Perform a write to the vector. This increases the vector-clock by 1 for the `actor` provided. /// /// Value to write public override Version Delete(Actor actor, long timeStamp) => new VersionDeletedVector(Key, Vector.Put(actor, timeStamp, None)); } /// /// Representation of a version vector with a value /// /// Actor type /// Value type internal record VersionValueVector(K Key, VersionVector Vector) : VersionSome(Key, Vector.Value.Value ?? throw new ArgumentNullException(nameof(Vector.Value.Value))) where OrdActor : Ord where ConflictV : Conflict { /// /// Perform a write to the vector. This increases the vector-clock by 1 for the `actor` provided. /// /// Value to write public override Version Write(Actor actor, long timeStamp, V value) => new VersionValueVector(Key, Vector.Put(actor, timeStamp, value)); /// /// Perform a write to the vector. This increases the vector-clock by 1 for the `actor` provided. /// /// Value to write public override Version Delete(Actor actor, long timeStamp) => new VersionDeletedVector(Key, Vector.Put(actor, timeStamp, None)); } ================================================ FILE: LanguageExt.Core/Concurrency/VersionVector/VersionVector.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; /// /// Version vector. Container of an optional value, a time-stamp, and a vector clock. Used to manage versioning /// of a single value. ConflictA allows for injectable conflict resolution. /// /// Conflict resolution instance type /// Actor ordering instance type /// Numeric clock instance type /// Actor type /// Clock type /// Value to version type public record VersionVector( Option Value, long TimeStamp, VectorClock Vector) where OrdActor : Ord where NumClock : Num where ConflictA : Conflict { /// /// Register a write from any actor /// /// Actor that did the write operation /// Value that the actor wrote /// The vector of the actor /// public VersionVector Put( VersionVector version) => VectorClock.relation(Vector, version.Vector) switch { // `version` happened in the past, we don't care about it Relation.CausedBy => this, // `version` is causally linked to us, it is the future of us, so accept it Relation.Causes => version, // `version` has changed on a different branch of time to us, and so it conflicts. Resolve it // by using the monoid as a merge for the values, and take the pairwise maximum of the two vector clocks Relation.Concurrent => ResolveConflict(version), // Should never get here _ => throw new NotSupportedException() }; VersionVector ResolveConflict(VersionVector version) { var (ts, nv) = ConflictA.Resolve((TimeStamp, Value), (version.TimeStamp, version.Value)); return new VersionVector( Value: nv, TimeStamp: ts, Vector: VectorClock.max(Vector, version.Vector)); } /// /// Perform a write to the vector. This increases the vector-clock by 1 for the `actor` provided. /// /// Value to write public VersionVector Put(Actor actor, long timeStamp, Option value) => new (Value: value, TimeStamp: timeStamp, Vector: Vector.Inc(actor, NumClock.FromInteger(0))); } /// /// Version vector. Container of an optional value, a time-stamp, and a vector clock. Used to manage versioning /// of a single value. ConflictA allows for injectable conflict resolution. /// /// Conflict resolution instance type /// Actor type /// Value to version type public record VersionVector(Option Value, long TimeStamp, VectorClock Vector) where Actor : IComparable where ConflictA : Conflict { /// /// Register a write from any actor /// /// Actor that did the write operation /// Value that the actor wrote /// The vector of the actor /// public VersionVector Put(VersionVector version) => VectorClock.relation(Vector, version.Vector) switch { // `version` happened in the past, we don't care about it Relation.CausedBy => this, // `version` is causally linked to us, it is the future of us, so accept it Relation.Causes => version, // `version` has changed on a different branch of time to us, and so it conflicts. Resolve it // by using the monoid as a merge for the values, and take the pairwise maximum of the two vector clocks Relation.Concurrent => ResolveConflict(version), // Should never get here _ => throw new NotSupportedException() }; VersionVector ResolveConflict(VersionVector version) { var (ts, nv) = ConflictA.Resolve((TimeStamp, Value), (version.TimeStamp, version.Value)); return new VersionVector(Value: nv, TimeStamp: ts, Vector: VectorClock.max(Vector, version.Vector)); } /// /// Perform a write to the vector. This increases the vector-clock by 1 for the `actor` provided. /// /// Value to write public VersionVector Put(Actor actor, long timeStamp, Option value) => new(Value: value, TimeStamp: timeStamp, Vector: Vector.Inc(actor, 0L)); } ================================================ FILE: LanguageExt.Core/DataTypes/BigInt/BigInt.cs ================================================ using System; using System.Globalization; using System.Numerics; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; /// /// BigInteger convenience wrapper /// #pragma warning disable CS8981 // The type name only contains lower-cased ascii characters. Such names may become reserved for the language. public readonly struct bigint : #pragma warning restore CS8981 // The type name only contains lower-cased ascii characters. Such names may become reserved for the language. IComparable, IComparable, IEquatable, Monoid { public readonly BigInteger Value; /// /// Initializes a new instance of bigint structure using provided BigInteger value. /// /// A big integer value to initialise this structure with public bigint(BigInteger value) => Value = value; /// /// Initializes a new instance of the bigint structure using /// the values in a byte array. /// /// /// value: /// An array of byte values in little-endian order. /// /// public bigint(byte[] value) { Value = new BigInteger(value); } /// /// Initializes a new instance of the bigint structure using /// a System.Decimal value. /// /// /// value: /// A decimal number. /// public bigint(decimal value) { Value = new BigInteger(value); } /// /// Initializes a new instance of the bigint structure using /// a double-precision floating-point value. /// /// /// value: /// A double-precision floating-point value. /// /// public bigint(double value) { Value = new BigInteger(value); } /// /// Initializes a new instance of the bigint structure using /// a 32-bit signed integer value. /// /// /// value: /// A 32-bit signed integer. /// public bigint(int value) { Value = new BigInteger(value); } /// /// Initializes a new instance of the bigint structure using /// a 64-bit signed integer value. /// /// /// value: /// A 64-bit signed integer. /// public bigint(long value) { Value = new BigInteger(value); } /// /// Initializes a new instance of the bigint structure using /// a single-precision floating-point value. /// /// /// value: /// A single-precision floating-point value. /// /// public bigint(float value) { Value = new BigInteger(value); } /// /// Initializes a new instance of the bigint structure using /// an unsigned 32-bit integer value. /// /// /// value: /// An unsigned 32-bit integer value. /// public bigint(uint value) { Value = new BigInteger(value); } /// /// Initializes a new instance of the bigint structure with an /// unsigned 64-bit integer value. /// /// /// value: /// An unsigned 64-bit integer. /// public bigint(ulong value) { Value = new BigInteger(value); } /// /// Gets a value that represents the number one (1). /// /// /// An object whose value is one (1). /// public static readonly bigint One = new (1); /// /// Gets a value that represents the number negative one (-1). /// /// /// An integer whose value is negative one (-1). /// public static readonly bigint MinusOne = new (-1); /// /// Gets a value that represents the number 0 (zero). /// /// /// An integer whose value is 0 (zero). /// public static bigint Zero = new (0); /// /// Indicates whether the value of the current bigint object /// is an even number. /// /// /// true if the value of the bigint object is an even number; /// otherwise, false. /// public bool IsEven => Value.IsEven; /// /// Indicates whether the value of the current bigint object /// is bigint.Zero. /// /// /// true if the value of the bigint object is bigint.Zero; /// otherwise, false. /// public bool IsZero => Value.IsZero; /// /// Indicates whether the value of the current bigint object /// is a power of two. /// /// /// true if the value of the bigint object is a power of two; /// otherwise, false. /// public bool IsPowerOfTwo => Value.IsPowerOfTwo; /// /// Indicates whether the value of the current bigint object /// is bigint.One. /// /// /// true if the value of the bigint object is bigint.One; /// otherwise, false. /// public bool IsOne => Value.IsOne; /// /// Gets a number that indicates the sign (negative, positive, or zero) of the current /// bigint object. /// /// /// A number that indicates the sign of the bigint object, as /// shown in the following table.NumberDescription-1The value of this object is negative.0The /// value of this object is 0 (zero).1The value of this object is positive. /// public int Sign => Value.Sign; /// /// Gets the absolute value of a bigint object. /// /// /// The absolute value of value. /// public static bigint Abs(bigint value) => new (BigInteger.Abs(value.Value)); /// /// Adds two bigint values and returns the result. /// /// /// The sum of left and right. /// public static bigint Add(bigint left, bigint right) => BigInteger.Add(left.Value, right.Value); /// /// Compares two bigint values and returns an integer that indicates /// whether the first value is less than, equal to, or greater than the second value. /// /// /// A signed integer that indicates the relative values of left and right, as shown /// in the following table.ValueConditionLess than zeroleft is less than right.Zeroleft /// equals right.Greater than zeroleft is greater than right. /// public static int Compare(bigint left, bigint right) => left.Value.CompareTo(right.Value); /// /// Divides one bigint value by another and returns the result. /// /// /// dividend: /// The value to be divided. /// /// divisor: /// The value to divide by. /// /// /// The quotient of the division. /// /// public static bigint Divide(bigint dividend, bigint divisor) => BigInteger.Divide(dividend.Value, divisor.Value); /// /// Divides one bigint value by another, returns the quotient and remainder. /// /// /// dividend: /// The value to be divided. /// /// divisor: /// The value to divide by. /// /// /// The quotient and remainder of the division as a tuple /// /// public static (bigint Quotient, bigint Remainder) DivRem(bigint dividend, bigint divisor) { var res = BigInteger.DivRem(dividend.Value, divisor.Value, out BigInteger rem); return (new (res), new (rem)); } /// /// Finds the greatest common divisor of two bigint values. /// /// /// left: /// The first value. /// /// right: /// The second value. /// /// /// The greatest common divisor of left and right. /// public static bigint GreatestCommonDivisor(bigint left, bigint right) => new (BigInteger.GreatestCommonDivisor(left.Value, right.Value)); /// /// Returns the natural (base e) logarithm of a specified number. /// /// /// value: /// The number whose logarithm is to be found. /// /// /// The natural (base e) logarithm of value, as shown in the table in the Remarks /// section. /// /// public static double Log(bigint value) => BigInteger.Log(value.Value); /// /// Returns the logarithm of a specified number in a specified base. /// /// /// value: /// A number whose logarithm is to be found. /// /// baseValue: /// The base of the logarithm. /// /// /// The base baseValue logarithm of value, as shown in the table in the Remarks section. /// /// public static double Log(bigint value, double baseValue) => BigInteger.Log(value.Value, baseValue); /// /// Returns the base 10 logarithm of a specified number. /// /// /// value: /// A number whose logarithm is to be found. /// /// /// The base 10 logarithm of value, as shown in the table in the Remarks section. /// /// public static double Log10(bigint value) => BigInteger.Log10(value.Value); /// /// Returns the larger of two bigint values. /// /// /// left: /// The first value to compare. /// /// right: /// The second value to compare. /// /// /// The left or right parameter, whichever is larger. /// public static bigint Max(bigint left, bigint right) => new (BigInteger.Max(left.Value, right.Value)); /// /// Returns the smaller of two bigint values. /// /// /// left: /// The first value to compare. /// /// right: /// The second value to compare. /// /// /// The left or right parameter, whichever is smaller. /// public static bigint Min(bigint left, bigint right) => new (BigInteger.Min(left.Value, right.Value)); /// /// Performs modulus division on a number raised to the power of another number. /// /// /// value: /// The number to raise to the exponent power. /// /// exponent: /// The exponent to raise value by. /// /// modulus: /// The number by which to divide value raised to the exponent power. /// /// /// The remainder after dividing value exponent by modulus. /// /// public static bigint ModPow(bigint value, bigint exponent, bigint modulus) => new (BigInteger.ModPow(value.Value, exponent.Value, modulus.Value)); /// /// Returns the product of two bigint values. /// /// /// left: /// The first number to multiply. /// /// right: /// The second number to multiply. /// /// /// The product of the left and right parameters. /// public static bigint Multiply(bigint left, bigint right) => new (BigInteger.Multiply(left.Value, right.Value)); /// /// Negates a specified bigint value. /// /// /// value: /// The value to negate. /// /// /// The result of the value parameter multiplied by negative one (-1). /// public static bigint Negate(bigint value) => new (BigInteger.Negate(value.Value)); /// /// Converts the string representation of a number in a specified style to its bigint /// equivalent. /// /// /// value: /// A string that contains a number to convert. /// /// style: /// A bitwise combination of the enumeration values that specify the permitted format /// of value. /// /// /// A value that is equivalent to the number specified in the value parameter. /// /// public static bigint Parse(string value, NumberStyles style) => new (BigInteger.Parse(value, style)); /// /// Converts the string representation of a number in a specified culture-specific /// format to its bigint equivalent. /// /// /// value: /// A string that contains a number to convert. /// /// provider: /// An object that provides culture-specific formatting information about value. /// /// /// A value that is equivalent to the number specified in the value parameter. /// /// public static bigint Parse(string value, IFormatProvider provider) => new (BigInteger.Parse(value, provider)); /// /// Converts the string representation of a number to its bigint /// equivalent. /// /// /// value: /// A string that contains the number to convert. /// /// /// A value that is equivalent to the number specified in the value parameter. /// /// public static bigint Parse(string value) => new (BigInteger.Parse(value)); /// /// Converts the string representation of a number in a specified style and culture-specific /// format to its bigint equivalent. /// /// /// value: /// A string that contains a number to convert. /// /// style: /// A bitwise combination of the enumeration values that specify the permitted format /// of value. /// /// provider: /// An object that provides culture-specific formatting information about value. /// /// /// A value that is equivalent to the number specified in the value parameter. /// /// public static bigint Parse(string value, NumberStyles style, IFormatProvider provider) => new (BigInteger.Parse(value, style, provider)); /// /// Raises a bigint value to the power of a specified value. /// /// /// value: /// The number to raise to the exponent power. /// /// exponent: /// The exponent to raise value by. /// /// /// The result of raising value to the exponent power. /// /// public static bigint Pow(bigint value, int exponent) => new (BigInteger.Pow(value.Value, exponent)); /// /// Performs integer division on two bigint values and returns /// the remainder. /// /// /// dividend: /// The value to be divided. /// /// divisor: /// The value to divide by. /// /// /// The remainder after dividing dividend by divisor. /// /// public static bigint Remainder(bigint dividend, bigint divisor) => new (BigInteger.Remainder(dividend.Value, divisor.Value)); /// /// Subtracts one bigint value from another and returns the result. /// /// /// left: /// The value to subtract from (the minuend). /// /// right: /// The value to subtract (the subtrahend). /// /// /// The result of subtracting right from left. /// public static bigint Subtract(bigint left, bigint right) => new (BigInteger.Subtract(left.Value, right.Value)); /// /// Tries to convert the string representation of a number in a specified style and /// culture-specific format to its bigint equivalent, and returns /// a value that indicates whether the conversion succeeded. /// /// /// value: /// The string representation of a number. The string is interpreted using the style /// specified by style. /// /// style: /// A bitwise combination of enumeration values that indicates the style elements /// that can be present in value. A typical value to specify is System.Globalization.NumberStyles.Integer. /// /// provider: /// An object that supplies culture-specific formatting information about value. /// /// /// Optional value /// /// public static Option TryParse(string value, NumberStyles style, IFormatProvider provider) => BigInteger.TryParse(value, style, provider, out BigInteger res) ? Some(new bigint(res)) : None; /// /// Tries to convert the string representation of a number to its bigint /// equivalent, and returns a value that indicates whether the conversion succeeded. /// /// /// value: /// The string representation of a number. /// /// /// Optional value /// /// public static Option TryParse(string value) => BigInteger.TryParse(value, out BigInteger res) ? Some(new bigint(res)) : None; /// /// Compares this instance to a signed 64-bit integer and returns an integer that /// indicates whether the value of this instance is less than, equal to, or greater /// than the value of the signed 64-bit integer. /// /// /// other: /// The signed 64-bit integer to compare. /// /// /// A signed integer value that indicates the relationship of this instance to other, /// as shown in the following table.Return valueDescriptionLess than zeroThe current /// instance is less than other.ZeroThe current instance equals other.Greater than /// zeroThe current instance is greater than other. /// public int CompareTo(long other) => Value.CompareTo(other); /// /// Compares this instance to an unsigned 64-bit integer and returns an integer that /// indicates whether the value of this instance is less than, equal to, or greater /// than the value of the unsigned 64-bit integer. /// /// /// other: /// The unsigned 64-bit integer to compare. /// /// /// A signed integer that indicates the relative value of this instance and other, /// as shown in the following table.Return valueDescriptionLess than zeroThe current /// instance is less than other.ZeroThe current instance equals other.Greater than /// zeroThe current instance is greater than other. /// public int CompareTo(ulong other) => Value.CompareTo(other); /// /// Compares this instance to a second bigint and returns an /// integer that indicates whether the value of this instance is less than, equal /// to, or greater than the value of the specified object. /// /// /// other: /// The object to compare. /// /// /// A signed integer value that indicates the relationship of this instance to other, /// as shown in the following table.Return valueDescriptionLess than zeroThe current /// instance is less than other.ZeroThe current instance equals other.Greater than /// zeroThe current instance is greater than other. /// public int CompareTo(BigInteger other) => Value.CompareTo(other); /// /// Compares this instance to a second bigint and returns an /// integer that indicates whether the value of this instance is less than, equal /// to, or greater than the value of the specified object. /// /// /// other: /// The object to compare. /// /// /// A signed integer value that indicates the relationship of this instance to other, /// as shown in the following table.Return valueDescriptionLess than zeroThe current /// instance is less than other.ZeroThe current instance equals other.Greater than /// zeroThe current instance is greater than other. /// public int CompareTo(bigint other) => Value.CompareTo(other.Value); /// /// Returns a value that indicates whether the current instance and a signed 64-bit /// integer have the same value. /// /// /// other: /// The signed 64-bit integer value to compare. /// /// /// true if the signed 64-bit integer and the current instance have the same value; /// otherwise, false. /// public bool Equals(long other) => Value.Equals(other); /// /// Returns a value that indicates whether the current instance and a specified bigint /// object have the same value. /// /// /// other: /// The object to compare. /// /// /// true if this bigint object and other have the same value; /// otherwise, false. /// public bool Equals(BigInteger other) => Value.Equals(other); /// /// Returns a value that indicates whether the current instance and a specified bigint /// object have the same value. /// /// /// other: /// The object to compare. /// /// /// true if this bigint object and other have the same value; /// otherwise, false. /// public bool Equals(bigint other) => Value.Equals(other.Value); /// /// Returns a value that indicates whether the current instance and a specified object /// have the same value. /// /// /// obj: /// The object to compare. /// /// /// true if the obj parameter is a bigint object or a type capable /// of implicit conversion to a bigint value, and its value is /// equal to the value of the current bigint object; otherwise, /// false. /// public override bool Equals(object? obj) => obj switch { bigint rhs => Value.Equals(rhs.Value), BigInteger rhs => Value.Equals(rhs), _ => false }; /// /// Returns a value that indicates whether the current instance and an unsigned 64-bit /// integer have the same value. /// /// /// other: /// The unsigned 64-bit integer to compare. /// /// /// true if the current instance and the unsigned 64-bit integer have the same value; /// otherwise, false. /// public bool Equals(ulong other) => Value.Equals(other); /// /// Returns the hash code for the current bigint object. /// /// /// /// A 32-bit signed integer hash code. /// public override int GetHashCode() => Value.GetHashCode(); public int CompareTo(object? obj) => obj switch { bigint rhs => Value.CompareTo(rhs.Value), BigInteger rhs => Value.CompareTo(rhs), _ => 1 }; /// /// Converts a bigint value to a byte array. /// /// /// The value of the current bigint object converted to an array /// of bytes. /// public byte[] ToByteArray() => Value.ToByteArray(); /// /// Converts the numeric value of the current bigint object to /// its equivalent string representation. /// /// /// The string representation of the current bigint value. /// public override string ToString() => Value.ToString(); /// /// Converts the numeric value of the current bigint object to /// its equivalent string representation by using the specified format. /// /// /// format: /// A standard or custom numeric format string. /// /// /// The string representation of the current bigint value in /// the format specified by the format parameter. /// /// public string ToString(string format) => Value.ToString(format); /// /// Converts the numeric value of the current bigint object to /// its equivalent string representation by using the specified culture-specific /// formatting information. /// /// /// provider: /// An object that supplies culture-specific formatting information. /// /// /// The string representation of the current bigint value in /// the format specified by the provider parameter. /// public string ToString(IFormatProvider provider) => Value.ToString(provider); /// /// Converts the numeric value of the current bigint object to /// its equivalent string representation by using the specified format and culture-specific /// format information. /// /// /// format: /// A standard or custom numeric format string. /// /// provider: /// An object that supplies culture-specific formatting information. /// /// /// The string representation of the current bigint value as /// specified by the format and provider parameters. /// /// public string ToString(string format, IFormatProvider provider) => Value.ToString(format, provider); /// /// Returns the value of the bigint operand. (The sign of the /// operand is unchanged.) /// /// /// value: /// An integer value. /// /// /// The value of the value operand. /// public static bigint operator +(bigint value) => value; /// /// Adds the values of two specified bigint objects. /// /// /// left: /// The first value to add. /// /// right: /// The second value to add. /// /// /// The sum of left and right. /// public static bigint operator +(bigint left, bigint right) => new (left.Value + right.Value); /// /// Negates a specified BigInteger value. /// /// /// value: /// The value to negate. /// /// /// The result of the value parameter multiplied by negative one (-1). /// public static bigint operator -(bigint value) => new (-value.Value); /// /// Subtracts a bigint value from another bigint /// value. /// /// /// left: /// The value to subtract from (the minuend). /// /// right: /// The value to subtract (the subtrahend). /// /// /// The result of subtracting right from left. /// public static bigint operator -(bigint left, bigint right) => new (left.Value - right.Value); /// /// Returns the bitwise one's complement of a bigint value. /// /// /// value: /// An integer value. /// /// /// The bitwise one's complement of value. /// public static bigint operator ~(bigint value) => new (~value.Value); /// /// Increments a bigint value by 1. /// /// /// value: /// The value to increment. /// /// /// The value of the value parameter incremented by 1. /// public static bigint operator ++(bigint value) => value + One; /// /// Decrements a bigint value by 1. /// /// /// value: /// The value to decrement. /// /// /// The value of the value parameter decremented by 1. /// public static bigint operator --(bigint value) => value - One; /// /// Multiplies two specified bigint values. /// /// /// left: /// The first value to multiply. /// /// right: /// The second value to multiply. /// /// /// The product of left and right. /// public static bigint operator *(bigint left, bigint right) => new (left.Value * right.Value); /// /// Divides a specified bigint value by another specified bigint /// value by using integer division. /// /// /// dividend: /// The value to be divided. /// /// divisor: /// The value to divide by. /// /// /// The integral result of the division. /// /// public static bigint operator /(bigint dividend, bigint divisor) => new (dividend.Value / divisor.Value); /// /// Returns the remainder that results from division with two specified bigint /// values. /// /// /// dividend: /// The value to be divided. /// /// divisor: /// The value to divide by. /// /// /// The remainder that results from the division. /// /// public static bigint operator %(bigint dividend, bigint divisor) => new (dividend.Value % divisor.Value); /// /// Performs a bitwise And operation on two bigint values. /// /// /// left: /// The first value. /// /// right: /// The second value. /// /// /// The result of the bitwise And operation. /// public static bigint operator &(bigint left, bigint right) => new (left.Value & right.Value); /// /// Performs a bitwise Or operation on two bigint values. /// /// /// left: /// The first value. /// /// right: /// The second value. /// /// /// The result of the bitwise Or operation. /// public static bigint operator |(bigint left, bigint right) => new (left.Value | right.Value); /// /// Performs a bitwise exclusive Or (XOr) operation on two bigint /// values. /// /// /// left: /// The first value. /// /// right: /// The second value. /// /// /// The result of the bitwise Or operation. /// public static bigint operator ^(bigint left, bigint right) => new (left.Value ^ right.Value); /// /// Shifts a bigint value a specified number of bits to the left. /// /// /// value: /// The value whose bits are to be shifted. /// /// shift: /// The number of bits to shift value to the left. /// /// /// A value that has been shifted to the left by the specified number of bits. /// public static bigint operator <<(bigint value, int shift) => new (value.Value << shift); /// /// Shifts a bigint value a specified number of bits to the right. /// /// /// value: /// The value whose bits are to be shifted. /// /// shift: /// The number of bits to shift value to the right. /// /// /// A value that has been shifted to the right by the specified number of bits. /// public static bigint operator >>(bigint value, int shift) => new (value.Value >> shift); /// /// Returns a value that indicates whether a bigint value and /// an unsigned long integer value are equal. /// /// /// left: /// The first value to compare. /// /// right: /// The second value to compare. /// /// /// true if the left and right parameters have the same value; otherwise, false. /// public static bool operator ==(bigint left, ulong right) => left.Value == right; /// /// Returns a value that indicates whether the values of two bigint /// objects are equal. /// /// /// left: /// The first value to compare. /// /// right: /// The second value to compare. /// /// /// true if the left and right parameters have the same value; otherwise, false. /// public static bool operator ==(BigInteger left, bigint right) => left == right.Value; /// /// Returns a value that indicates whether the values of two bigint /// objects are equal. /// /// /// left: /// The first value to compare. /// /// right: /// The second value to compare. /// /// /// true if the left and right parameters have the same value; otherwise, false. /// public static bool operator ==(bigint left, BigInteger right) => left.Value == right; /// /// Returns a value that indicates whether a signed long integer value and a bigint /// value are equal. /// /// /// left: /// The first value to compare. /// /// right: /// The second value to compare. /// /// /// true if the left and right parameters have the same value; otherwise, false. /// public static bool operator ==(long left, bigint right) => left == right.Value; /// /// Returns a value that indicates whether a bigint value and /// a signed long integer value are equal. /// /// /// left: /// The first value to compare. /// /// right: /// The second value to compare. /// /// /// true if the left and right parameters have the same value; otherwise, false. /// public static bool operator ==(bigint left, long right) => left.Value == right; /// /// Returns a value that indicates whether the values of two bigint /// objects are equal. /// /// /// left: /// The first value to compare. /// /// right: /// The second value to compare. /// /// /// true if the left and right parameters have the same value; otherwise, false. /// public static bool operator ==(bigint left, bigint right) => left.Value == right.Value; /// /// Returns a value that indicates whether an unsigned long integer value and a bigint /// value are equal. /// /// /// left: /// The first value to compare. /// /// right: /// The second value to compare. /// /// /// true if the left and right parameters have the same value; otherwise, false. /// public static bool operator ==(ulong left, bigint right) => left == right.Value; /// /// Returns a value that indicates whether a 64-bit unsigned integer and a bigint /// value are not equal. /// /// /// left: /// The first value to compare. /// /// right: /// The second value to compare. /// /// /// true if left and right are not equal; otherwise, false. /// public static bool operator !=(ulong left, bigint right) => left != right.Value; /// /// Returns a value that indicates whether a bigint value and /// a 64-bit unsigned integer are not equal. /// /// /// left: /// The first value to compare. /// /// right: /// The second value to compare. /// /// /// true if left and right are not equal; otherwise, false. /// public static bool operator !=(bigint left, ulong right) => left.Value != right; /// /// Returns a value that indicates whether a 64-bit signed integer and a bigint /// value are not equal. /// /// /// left: /// The first value to compare. /// /// right: /// The second value to compare. /// /// /// true if left and right are not equal; otherwise, false. /// public static bool operator !=(long left, bigint right) => left != right.Value; /// /// Returns a value that indicates whether a bigint value and /// a 64-bit signed integer are not equal. /// /// /// left: /// The first value to compare. /// /// right: /// The second value to compare. /// /// /// true if left and right are not equal; otherwise, false. /// public static bool operator !=(bigint left, long right) => left.Value != right; /// /// Returns a value that indicates whether two bigint objects /// have different values. /// /// /// left: /// The first value to compare. /// /// right: /// The second value to compare. /// /// /// true if left and right are not equal; otherwise, false. /// public static bool operator !=(bigint left, bigint right) => left.Value != right.Value; /// /// Returns a value that indicates whether two bigint objects /// have different values. /// /// /// left: /// The first value to compare. /// /// right: /// The second value to compare. /// /// /// true if left and right are not equal; otherwise, false. /// public static bool operator !=(BigInteger left, bigint right) => left != right.Value; /// /// Returns a value that indicates whether two bigint objects /// have different values. /// /// /// left: /// The first value to compare. /// /// right: /// The second value to compare. /// /// /// true if left and right are not equal; otherwise, false. /// public static bool operator !=(bigint left, BigInteger right) => left.Value != right; /// /// Returns a value that indicates whether a 64-bit signed integer is less than a /// bigint value. /// /// /// left: /// The first value to compare. /// /// right: /// The second value to compare. /// /// /// true if left is less than right; otherwise, false. /// public static bool operator <(long left, bigint right) => left < right.Value; /// /// Returns a value that indicates whether a bigint value is /// less than a 64-bit unsigned integer. /// /// /// left: /// The first value to compare. /// /// right: /// The second value to compare. /// /// /// true if left is less than right; otherwise, false. /// public static bool operator <(bigint left, ulong right) => left.Value < right; /// /// Returns a value that indicates whether a 64-bit unsigned integer is less than /// a bigint value. /// /// /// left: /// The first value to compare. /// /// right: /// The second value to compare. /// /// /// true if left is less than right; otherwise, false. /// public static bool operator <(ulong left, bigint right) => left < right.Value; /// /// Returns a value that indicates whether a bigint value is /// less than another bigint value. /// /// /// left: /// The first value to compare. /// /// right: /// The second value to compare. /// /// /// true if left is less than right; otherwise, false. /// public static bool operator <(bigint left, bigint right) => left.Value < right.Value; /// /// Returns a value that indicates whether a bigint value is /// less than another bigint value. /// /// /// left: /// The first value to compare. /// /// right: /// The second value to compare. /// /// /// true if left is less than right; otherwise, false. /// public static bool operator <(BigInteger left, bigint right) => left < right.Value; /// /// Returns a value that indicates whether a bigint value is /// less than another bigint value. /// /// /// left: /// The first value to compare. /// /// right: /// The second value to compare. /// /// /// true if left is less than right; otherwise, false. /// public static bool operator <(bigint left, BigInteger right) => left.Value < right; /// /// Returns a value that indicates whether a bigint value is /// less than a 64-bit signed integer. /// /// /// left: /// The first value to compare. /// /// right: /// The second value to compare. /// /// /// true if left is less than right; otherwise, false. /// public static bool operator <(bigint left, long right) => left.Value < right; /// /// Returns a value that indicates whether a bigint value is /// greater than a 64-bit unsigned integer. /// /// /// left: /// The first value to compare. /// /// right: /// The second value to compare. /// /// /// true if left is greater than right; otherwise, false. /// public static bool operator >(ulong left, bigint right) => left < right.Value; /// /// Returns a value that indicates whether a bigint value is /// greater than another bigint value. /// /// /// left: /// The first value to compare. /// /// right: /// The second value to compare. /// /// /// true if left is greater than right; otherwise, false. /// public static bool operator >(bigint left, bigint right) => left.Value > right.Value; /// /// Returns a value that indicates whether a bigint value is /// greater than another bigint value. /// /// /// left: /// The first value to compare. /// /// right: /// The second value to compare. /// /// /// true if left is greater than right; otherwise, false. /// public static bool operator >(BigInteger left, bigint right) => left > right.Value; /// /// Returns a value that indicates whether a bigint value is /// greater than another bigint value. /// /// /// left: /// The first value to compare. /// /// right: /// The second value to compare. /// /// /// true if left is greater than right; otherwise, false. /// public static bool operator >(bigint left, BigInteger right) => left.Value > right; /// /// Returns a value that indicates whether a 64-bit signed integer is greater than /// a bigint value. /// /// /// left: /// The first value to compare. /// /// right: /// The second value to compare. /// /// /// true if left is greater than right; otherwise, false. /// public static bool operator >(long left, bigint right) => left > right.Value; /// /// Returns a value that indicates whether a bigint is greater /// than a 64-bit signed integer value. /// /// /// left: /// The first value to compare. /// /// right: /// The second value to compare. /// /// /// true if left is greater than right; otherwise, false. /// public static bool operator >(bigint left, long right) => left.Value > right; /// /// Returns a value that indicates whether a bigint value is /// greater than a 64-bit unsigned integer. /// /// /// left: /// The first value to compare. /// /// right: /// The second value to compare. /// /// /// true if left is greater than right; otherwise, false. /// public static bool operator >(bigint left, ulong right) => left.Value > right; /// /// Returns a value that indicates whether a 64-bit signed integer is less than or /// equal to a bigint value. /// /// /// left: /// The first value to compare. /// /// right: /// The second value to compare. /// /// /// true if left is less than or equal to right; otherwise, false. /// public static bool operator <=(long left, bigint right) => left <= right.Value; /// /// Returns a value that indicates whether a bigint value is /// less than or equal to a 64-bit signed integer. /// /// /// left: /// The first value to compare. /// /// right: /// The second value to compare. /// /// /// true if left is less than or equal to right; otherwise, false. /// public static bool operator <=(bigint left, long right) => left.Value <= right; /// /// Returns a value that indicates whether a bigint value is /// less than or equal to another bigint value. /// /// /// left: /// The first value to compare. /// /// right: /// The second value to compare. /// /// /// true if left is less than or equal to right; otherwise, false. /// public static bool operator <=(bigint left, bigint right) => left.Value <= right.Value; /// /// Returns a value that indicates whether a bigint value is /// less than or equal to another bigint value. /// /// /// left: /// The first value to compare. /// /// right: /// The second value to compare. /// /// /// true if left is less than or equal to right; otherwise, false. /// public static bool operator <=(BigInteger left, bigint right) => left <= right.Value; /// /// Returns a value that indicates whether a bigint value is /// less than or equal to another bigint value. /// /// /// left: /// The first value to compare. /// /// right: /// The second value to compare. /// /// /// true if left is less than or equal to right; otherwise, false. /// public static bool operator <=(bigint left, BigInteger right) => left.Value <= right; /// /// Returns a value that indicates whether a bigint value is /// less than or equal to a 64-bit unsigned integer. /// /// /// left: /// The first value to compare. /// /// right: /// The second value to compare. /// /// /// true if left is less than or equal to right; otherwise, false. /// public static bool operator <=(bigint left, ulong right) => left.Value <= right; /// /// Returns a value that indicates whether a 64-bit unsigned integer is less than /// or equal to a bigint value. /// /// /// left: /// The first value to compare. /// /// right: /// The second value to compare. /// /// /// true if left is less than or equal to right; otherwise, false. /// public static bool operator <=(ulong left, bigint right) => left <= right.Value; /// /// Returns a value that indicates whether a 64-bit signed integer is greater than /// or equal to a bigint value. /// /// /// left: /// The first value to compare. /// /// right: /// The second value to compare. /// /// /// true if left is greater than right; otherwise, false. /// public static bool operator >=(long left, bigint right) => left >= right.Value; /// /// Returns a value that indicates whether a bigint value is /// greater than or equal to a 64-bit signed integer value. /// /// /// left: /// The first value to compare. /// /// right: /// The second value to compare. /// /// /// true if left is greater than right; otherwise, false. /// public static bool operator >=(bigint left, long right) => left.Value >= right; /// /// Returns a value that indicates whether a bigint value is /// greater than or equal to another bigint value. /// /// /// left: /// The first value to compare. /// /// right: /// The second value to compare. /// /// /// true if left is greater than right; otherwise, false. /// public static bool operator >=(bigint left, bigint right) => left.Value >= right.Value; /// /// Returns a value that indicates whether a bigint value is /// greater than or equal to another bigint value. /// /// /// left: /// The first value to compare. /// /// right: /// The second value to compare. /// /// /// true if left is greater than right; otherwise, false. /// public static bool operator >=(BigInteger left, bigint right) => left >= right.Value; /// /// Returns a value that indicates whether a bigint value is /// greater than or equal to another bigint value. /// /// /// left: /// The first value to compare. /// /// right: /// The second value to compare. /// /// /// true if left is greater than right; otherwise, false. /// public static bool operator >=(bigint left, BigInteger right) => left.Value >= right; /// /// Returns a value that indicates whether a 64-bit unsigned integer is greater than /// or equal to a bigint value. /// /// /// left: /// The first value to compare. /// /// right: /// The second value to compare. /// /// /// true if left is greater than right; otherwise, false. /// public static bool operator >=(ulong left, bigint right) => left >= right.Value; /// /// Returns a value that indicates whether a bigint value is /// greater than or equal to a 64-bit unsigned integer value. /// /// /// left: /// The first value to compare. /// /// right: /// The second value to compare. /// /// /// true if left is greater than right; otherwise, false. /// public static bool operator >=(bigint left, ulong right) => left.Value >= right; /// /// Defines an implicit conversion of a BigInteger to a bigint /// value. /// /// /// value: /// The value to convert to a bigint. /// /// /// An object that contains the value of the value parameter. /// public static implicit operator bigint(BigInteger value) => new (value); /// /// Defines an implicit conversion of an unsigned byte to a bigint /// value. /// /// /// value: /// The value to convert to a bigint. /// /// /// An object that contains the value of the value parameter. /// public static implicit operator bigint(byte value) => new (value); /// /// Defines an implicit conversion of a 16-bit unsigned integer to a bigint /// value. /// /// /// value: /// The value to convert to a bigint. /// /// /// An object that contains the value of the value parameter. /// public static implicit operator bigint(ushort value) => new (value); /// /// Defines an implicit conversion of an 8-bit signed integer to a bigint /// value. /// /// /// value: /// The value to convert to a bigint. /// /// /// An object that contains the value of the value parameter. /// public static implicit operator bigint(sbyte value) => new (value); /// /// Defines an implicit conversion of a 32-bit unsigned integer to a bigint /// value. /// /// /// value: /// The value to convert to a bigint. /// /// /// An object that contains the value of the value parameter. /// public static implicit operator bigint(uint value) => new (value); /// /// Defines an implicit conversion of a signed 64-bit integer to a bigint /// value. /// /// /// value: /// The value to convert to a bigint. /// /// /// An object that contains the value of the value parameter. /// public static implicit operator bigint(long value) => new (value); /// /// Defines an implicit conversion of a signed 32-bit integer to a bigint /// value. /// /// /// value: /// The value to convert to a bigint. /// /// /// An object that contains the value of the value parameter. /// public static implicit operator bigint(int value) => new (value); /// /// Defines an implicit conversion of a signed 16-bit integer to a bigint /// value. /// /// /// value: /// The value to convert to a bigint. /// /// /// An object that contains the value of the value parameter. /// public static implicit operator bigint(short value) => new (value); /// /// Defines an implicit conversion of a 64-bit unsigned integer to a bigint /// value. /// /// /// value: /// The value to convert to a bigint. /// /// /// An object that contains the value of the value parameter. /// public static implicit operator bigint(ulong value) => new (value); /// /// Defines an explicit conversion of a System.Decimal object to a bigint /// value. /// /// /// value: /// The value to convert to a bigint. /// /// /// An object that contains the value of the value parameter. /// public static explicit operator bigint(decimal value) => new (value); /// /// Defines an explicit conversion of a System.Double value to a bigint /// value. /// /// /// value: /// The value to convert to a bigint. /// /// /// An object that contains the value of the value parameter. /// /// public static explicit operator bigint(double value) => new (value); /// /// Defines an explicit conversion of a bigint object to an unsigned /// byte value. /// /// /// value: /// The value to convert to a System.Byte. /// /// /// An object that contains the value of the value parameter. /// /// public static explicit operator byte(bigint value) => (byte)value.Value; /// /// Defines an explicit conversion of a bigint object to an bigint value. /// /// /// value: /// The value to convert to a System.Byte. /// /// /// An object that contains the value of the value parameter. /// /// public static implicit operator BigInteger(bigint value) => value.Value; /// /// Defines an explicit conversion of a bigint object to a System.Decimal /// value. /// /// /// value: /// The value to convert to a System.Decimal. /// /// /// An object that contains the value of the value parameter. /// /// public static explicit operator decimal(bigint value) => (decimal)value.Value; /// /// Defines an explicit conversion of a bigint object to a System.Double /// value. /// /// /// value: /// The value to convert to a System.Double. /// /// /// An object that contains the value of the value parameter. /// public static explicit operator double(bigint value) => (double)value.Value; /// /// Defines an explicit conversion of a bigint object to a 16-bit /// signed integer value. /// /// /// value: /// The value to convert to a 16-bit signed integer. /// /// /// An object that contains the value of the value parameter. /// /// public static explicit operator short(bigint value) => (short)value.Value; /// /// Defines an explicit conversion of a bigint object to a 64-bit /// signed integer value. /// /// /// value: /// The value to convert to a 64-bit signed integer. /// /// /// An object that contains the value of the value parameter. /// /// public static explicit operator long(bigint value) => (long)value.Value; /// /// Defines an explicit conversion of a bigint object to a signed /// 8-bit value. /// /// /// value: /// The value to convert to a signed 8-bit value. /// /// /// An object that contains the value of the value parameter. /// /// public static explicit operator sbyte(bigint value) => (sbyte)value.Value; /// /// Defines an explicit conversion of a bigint object to an unsigned /// 16-bit integer value. /// /// /// value: /// The value to convert to an unsigned 16-bit integer. /// /// /// An object that contains the value of the value parameter /// /// public static explicit operator ushort(bigint value) => (ushort)value.Value; /// /// Defines an explicit conversion of a bigint object to an unsigned /// 32-bit integer value. /// /// /// value: /// The value to convert to an unsigned 32-bit integer. /// /// /// An object that contains the value of the value parameter. /// /// public static explicit operator uint(bigint value) => (uint)value.Value; /// /// Defines an explicit conversion of a bigint object to an unsigned /// 64-bit integer value. /// /// /// value: /// The value to convert to an unsigned 64-bit integer. /// /// /// An object that contains the value of the value parameter. /// /// public static explicit operator ulong(bigint value) => (ulong)value.Value; /// /// Defines an explicit conversion of a System.Single object to a bigint /// value. /// /// /// value: /// The value to convert to a bigint. /// /// /// An object that contains the value of the value parameter. /// /// public static explicit operator bigint(float value) => new (value); /// /// Defines an explicit conversion of a bigint object to a 32-bit /// signed integer value. /// /// /// value: /// The value to convert to a 32-bit signed integer. /// /// /// An object that contains the value of the value parameter. /// /// public static explicit operator int(bigint value) => (int)value.Value; /// /// Defines an explicit conversion of a bigint object to a single-precision /// floating-point value. /// /// /// value: /// The value to convert to a single-precision floating-point value. /// /// /// An object that contains the closest possible representation of the value parameter. /// public static explicit operator float(bigint value) => (float)value.Value; /// /// Semigroup append /// bigint Semigroup.Combine(bigint y) => Value + y.Value; /// /// Monoid Empty /// static bigint Monoid.Empty => BigInteger.Zero; } ================================================ FILE: LanguageExt.Core/DataTypes/Change/Change.cs ================================================ using System; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; /// /// Represents changes to a value in a collection type (i.e. a key-value collection) /// /// Value type public abstract class Change : IEquatable>, Monoid> { /// /// Returns true if nothing has changed /// public bool HasNoChange => this is NoChange; /// /// Returns true if anything has changed (add, update, or removal) /// public bool HasChanged => !HasNoChange; /// /// Returns true if a value has been removed /// public bool HasRemoved => this is EntryRemoved; /// /// Returns true if a value has been mapped to another /// public bool HasMapped => this is EntryMappedTo; /// /// Returns true if a value has been mapped to another /// public bool HasMappedFrom() => this is EntryMappedFrom; /// /// Returns true if a value has been added /// public bool HasAdded => this is EntryAdded; /// /// If a value has been updated this will return Some(Value), else none /// public Option ToOption() => this switch { EntryAdded(var v) => Some(v), EntryMappedTo(var v) => Some(v), _ => Option.None }; /// /// Returns a `NoChange` state /// public static Change None => NoChange.Default; /// /// Returns a `EntryRemoved` state /// public static Change Removed(A oldValue) => new EntryRemoved(oldValue); /// /// Returns a `EntryAdded` state /// public static Change Added(A value) => new EntryAdded(value); /// /// Returns a `EntryMapped` state /// public static Change Mapped(FROM oldValue, A value) => new EntryMapped(oldValue, value); /// /// Equality /// public override bool Equals(object? obj) => obj is Change rhs && Equals(rhs); /// /// Equality /// public abstract bool Equals(Change? obj); /// /// Hash code /// public override int GetHashCode() => FNV32.OffsetBasis; public Change Combine(Change y) => (this, y) switch { (NoChange, _) => y, (_, NoChange) => this, (_, EntryRemoved) => y, (EntryRemoved (var vx), EntryAdded (var vy)) => Mapped(vx, vy), (EntryAdded, EntryMappedTo(var vz)) => Added(vz), (EntryMappedFrom(var vx), EntryMappedTo(var vz)) => Mapped(vx, vz), _ => y }; static Change Monoid>.Empty => None; } ================================================ FILE: LanguageExt.Core/DataTypes/Change/EntryAdded.cs ================================================ using System; using LanguageExt.ClassInstances; namespace LanguageExt; /// /// Entry added to a collection /// /// Value type public sealed class EntryAdded : Change, IEquatable> { /// /// Value that has been added /// public readonly A Value; internal EntryAdded(A value) => Value = value; public override bool Equals(Change? obj) => obj is EntryAdded rhs && Equals(rhs); public bool Equals(EntryAdded? rhs) => !ReferenceEquals(rhs, null) && EqDefault.Equals(Value, rhs.Value); public override int GetHashCode() => Value?.GetHashCode() ?? FNV32.OffsetBasis; public void Deconstruct(out A value) => value = Value; public override string ToString() => $"+{Value}"; } ================================================ FILE: LanguageExt.Core/DataTypes/Change/EntryMapped.cs ================================================ using System; using LanguageExt.ClassInstances; namespace LanguageExt; /// /// Existing entry updated to this value /// /// Value mapped to type public interface EntryMappedTo { /// /// Value mapped-to /// public B To { get; } /// /// Deconstructs to a single value /// void Deconstruct(out B @to); } /// /// Existing entry updated from this value /// /// Value mapped from type public interface EntryMappedFrom { /// /// Value mapped-from /// public A From { get; } /// /// Deconstructs to a single value /// void Deconstruct(out A @from); } /// /// Existing entry updated /// public sealed class EntryMapped : Change, EntryMappedFrom, EntryMappedTo, IEquatable> { internal EntryMapped(A @from, B to) { From = @from; To = @to; } /// /// Value mapped from /// public A From { get; } /// /// Value mapped to /// public B To { get; } public override bool Equals(Change? obj) => obj is EntryMapped rhs && Equals(rhs); public bool Equals(EntryMapped? rhs) => !ReferenceEquals(rhs, null) && EqDefault.Equals(From, rhs.From) && EqDefault.Equals(To, rhs.To); public override int GetHashCode() => FNV32.Next( From?.GetHashCode() ?? FNV32.OffsetBasis, To?.GetHashCode() ?? FNV32.OffsetBasis); public override string ToString() => $"{From} -> {To}"; public void Deconstruct(out B to) => to = To; public void Deconstruct(out A @from) => @from = From; public void Deconstruct(out A @from, out B to) { @from = From; to = To; } } ================================================ FILE: LanguageExt.Core/DataTypes/Change/EntryRemoved.cs ================================================ using System; using LanguageExt.ClassInstances; namespace LanguageExt; /// /// Existing entry removed /// /// Value type public sealed class EntryRemoved : Change, IEquatable> { /// /// Value that was removed /// public readonly A OldValue; internal EntryRemoved(A oldValue) => OldValue = oldValue; public override bool Equals(Change? obj) => obj is EntryRemoved rhs && Equals(rhs); public override int GetHashCode() => OldValue?.GetHashCode() ?? FNV32.OffsetBasis; public bool Equals(EntryRemoved? rhs) => !ReferenceEquals(rhs, null) && EqDefault.Equals(OldValue, rhs.OldValue); public void Deconstruct(out A oldValue) { oldValue = OldValue; } public override string ToString() => $"-{OldValue}"; } ================================================ FILE: LanguageExt.Core/DataTypes/Change/NoChange.cs ================================================ using System; namespace LanguageExt; /// /// No change to the collection /// /// Value type public sealed class NoChange : Change, IEquatable> { /// /// Singleton value of `NoChange` /// public static readonly Change Default = new NoChange(); private NoChange() { } public override bool Equals(Change? obj) => obj is NoChange; public bool Equals(NoChange? rhs) => rhs is not null; public override int GetHashCode() => FNV32.OffsetBasis; public override string ToString() => $"No Change"; } ================================================ FILE: LanguageExt.Core/DataTypes/Compositions/Compositions.Extensions.cs ================================================ using LanguageExt; using LanguageExt.Traits; public static class CompositionsExt { public static Compositions Cons(this A a, Compositions ma) where A : Monoid => Compositions.cons(a, ma); } ================================================ FILE: LanguageExt.Core/DataTypes/Compositions/Compositions.Module.cs ================================================ using static LanguageExt.Prelude; using LanguageExt.Traits; using System.Collections.Generic; using LanguageExt.ClassInstances; namespace LanguageExt; public static class Compositions { /// /// Returns true if the given tree is appropriately right-biased. /// public static bool wellFormed(Compositions ca) where EqA : Eq where A : Monoid { bool wellFormedNode(int n, Compositions.Node node) { var ni = node.Size; if (n == 1 && ni == 1 && node.Children.IsNone) return true; else if (node.Children.IsSome && n == ni) { var v = node.Value; var (l, r) = ((Compositions.Node, Compositions.Node))node.Children; return wellFormedNode(n / 2, l) && EqA.Equals(v, l.Value.Combine(r.Value)) && wellFormedNode(n / 2, r); } else { return false; } } bool go(int m, Seq.Node> ma) { if (ma.IsEmpty) return true; var x = ma.Head.Value!; var xs = ma.Tail; var s = x.Size; return s >= m && wellFormedNode(s, x) && go(s * 2, xs); } return go(1, ca.Tree); } /// /// Return the compositions list with the first `k` elements removed, in `O(log k)` time. /// public static Compositions skip(int amount, Compositions compositions) where A : Monoid { Seq.Node> go(int n, Seq.Node> nodes) { if (nodes.IsEmpty) return nodes; if (n <= 0) return nodes; var s = nodes.Head.Value!.Size; var c = nodes.Head.Value!.Children; var ri = nodes.Tail; var ord = n.CompareTo(s); if (ord < 0) { var (l, r) = ((Compositions.Node, Compositions.Node))c; return go(n, l.Cons(r.Cons(ri))); } else { return go(n - s, ri); } } return new Compositions(go(amount, compositions.Tree)); } /// /// Return the compositions list containing only the first `k` elements /// of the input. In the worst case, performs `O(k log k)` element compositions, /// in order to maintain the right-associative bias. If you wish to run `composed` /// on the result of `take`, use `takeComposed` for better performance. /// public static Compositions take(int amount, Compositions compositions) where A : Monoid { Seq.Node> go(int n, Seq.Node> nodes) { if (nodes.IsEmpty) return nodes; if (n <= 0) return Seq.Node>(); var x = nodes.Head.Value!; var s = x.Size; var c = x.Children; var ri = nodes.Tail; var ord = n.CompareTo(s); if (ord < 0) { var (l, r) = ((Compositions.Node, Compositions.Node))c; return go(n, l.Cons(r.Cons(ri))); } else { return new Compositions([x]) .Combine(new Compositions(go(n - s, ri))) .Tree; } } return new Compositions(go(amount, compositions.Tree)); } /// /// Returns the composition of the first `k` elements of the compositions list, doing only `O(log k)` compositions. /// Faster than simply using `take` and then `composed` separately. /// public static A takeComposed(int amount, Compositions compositions) where A : Monoid { A go(int n, Seq.Node> nodes) { if (nodes.IsEmpty) return A.Empty; if (n <= 0) return A.Empty; var x = nodes.Head.Value!; var s = x.Size; var c = x.Children; var v = x.Value; var ri = nodes.Tail; var ord = n.CompareTo(s); if (ord < 0) { var (l, r) = ((Compositions.Node, Compositions.Node))c; return go(n, l.Cons(r.Cons(ri))); } else { return v.Combine(go(n - s, ri)); } } return go(amount, compositions.Tree); } /// /// A convenience alias for 'take' and 'drop' /// public static (Compositions taken, Compositions skipped) splitAt(int i, Compositions c) where A : Monoid => (take(i, c), skip(i, c)); /// /// Compose every element in the compositions list. Performs only `O(log n)` compositions. /// public static A composed(Compositions compositions) where A : Monoid => FoldCompositions.Fold(compositions, A.Empty, (x, y) => x.Combine(y))(unit); /// /// Construct a compositions list containing just one element. /// public static Compositions singleton(A value) where A : Monoid => new ([new Compositions.Node(1, None, value)]); /// /// Get the number of elements in the compositions list, in `O(log n)` time. /// public static int count(Compositions compositions) where A : Monoid => compositions.Tree.Map(n => n.Size).Sum(); /// /// Add a new element to the front of a compositions list. Performs `O(log n)` element compositions. /// public static Compositions cons(A x, Compositions xs) where A : Monoid => singleton(x).Combine(xs); /// /// Convert a compositions list into a list of elements. The other direction /// is provided in the 'Data.Foldable.Foldable' instance.This will perform O(n log n) element compositions. /// public static Compositions fromList(IEnumerable ma) where A : Monoid => ma.AsIterable().Fold(Compositions.Empty, (s, x) => s.Combine(singleton(x))); } ================================================ FILE: LanguageExt.Core/DataTypes/Compositions/Compositions.cs ================================================ using static LanguageExt.Prelude; using LanguageExt.Traits; using System.Collections.Generic; using LanguageExt.ClassInstances; using System.Collections; using System; namespace LanguageExt; /// /// A _compositions list_ or _composition tree_ is a list data type where the elements are monoids, /// and the 'mconcat' of any contiguous sublist can be computed in logarithmic time. /// /// A common use case of this type is in a wiki, version control system, or collaborative editor, where /// each change or delta would be stored in a list, and it is sometimes necessary to compute the composed /// delta between any two versions. /// /// This version of a composition list is strictly biased to right-associativity, in that we only support /// efficient consing to the front of the list. This also means that the 'take' operation can be inefficient. /// /// The append operation `append(a, b)` performs `O(a log (a + b))` element compositions, so you want /// the left-hand list `a` to be as small as possible. /// public struct Compositions : IEquatable>, IEnumerable, Monoid> where A : Monoid { public static Compositions Empty { get; } = new(Seq()); public readonly Seq Tree; int? hashCode; internal Compositions(Seq tree) { hashCode = null; Tree = tree; } public Compositions Combine(Compositions compy) { var compx = this; Seq go(Seq mx, Seq my) { if (mx.IsEmpty) return my; if (my.IsEmpty) return go(mx.Tail, [mx.Head.Value!]); var x = mx.Head.Value!; var sx = mx.Head.Value!.Size; var cx = mx.Head.Value!.Children; var vx = mx.Head.Value!.Value; var xs = mx.Tail; var y = my.Head.Value!; var sy = my.Head.Value!.Size; var vy = my.Head.Value!.Value; var ys = my.Tail; var ord = sx.CompareTo(sy); if (ord < 0) return go(xs, x.Cons(my)); else if (ord > 0) { var (l, r) = ((Node, Node))cx; return go(r.Cons(l.Cons(xs)), my); } else { return go(new Node(sx + sy, Some((x, y)), vx.Combine(vy)).Cons(xs), ys); } } return new Compositions(go(compx.Tree, compy.Tree)); } /// /// Returns true if the given tree is appropriately right-biased. /// public bool WellFormed() where EqA : Eq => Compositions.wellFormed(this); /// /// Return the compositions list with the first `k` elements removed, in `O(log k)` time. /// public Compositions Skip(int amount) => Compositions.skip(amount, this); /// /// Return the compositions list containing only the first `k` elements /// of the input. In the worst case, performs `O(k log k)` element compositions, /// in order to maintain the right-associative bias. If you wish to run `composed` /// on the result of `take`, use `takeComposed` for better performance. /// public Compositions Take(int amount) => Compositions.take(amount, this); /// /// Returns the composition of the first `k` elements of the compositions list, doing only `O(log k)` compositions. /// Faster than simply using `take` and then `composed` separately. /// public A TakeComposed(int amount) => Compositions.takeComposed(amount, this); /// /// A convenience alias for 'take' and 'skip' /// public (Compositions taken, Compositions skipped) SplitAt(int i) => Compositions.splitAt(i, this); /// /// Compose every element in the compositions list. Performs only `O(log n)` compositions. /// public A Composed() => Compositions.composed(this); /// /// Construct a compositions list containing just one element. /// public static Compositions Singleton(A value) => Compositions.singleton(value); /// /// Get the number of elements in the compositions list, in `O(log n)` time. /// public int Count() => Compositions.count(this); /// /// Convert a compositions list into a list of elements. The other direction /// is provided in the 'Data.Foldable.Foldable' instance.This will perform O(n log n) element compositions. /// public static Compositions FromList(IEnumerable ma) => Compositions.fromList(ma); /// /// Equality operator /// public bool Equals(Compositions b) => EqEnumerable.Equals(this, b); /// /// Equality operator /// public override bool Equals(object? obj) => obj is Compositions b && Equals(b); /// /// Equality operator /// public static bool operator ==(Compositions a, Compositions b) => a.Equals(b); /// /// Equality operator /// public static bool operator !=(Compositions a, Compositions b) => !(a == b); /// /// Get hash code /// /// public override int GetHashCode() => hashCode ?? (int)(hashCode = hash(this)); /// /// Convert to a sequence /// public Seq ToSeq() => FoldCompositions.Fold(this, Seq(), (s, x) => x.Cons(s))(unit); /// /// Convert to an enumerable /// public Iterable AsIterable() => FoldCompositions.Fold(this, Seq(), (s, x) => x.Cons(s))(unit).AsIterable(); /// /// Get enumerator /// public IEnumerator GetEnumerator() => // ReSharper disable once NotDisposedResourceIsReturned AsIterable().GetEnumerator(); /// /// Get enumerator /// IEnumerator IEnumerable.GetEnumerator() => // ReSharper disable once NotDisposedResourceIsReturned AsIterable().GetEnumerator(); public class Node { public readonly int Size; public readonly Option<(Node, Node)> Children; public readonly A Value; public Node(int size, Option<(Node, Node)> children, A value) { Size = size; Children = children; Value = value; } } } ================================================ FILE: LanguageExt.Core/DataTypes/Compositions/FoldCompositions.cs ================================================ using static LanguageExt.Prelude; using LanguageExt.Traits; using System; namespace LanguageExt.ClassInstances; public struct FoldCompositions where A : Monoid { static S FoldNode(S state, Func f, Compositions.Node node) { if (node.Children.IsNone) return f(state, node.Value); var (l, r) = ((Compositions.Node, Compositions.Node))node.Children; state = FoldNode(state, f, l); state = FoldNode(state, f, r); return state; } static S FoldNodes(S state, Func f, Seq.Node> nodes) => nodes.Fold(state, (s, n) => FoldNode(s, f, n)); static S FoldNodeBack(S state, Func f, Compositions.Node node) { if (node.Children.IsNone) return f(state, node.Value); var (l, r) = ((Compositions.Node, Compositions.Node))node.Children; state = FoldNode(state, f, r); state = FoldNode(state, f, l); return state; } static S FoldNodesBack(S state, Func f, Seq.Node> nodes) => nodes.FoldBack(state, (s, n) => FoldNode(s, f, n)); internal static Seq FoldMap(Func f, Seq.Node> nodes) => FoldNodes(Seq(), (s, n) => f(n).Cons(s), nodes); internal static Seq FoldMapBack(Func f, Seq.Node> nodes) => FoldNodesBack(Seq(), (s, n) => f(n).Cons(s), nodes); public static Func Count(Compositions fa) => _ => FoldNodes(0, (s, _) => s + 1, fa.Tree); public static Func Fold(Compositions fa, S state, Func f) => _ => FoldNodes(state, f, fa.Tree); public static Func FoldBack(Compositions fa, S state, Func f) => _ => FoldNodesBack(state, f, fa.Tree); } ================================================ FILE: LanguageExt.Core/DataTypes/Fail/Fail.Extensions.cs ================================================ using System; using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt; public static class FailExtensions { extension(Fail lhs) { /// /// Value extraction /// public static A operator >>(Fail px, Lower _) => px.Value; } extension(Fail lhs) { /// /// Function composition /// /// Input /// Function /// Result of invoking the function public static Fail operator >>(Fail x, Func f) => new (f(x.Value)); } //////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Conversion // public static Validation ToValidation(this Fail fail) where F : Monoid => Validation.Fail(fail.Value); extension(Fail fail) { public Fin ToFin() => Fin.Fail(fail.Value); public Eff ToEff() => Eff.Fail(fail.Value); public Eff ToEff() => Eff.Fail(fail.Value); } } ================================================ FILE: LanguageExt.Core/DataTypes/Fail/Fail.cs ================================================ using System; using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt; /// /// Represents an error value. /// /// /// On its own this doesn't do much, but allows other monads to convert /// from it and provide binding extensions that mean it can be lifted into /// other monads without specifying lots of extra generic arguments. /// /// Bound value /// Bound value type public readonly record struct Fail(E Value) { //////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Standard operators // public Fail MapFail(Func f) => new(f(Value)); //////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Conversion // public Either ToEither() => Either.Left(Value); public override string ToString() => $"Fail({Value})"; public Either SelectMany(Func> bind, Func project) => this; public Option SelectMany(Func> bind, Func project) => default; } ================================================ FILE: LanguageExt.Core/DataTypes/Loop/Loop.Extensions.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public static class LoopExtensions { extension(Loop lhs) { /// /// Value extraction /// public static A operator >>(Loop px, Lower _) => px.Value; } extension(Loop lhs) { /// /// Function composition /// /// Input /// Function /// Result of invoking the function public static Loop operator >>(Loop x, Func f) => new (f(x.Value)); } } ================================================ FILE: LanguageExt.Core/DataTypes/Loop/Loop.Prelude.cs ================================================ using System; using System.Diagnostics.Contracts; namespace LanguageExt; public static partial class Prelude { [Pure] public static Loop Loop(A value) => new (value); } ================================================ FILE: LanguageExt.Core/DataTypes/Loop/Loop.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; /// /// Represents a value to use in tail-recursive loops. Usually understood to be the value t /// pass to the next stage of the loop so it can continue (and either generated by the previous /// loop-stage or is the initial trigger-value. /// /// /// /// On its own this doesn't do much, but allows other types to convert from it and provide /// binding extensions that mean it can be lifted into other monads without specifying lots /// of extra generic arguments. /// /// Bound value /// Bound value type public readonly record struct Loop(A Value) { /// /// Functor map /// /// Mapping function /// Result bound value type /// Result of the applying the mapping function to the `Pure` value public Loop Map(Func f) => new (f(Value)); /// /// Convert to a `Next` value /// public Next ToNext() => Next.Loop(Value); } ================================================ FILE: LanguageExt.Core/DataTypes/Lower/Lower.cs ================================================ namespace LanguageExt; public readonly record struct Lower; ================================================ FILE: LanguageExt.Core/DataTypes/MapPatch/HashMapPatch.cs ================================================ using System.Runtime.CompilerServices; using LanguageExt.ClassInstances; using LanguageExt.Traits; namespace LanguageExt; /// /// Represents change to a Hash-Map /// /// /// This is primarily used by the `Change` events on the `AtomHashMap` types, /// and the `Changes` property of `TrackingHashMap`. /// /// Key type /// Value type public sealed class HashMapPatch { readonly TrieMap, K, V> prev; readonly TrieMap, K, V> curr; readonly TrieMap, K, Change>? changes; readonly K? key; readonly Change? change; [MethodImpl(MethodImplOptions.AggressiveInlining)] internal HashMapPatch( TrieMap, K, V> prev, TrieMap, K, V> curr, TrieMap, K, Change> changes) { this.prev = prev; this.curr = curr; this.changes = changes; } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal HashMapPatch( TrieMap, K, V> prev, TrieMap, K, V> curr, K key, Change change) { this.prev = prev; this.curr = curr; this.key = key; this.change = change; } public HashMap From { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new(prev); } public HashMap To { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new(curr); } public HashMap> Changes { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => changes is null ? HashMap>.Empty.Add(key!, change!) : new HashMap>(changes); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public override string ToString() => Changes.ToString(); } /// /// Represents change to a Hash-Map /// /// /// This is primarily used by the `Change` events on the `AtomHashMap` types, /// and the `Changes` property of `TrackingHashMap`. /// /// Key type /// Value type public sealed class HashMapPatch where EqK : Eq { readonly TrieMap prev; readonly TrieMap curr; readonly TrieMap>? changes; readonly K? key; readonly Change? change; [MethodImpl(MethodImplOptions.AggressiveInlining)] internal HashMapPatch( TrieMap prev, TrieMap curr, TrieMap> changes) { this.prev = prev; this.curr = curr; this.changes = changes; } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal HashMapPatch( TrieMap prev, TrieMap curr, K key, Change change) { this.prev = prev; this.curr = curr; this.key = key; this.change = change; } public HashMap From { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new (prev); } public HashMap To { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new (curr); } public HashMap> Changes { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => changes is null ? HashMap>.Empty.Add(key!, change!) : new HashMap>(changes); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public override string ToString() => Changes.ToString(); } ================================================ FILE: LanguageExt.Core/DataTypes/Next/Next.Module.cs ================================================ namespace LanguageExt; /// /// Module for the `Next` data-type /// public static class Next { /// /// Continue the recursion /// /// Value to pass back to the recursive function to 'go around' /// Loop value type /// Done value type /// Next structure public static Next Loop(A value) => new (1, value, default!); /// /// Cancel the recursion and return /// /// Final value /// Loop value type /// Loop value type /// Next structure public static Next Done(B value) => new (2, default!, value); } ================================================ FILE: LanguageExt.Core/DataTypes/Next/Next.cs ================================================ using System; using System.Runtime.CompilerServices; using LanguageExt.Common; using static LanguageExt.Prelude; namespace LanguageExt; /// /// Simple data type that helps indicate whether a recursive function should loop or not. /// /// /// This was created to pair with the `Monad` trait `Tail` function and is extremely lightweight. /// /// /// It's designed to cause no additional allocations when used in a tail-recursive manner. That does mean there /// are some footguns in here if you're not careful. So, make sure that before you access `Loop` or `Done` /// that you've confirmed the state of the structure by using `IsLoop` or `IsDone`. /// /// You can then use C#'s pattern-matching to extract the value from the structure: /// /// var result = next switch /// { /// { IsLoop: true, Loop: var value } => ..., /// { IsDone: true, Done: var value } => ..., /// _ => throw new Exception("Invalid state") /// }; /// /// /// If we ever get real struct discriminated unions, then this can be replaced with that. /// /// Loop value type /// Done value type public readonly struct Next { readonly int state; readonly A left; readonly B right; internal static Next UnsafeDefault = default; internal Next(int state, A left, B right) { this.state = state; this.left = left; this.right = right; } /// /// Pattern match /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public C Match(Func Loop, Func Done) => IsLoop ? Loop(left) : IsDone ? Done(right) : throw new BottomException(); public Next Map(Func f) => state switch { 1 => new Next(state, left, default!), 2 => new Next(state, left, f(right)), _ => throw new BottomException() }; public Either ToEither() => state switch { 1 => Left(left), 2 => Right(right), _ => throw new BottomException() }; /// /// Returns true if we should loop /// public bool IsLoop { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => state == 1; } /// /// Returns true if we should complete and return /// public bool IsDone { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => state == 2; } /// /// Try to access the loop-value type /// /// Either a valid `A` value or will throw an exception /// Throws if the structure is in a done state public A Loop { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => state == 1 ? left : throw new InvalidCastException(); } /// /// Try to access the done-value type /// /// Either a valid `B` value or will throw an exception /// Throws if the structure is in a done state public B Done { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => state == 2 ? right : throw new InvalidCastException(); } /// /// Explicit cast to the complete value type /// /// Next structure /// Either a valid `A` value or will throw an exception /// Throws if the structure is in a done state [MethodImpl(MethodImplOptions.AggressiveInlining)] public static explicit operator A(Next next) => next.Loop; /// /// Explicit cast to the complete value type /// /// Next structure /// Either a valid `A` value or will throw an exception /// Throws if the structure is in a done state [MethodImpl(MethodImplOptions.AggressiveInlining)] public static explicit operator B(Next next) => next.Done; [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator Next(Loop value) => Next.Loop(value.Value); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator Next(Pure value) => Next.Done(value.Value); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator Next(Either value) => value switch { Either.Left(var l) => Next.Loop(l), Either.Right(var r) => Next.Done(r), _ => throw new BottomException() }; [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator Either(Next value) => value.ToEither(); } ================================================ FILE: LanguageExt.Core/DataTypes/Patch/Edit.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; /// /// Data type that represents an edit in a patch. Supports three sub-class 'cases': /// /// Edit.Insert /// Edit.Delete /// Edit.Replace /// /// These represent the total set of operations that can be represented in a `Patch` /// /// /// public abstract class Edit : IEquatable> where EqA : Eq { public readonly int Position; public readonly A Element; /// /// Ctor /// Edit(int position, A element) { Position = position; Element = element; } /// /// Position lens that allows for the Position to be modified /// internal abstract Edit Index(Func f); /// /// Maps the outgoing value using the function provided. This has /// no effect on Insert /// public abstract Edit MapOld(Func f); /// /// Maps the new value using the function provided. This has /// no effect on Delete /// public abstract Edit MapNew(Func f); /// /// Equality operator /// public override bool Equals(object? obj) => obj is Edit edit && Equals(edit); /// /// Equality operator /// public static bool operator ==(Edit a, Edit b) => a.Equals(b); /// /// Non-equality operator /// public static bool operator !=(Edit a, Edit b) => !(a == b); /// /// Hash code provider /// public override int GetHashCode() => (Position + 13) * Element!.GetHashCode(); /// /// Equality operator /// public bool Equals(Edit? other) => ReferenceEquals(this, other) || (Position == other?.Position && GetType() == other.GetType() && EqA.Equals(Element, other.Element)); /// /// Represents an Insert edit operation /// public sealed class Insert : Edit, IEquatable { public Insert(int position, A element) : base(position, element) { } public static Insert New(int position, A element) => new Insert(position, element); public override string ToString() => $"Insert({Position}, {Element})"; internal override Edit Index(Func f) => new Insert(f(Position), Element); public override Edit MapOld(Func f) => this; public override Edit MapNew(Func f) => new Insert(Position, f(Element)); public bool Equals(Insert? other) => base.Equals(other); public override bool Equals(object? obj) => obj is Insert ins && Equals(ins); public static bool operator ==(Insert a, Insert b) => a.Equals(b); public static bool operator !=(Insert a, Insert b) => !(a == b); public override int GetHashCode() => (Position + 13) * (Element!.GetHashCode() + 7); } /// /// Represents an Delete edit operation /// public sealed class Delete : Edit, IEquatable { public Delete(int position, A element) : base(position, element) { } public static Delete New(int position, A element) => new Delete(position, element); public override string ToString() => $"Delete({Position}, {Element})"; internal override Edit Index(Func f) => new Delete(f(Position), Element); public override Edit MapOld(Func f) => new Delete(Position, f(Element)); public override Edit MapNew(Func f) => this; public bool Equals(Delete? other) => base.Equals(other); public override bool Equals(object? obj) => obj is Delete del && Equals(del); public static bool operator ==(Delete a, Delete b) => a.Equals(b); public static bool operator !=(Delete a, Delete b) => !(a == b); public override int GetHashCode() => (Position + 13) * (Element!.GetHashCode() + 7); } /// /// Represents an Replace edit operation /// public sealed class Replace : Edit, IEquatable { public readonly A ReplaceElement; public Replace(int position, A element, A replaceElement) : base(position, element) => ReplaceElement = replaceElement; public static Replace New(int position, A element, A replaceElement) => new (position, element, replaceElement); public override string ToString() => $"Replace({Position}, {Element} -> {ReplaceElement})"; internal override Edit Index(Func f) => new Replace(f(Position), Element, ReplaceElement); public override Edit MapOld(Func f) => new Replace(Position, f(Element), ReplaceElement); public override Edit MapNew(Func f) => new Replace(Position, Element, f(ReplaceElement)); public bool Equals(Replace? other) => ReferenceEquals(this, other) || (base.Equals(other) && EqA.Equals(ReplaceElement, other.ReplaceElement)); public override bool Equals(object? obj) => obj is Replace repl && Equals(repl); public static bool operator ==(Replace a, Replace b) => a.Equals(b); public static bool operator !=(Replace a, Replace b) => !(a == b); public override int GetHashCode() => (Position + 13) * (Element!.GetHashCode() + 17) * (ReplaceElement!.GetHashCode() + 7); } } ================================================ FILE: LanguageExt.Core/DataTypes/Patch/Patch.Internal.cs ================================================ using static LanguageExt.Prelude; using LanguageExt.ClassInstances; using LanguageExt.Traits; using System; using System.Linq; namespace LanguageExt; internal static class PatchInternal { public static (A, Seq) mapAccumR(Func f, A state, Seq t) { if (t.IsEmpty) return (state, Seq()); var (a1, c1) = mapAccumR(f, state, t.Tail); var (a, c) = f(a1, t.Head.Value!); return (a, c.Cons(c1)); } public static (A, Seq) mapAccumL(Func f, A state, Seq t) { if (t.IsEmpty) return (state, Seq()); var (a, c) = f(state, t.Head.Value!); var (a1, c1) = mapAccumL(f, a, t.Tail); return (a1, c.Cons(c1)); } public static (C, Seq) leastChanges(PatchParams p, SpanArray ss, SpanArray tt) where OrdC : Ord where C : Monoid { var rawChanges = rawChanges(p, ss, tt); var changes = rawChanges.Last; var newlst = changes.Map(pair => toSeq(pair.Item2.Somes().Reverse())); return (changes.Item1, newlst); } static (int quot, int rem) quotRem(int x, int y) => (x / y, x % y); static A minimumBy(Func compare, Lst list) => list.Count == 0 ? throw new Exception("List is empty") : list.Fold(list[0], (x, y) => compare(x, y) > 0 ? y : x); static SpanArray constructN(int n, Func, A> f) { var vector = SpanArray.New(n); for (var i = 0; i < n; i++) { var slice = vector.Slice(0, i); var x = f(slice); vector[i] = x; } return vector; } public static SpanArray<(C, Seq>)> rawChanges(PatchParams p, SpanArray src, SpanArray dst) where OrdC : Ord where C : Monoid { var lenX = 1 + dst.Count; var lenY = 1 + src.Count; var lenN = lenX * lenY; int ix(int x, int y) => x * lenY + y; (C, Seq>) get(SpanArray<(C, Seq>)> m, int x, int y) { int i = ix(x, y); return i < m.Count ? m[i] : throw new Exception($"Unable to get ({x},{y}) from change matrix"); } int position(Seq> seq) => seq.Map(oo => oo.Map(p.positionOffset).IfNone(1)).Sum(); (C, Seq>) ctr(SpanArray<(C, Seq>)> v) { var (quot, rem) = quotRem(v.Count, lenY); if (quot == 0 && rem == 0) { return (C.Empty, Seq>()); } else if (quot == 0) { var y = rem - 1; var o = p.delete(0, src[y]); var (pc, po) = get(v, 0, y); return (p.cost(o).Combine(pc), Some(o).Cons(po)); } else if (rem == 0) { var x = quot - 1; var o = p.insert(x, dst[x]); var (pc, po) = get(v, x, 0); return (p.cost(o).Combine(pc), Some(o).Cons(po)); } else { var y = rem - 1; var x = quot - 1; var s = src[y]; var d = dst[x]; var tl = get(v, x, y); var top = get(v, x + 1, y); var left = get(v, x, y + 1); if (p.equivalent(s, d)) { return (tl.Item1, Prelude.Cons(None, tl.Item2)); } else { var c1 = p.delete(position(top.Item2), s); var item1 = (p.cost(c1).Combine(top.Item1), Some(c1).Cons(top.Item2)); var c2 = p.insert(position(left.Item2), d); var item2 = (p.cost(c2).Combine(left.Item1), Some(c2).Cons(left.Item2)); var c3 = p.substitute(position(tl.Item2), s, d); var item3 = (p.cost(c3).Combine(tl.Item1), Some(c3).Cons(tl.Item2)); var compare = OrdTupleFirst>>.Compare; return minimumBy(compare, List(item1, item2, item3)); } } } return constructN<(C, Seq>)>(lenN, ctr); } } ================================================ FILE: LanguageExt.Core/DataTypes/Patch/Patch.Module.cs ================================================ using static LanguageExt.Prelude; using LanguageExt.Traits; using System; using System.Linq; using System.Collections.Generic; namespace LanguageExt; /// /// A `Patch` is a collection of edits performed to a _document_, in this case an 'IEnumerable〈A〉'. /// They are implemented as a list of 'Edit', and can be converted to and from raw lists of edits using /// `toList` and `fromList` respectively. /// /// Patches form a groupoid (a 'Monoid' with inverses, and a partial composition relation), where the /// inverse element can be computed with 'inverse' and the groupoid operation is a _composition_ of /// patches. Applying `append(p1, p2)` is the same as applying `p1` _then_ /// `p2` (see `Patch.apply`). This composition operator may produce structurally different patches depending /// on associativity, however the patches are guaranteed to be _equivalent_ in the sense that the resultant /// document will be the same when they are applied. /// /// For convenience, we make our composition operator here total, to fit the `Monoid` typeclass, but provide /// some predicates (`Patch.composable` and `Patch.applicable`) to determine if the operation can be validly /// used. /// public static class Patch { /// /// Convert a list of edits to a patch, making sure to eliminate conflicting edits /// and sorting by index. /// public static Patch unsafeFromSeq(Seq> edits) where EqA : Eq => new (edits); /// /// Convert a list of edits to a patch, making sure to eliminate conflicting edits /// and sorting by index. /// public static Patch fromSeq(Seq> edits) where EqA : Eq => new (toSeq((from es in normalise(edits) group es by es.Position into eg from ed in eg.Distinct() orderby ed.Position select ed).ToArray())); /// /// Internal: Eliminate conflicting edits /// internal static Seq> normalise(Seq> edits) where EqA : Eq { var (inss, dels, repls) = partition3(edits); return normalise1(inss, dels, repls); static (Seq.Insert> inserts, Seq.Delete> deletes, Seq.Replace> replaces) partition3(Seq> grp) { if (grp.IsEmpty) return (Seq.Insert>(), Seq.Delete>(), Seq.Replace>()); if (grp.Head.Value! is Edit.Insert ins) { var (i, d, r) = partition3(grp.Tail); return (ins.Cons(i), d, r); } else if (grp.Head.Value! is Edit.Delete del) { var (i, d, r) = partition3(grp.Tail); return (i, del.Cons(d), r); } else if (grp.Head.Value! is Edit.Replace repl) { var (i, d, r) = partition3(grp.Tail); return (i, d, repl.Cons(r)); } else { throw new NotSupportedException(); } } static Seq> normalise1(Seq.Insert> inserts, Seq.Delete> deletes, Seq.Replace> replaces) { if (!inserts.IsEmpty && !deletes.IsEmpty) return normalise1(inserts.Tail, deletes.Tail, Edit.Replace.New(deletes.Head.Value!.Position, deletes.Head.Value!.Element, inserts.Head.Value!.Element).Cons(replaces)); if (deletes.IsEmpty) return toSeq(inserts.Map(i => i as Edit).ConcatFast(replaces.Take(1)).ToArray()); if (inserts.IsEmpty) return [deletes.Head.Value!]; throw new InvalidOperationException(); } } /// /// Monoid append: produces a patch is a merged version of both provided /// patches. /// public static Patch append(Patch px, Patch py) where EqA : Eq => px.Combine(py); /// /// Compute the inverse of a patch /// public static Patch inverse(Patch patch) where EqA : Eq { return new Patch(PatchInternal.mapAccumL(go, 0, patch.Edits).Item2); (int, Edit) go(int off, Edit edit) => edit switch { Edit.Insert ins => (off + 1, Edit.Delete.New(off + ins.Position, ins.Element)), Edit.Delete del => (off - 1, Edit.Insert.New(off + del.Position, del.Element)), Edit.Replace rpl => (off, Edit.Replace.New(off + rpl.Position, rpl.ReplaceElement, rpl.Element)), _ => throw new NotSupportedException() }; } /// /// Returns true if a patch can be safely applied to a document, that is, /// `applicable(p, d)` holds when `d` is a valid source document for the patch `p`. /// public static bool applicable(Patch pa, IEnumerable va) where EqA : Eq { var i = SpanArray.New(va); return pa.Edits.ForAll( e => e switch { Edit.Insert ins => ins.Position <= i.Count, Edit.Delete del => i.Elem(del.Position).Match( Some: ci => EqA.Equals(del.Element, ci), None: () => false), _ => e is Edit.Replace repl && i.Elem(repl.Position).Match( Some: ci => EqA.Equals(repl.Element, ci), None: () => false) }); } /// /// Returns true if a patch can be validly composed with another. /// That is, `composable(p, q)` holds if `q` can be validly applied after `p`. /// public static bool composable(Patch pa, Patch pb) where EqA : Eq { return go(pa.Edits, pb.Edits, 0); static bool go(Seq> ea, Seq> eb, int off) { if (ea.IsEmpty || eb.IsEmpty) return true; var x = ea.Head.Value!; var xs = ea.Tail; var y = eb.Head.Value!; var ys = eb.Tail; var yi = y.Index(i => i + off); var xi = x.Position; if (xi < yi.Position) { return go(xs, eb, off + offset(x)); } else if (xi > yi.Position) { return go(ea, ys, off); } else { switch (x) { case Edit.Delete when yi is Edit.Insert: return go(xs, ys, off + offset(x)); case Edit.Delete: return go(xs, eb, off + offset(x)); default: { return yi is Edit.Insert ? go(ea, ys, off) : x switch { Edit.Replace replA1 when yi is Edit.Replace replB1 => EqA.Equals(replA1.ReplaceElement, replB1.Element) && go(xs, ys, off), Edit.Replace replA2 when yi is Edit.Delete del3 => EqA.Equals(replA2.ReplaceElement, del3.Element) && go(xs, ys, off), Edit.Insert ins3 when yi is Edit.Replace replB2 => EqA.Equals(ins3.Element, replB2.Element) && go(xs, ys, off + offset(x)), Edit.Insert ins4 when yi is Edit.Delete del4 => EqA.Equals(ins4.Element, del4.Element) && go(xs, ys, off + offset(x)), _ => throw new NotSupportedException() }; } } } } static int offset(Edit edit) => edit switch { Edit.Insert => -1, Edit.Delete => 1, Edit.Replace => 0, _ => throw new NotSupportedException() }; } /// /// Build the default parameters for building a patch /// static PatchParams, MInt32> parms() where EqA : Eq => new (EqA.Equals, Edit.Delete.New, Edit.Insert.New, Edit.Replace.New, static _ => MInt32.One, static x => x is Edit.Delete ? 0 : 1); /// /// Returns the delta of the document's size when a patch is applied. /// Essentially the number of `Insert` minus the number of `Delete`. /// public static int sizeChange(Patch patch) where EqA : Eq { var count = 0; foreach (var item in patch.Edits) { switch (item) { case Edit.Delete: count--; break; case Edit.Insert: count++; break; } } return count; } /// /// Apply a patch to a document, returning the transformed document. /// public static Lst apply(Patch patch, Lst va) where EqA : Eq => toList(apply(patch, va.AsIterable())); /// /// Apply a patch to a document, returning the transformed document. /// public static Seq apply(Patch patch, Seq va) where EqA : Eq => toSeq(apply(patch, va.AsEnumerable())); /// /// Apply a patch to a document, returning the transformed document. /// public static Arr apply(Patch patch, Arr va) where EqA : Eq => apply(patch, va.AsIterable()).ToArr(); /// /// Apply a patch to a document, returning the transformed document. /// public static A[] apply(Patch patch, A[] va) where EqA : Eq => apply(patch, va.AsEnumerable()).ToArray(); /// /// Apply a patch to a document, returning the transformed document. /// public static SpanArray apply(Patch patch, SpanArray va) where EqA : Eq => SpanArray.New(apply(patch, va.AsEnumerable())); /// /// Apply a patch to a document, returning the transformed document. /// public static List apply(Patch patch, List va) where EqA : Eq => [..apply(patch, va.AsEnumerable())]; /// /// Apply a patch to a document, returning the transformed document. /// public static Iterable apply(Patch patch, IEnumerable va) where EqA : Eq { if (patch.Edits.Count == 0) return va.AsIterable(); var i = SpanArray.New(va); var dlength = i.Count + sizeChange(patch); var d = SpanArray.New(dlength); go(patch.Edits, i, d, 0); return d.AsIterable(); static Unit go(Seq> edits, SpanArray src, SpanArray dest, int si) { while (true) { if (edits.IsEmpty) { return dest.Count > 0 ? src.UnsafeCopy(dest) : unit; } else { var x = edits.Head.Value!.Position - si; if (x > 0) { src.Take(x).UnsafeCopy(dest.Take(x)); src = src.Skip(x); dest = dest.Skip(x); si = si + x; } else { switch (edits.Head.Value!) { case Edit.Insert insert: dest[0] = insert.Element; edits = edits.Tail; dest = dest.Tail; continue; case Edit.Delete: edits = edits.Tail; src = src.Tail; si = si + 1; continue; case Edit.Replace replace: dest[0] = replace.ReplaceElement; edits = edits.Tail; src = src.Tail; dest = dest.Tail; si = si + 1; continue; default: throw new NotSupportedException(); } } } } } } /// /// Empty patch /// public static Patch empty() where EqA : Eq => Patch.Empty; /// /// Resolve a conflict by always using the left-hand side /// public static A ours(A x, A y) => x; /// /// Resolve a conflict by always using the right-hand side /// public static A theirs(A x, A y) => y; /// /// A convenience version of `transformWith` which resolves conflicts using `append`. /// public static (Patch a, Patch b) transform(Patch p, Patch q) where EqA : Eq where A : Monoid => transformWith((x, y) => x.Combine(y), p, q); /// /// Given two diverging patches `p` and `q`, `transform(m, p, q)` returns /// a pair of updated patches `(np, nq)` such that `append(q, np)` and /// `append(p, nq)` are equivalent patches that incorporate the changes /// of _both_ `p` and `q`, up to merge conflicts, which are handled by /// the provided function `m`. /// /// This is the standard `transform` function of Operational Transformation /// patch resolution techniques, and can be thought of as the pushout /// of two diverging patches within the patch groupoid. /// public static (Patch a, Patch b) transformWith(Func conflict, Patch p, Patch q) where EqA : Eq { var (pi, qi) = go(p.Edits, 0, q.Edits, 0, conflict); return (new Patch(pi), new Patch(qi)); static (Seq> a, Seq> b) go(Seq> xs, int a, Seq> ys, int b, Func conflict) { if (xs.IsEmpty && ys.IsEmpty) return (xs, ys); if (ys.IsEmpty) return (xs.Map(e => e.Index(i => i + a)), ys); if (xs.IsEmpty) return (xs, ys.Map(e => e.Index(i => i + b))); var x = xs.Head.Value!; var y = ys.Head.Value!; var ord = x.Position.CompareTo(y.Position); switch (ord) { case < 0: { var (_1, _2) = go(xs.Tail, a, ys, b + offset(x), conflict); return (x.Index(i => i + a).Cons(_1), _2); } case > 0: { var (_1, _2) = go(xs, a + offset(y), ys.Tail, b, conflict); return (_1, y.Index(i => i + b).Cons(_2)); } default: { if (x == y) { return go(xs.Tail, a + offset(y), ys.Tail, b + offset(x), conflict); } else switch (x) { case Edit.Insert ins1 when y is Edit.Insert ins2: { var n = conflict(ins1.Element, ins2.Element); return cons2( (Edit.Replace.New(ins1.Position + a, ins2.Element, n), Edit.Replace.New(ins1.Position + b, ins1.Element, n)), go(xs.Tail, a + offset(y), ys.Tail, b + offset(x), conflict)); } case Edit.Replace repl1 when y is Edit.Replace repl2: { var n = conflict(repl1.ReplaceElement, repl2.ReplaceElement); return cons2( (Edit.Replace.New(repl1.Position + a, repl2.ReplaceElement, n), Edit.Replace.New(repl1.Position + b, repl1.ReplaceElement, n)), go(xs.Tail, a, ys.Tail, b, conflict)); } case Edit.Insert: { var (_1, _2) = go(xs.Tail, a, ys, b + offset(x), conflict); return (x.Index(i => i + a).Cons(_1), _2); } default: { if (y is Edit.Insert) { var (_1, _2) = go(xs, a + offset(y), ys.Tail, b, conflict); return (_1, y.Index(i => i + b).Cons(_2)); } else switch (x) { case Edit.Replace repl3 when y is Edit.Delete: { var (_1, _2) = go(xs.Tail, a + offset(y), ys.Tail, b, conflict); return (_1, (Edit.Delete.New(repl3.Position, repl3.ReplaceElement)).Index(i => i + b).Cons(_2)); } case Edit.Delete when y is Edit.Replace repl4: { var (_1, _2) = go(xs.Tail, a, ys.Tail, b + offset(x), conflict); return ((Edit.Delete.New(repl4.Position, repl4.ReplaceElement)).Index(i => i + a).Cons(_1), _2); } case Edit.Delete when y is Edit.Delete: return go(xs.Tail, a + offset(y), ys.Tail, b + offset(x), conflict); default: throw new NotSupportedException(); } } } } } } static int offset(Edit edit) => edit switch { Edit.Insert => 1, Edit.Delete => -1, Edit.Replace => 0, _ => throw new NotSupportedException() }; static (Seq>, Seq>) cons2((Edit cx, Edit cy) head, (Seq> cxs, Seq> cys) tail) => (head.cx.Cons(tail.cxs), head.cy.Cons(tail.cys)); } /// /// Compute the difference between two documents, using the Wagner-Fischer algorithm. /// O(mn) time and space. /// /// apply(diff(d,e), d) == e /// /// diff(d, d) == Patch.empty /// /// apply(diff(d, e), d) == apply(inverse(diff(e, d)), d) /// /// apply(append(diff(a, b), diff(b, c), a) == apply(diff(a, c), a) /// /// applicable(diff(a, b) a) /// /// public static Patch diff(IEnumerable va, IEnumerable vb) where EqA : Eq { var (_, s) = PatchInternal.leastChanges, MInt32>( parms(), SpanArray.New(va), SpanArray.New(vb)); return new Patch(adjust(0, s)); static Seq> adjust(int o, Seq> list) => list.IsEmpty ? Seq>() : list.Head.Value! is Edit.Insert ia ? Edit.Insert.New(ia.Position + o, ia.Element).Cons(adjust(o - 1, list.Tail)) : list.Head.Value! is Edit.Delete da ? Edit.Delete.New(da.Position + o, da.Element).Cons(adjust(o + 1, list.Tail)) : list.Head.Value! is Edit.Replace ra ? Edit.Replace.New(ra.Position + o, ra.Element, ra.ReplaceElement).Cons(adjust(o, list.Tail)) : throw new NotSupportedException(); } // TODO: Consider building a monoid wrappers for all numeric values (and strings) internal readonly record struct MInt32(int Value) : Monoid, Ord { public MInt32 Combine(MInt32 y) => new (Value + y.Value); public static MInt32 Empty { get; } = new (0); public static readonly MInt32 Zero = new(0); public static readonly MInt32 One = new(1); public static int GetHashCode(MInt32 x) => x.Value.GetHashCode(); public static bool Equals(MInt32 x, MInt32 y) => x.Value == y.Value; public static int Compare(MInt32 x, MInt32 y) => x.Value.CompareTo(y.Value); } } ================================================ FILE: LanguageExt.Core/DataTypes/Patch/Patch.cs ================================================ using static LanguageExt.Prelude; using LanguageExt.ClassInstances; using LanguageExt.Traits; using System; using System.Collections.Generic; namespace LanguageExt; /// /// A `Patch` is a collection of edits performed to a _document_, in this case an 'IEnumerable〈A〉'. /// They are implemented as a list of 'Edit', and can be converted to and from raw lists of edits using /// `toList` and `fromList` respectively. /// /// Patches form a groupoid (a 'Monoid' with inverses, and a partial composition relation), where the /// inverse element can be computed with 'inverse' and the groupoid operation is a _composition_ of /// patches. Applying `append(p1, p2)` is the same as applying `p1` _then_ /// `p2` (see `Patch.apply`). This composition operator may produce structurally different patches depending /// on associativity, however the patches are guaranteed to be _equivalent_ in the sense that the resultant /// document will be the same when they are applied. /// /// For convenience, we make our composition operator here total, to fit the `Monoid` typeclass, but provide /// some predicates (`Patch.composable` and `Patch.applicable`) to determine if the operation can be validly /// used. /// public readonly struct Patch : Monoid>, IEquatable> where EqA : Eq { /// /// Empty patch /// public static Patch Empty { get; } = new(Seq>()); /// /// Edits that represent the operations to perform on a document /// to transform it. /// public readonly Seq> Edits; /// /// Ctor /// internal Patch(Seq> edits) => Edits = edits; /// /// Equality operator /// public bool Equals(Patch mb) => EqSeq, Edit>.Equals(Edits, mb.Edits); /// /// Equality operator /// public override bool Equals(object? obj) => obj is Patch p && Equals(p); /// /// Equality operator /// public static bool operator ==(Patch pa, Patch pb) => pa.Equals(pb); /// /// Non-equality operator /// public static bool operator !=(Patch pa, Patch pb) => !(pa == pb); /// /// Patch summing operator: monoid append /// public static Patch operator +(Patch pa, Patch pb) => pa.Combine(pb); /// /// Patch inverse operator /// public static Patch operator -(Patch pa) => Patch.inverse(pa); /// /// Hash code provider /// public override int GetHashCode() => Edits.GetHashCode(); /// /// Return a string representation of the patch /// public override string ToString() => $"[{String.Join("; ", Edits.Map(x => x.ToString()))}]"; /// /// Returns true if a patch can be safely applied to a document, that is, /// `applicable(p, d)` holds when `d` is a valid source document for the patch `p`. /// public bool Applicable(IEnumerable document) => Patch.applicable(this, document); /// /// Monoid append: produces a patch is a merged version of this /// and the provided patch. /// public Patch Combine(Patch mb) { var px = this; var py = mb; Seq> replace(int i, A o, A n, Seq> seq) => EqA.Equals(o, n) ? seq : Edit.Replace.New(i, o, n).Cons(seq); Seq> merge(Seq> ex, Seq> ey, int off) { if (ex.IsEmpty) { return ey.Map(e => e.Index(i => i + off)); } else if (ey.IsEmpty) { return ex; } else { var x = ex.Head.Value!; var xs = ex.Tail; var y = ey.Head.Value!; var ys = ey.Tail; var yi = y.Index(i => i + off); var ord = x.Position.CompareTo(yi.Position); if (ord < 0) { return x.Cons(merge(xs, ey, off + offset(x))); } else if (ord > 0) { return yi.Cons(merge(ex, ys, off)); } else { if (x is Edit.Delete del1 && yi is Edit.Insert ins1) { return replace(del1.Position, del1.Element, ins1.Element, merge(xs, ys, off + offset(x))); } else if (x is Edit.Delete) { return x.Cons(merge(xs, ey, off + offset(x))); } else if (yi is Edit.Insert) { return yi.Cons(merge(ex, ys, off)); } else if (x is Edit.Replace replA1 && yi is Edit.Replace replB1) { return replace(replA1.Position, replA1.Element, replB1.ReplaceElement, merge(xs, ys, off)); } else if (x is Edit.Replace replA2 && yi is Edit.Delete) { return Edit.Delete.New(replA2.Position, replA2.Element).Cons(merge(xs, ys, off)); } else if (x is Edit.Insert ins3 && yi is Edit.Replace replB2) { return Edit.Insert.New(ins3.Position, replB2.ReplaceElement) .Cons(merge(xs, ys, off + offset(x))); } else if (x is Edit.Insert && yi is Edit.Delete) { return merge(xs, ys, off + offset(x)); } else { throw new NotSupportedException(); } } } } int offset(Edit edit) => edit is Edit.Insert ? -1 : edit is Edit.Delete ? 1 : edit is Edit.Replace ? 0 : throw new NotSupportedException(); return new Patch(merge(px.Edits, py.Edits, 0)); } /// /// Compute the inverse of a patch /// public Patch Inverse() => Patch.inverse(this); /// /// Returns the delta of the document's size when a patch is applied. /// Essentially the number of `Insert` minus the number of `Delete` /// public int SizeChange() => Patch.sizeChange(this); /// /// Apply a patch to a document, returning the transformed document. /// public Iterable Apply(IEnumerable va) => Patch.apply(this, va); /// /// Apply a patch to a document, returning the transformed document. /// public Seq Apply(Seq va) => Patch.apply(this, va); /// /// Apply a patch to a document, returning the transformed document. /// public Lst Apply(Lst va) => Patch.apply(this, va); /// /// Apply a patch to a document, returning the transformed document. /// public SpanArray Apply(SpanArray va) => Patch.apply(this, va); /// /// Apply a patch to a document, returning the transformed document. /// public Arr Apply(Arr va) => Patch.apply(this, va); /// /// Apply a patch to a document, returning the transformed document. /// public A[] Apply(A[] va) => Patch.apply(this, va); /// /// Apply a patch to a document, returning the transformed document. /// public List Apply(List va) => Patch.apply(this, va); } ================================================ FILE: LanguageExt.Core/DataTypes/Patch/PatchParams.cs ================================================ using System; namespace LanguageExt { /// /// Parameters for injecting the default behaviour for /// building patches /// internal class PatchParams { public readonly Func equivalent; public readonly Func delete; public readonly Func insert; public readonly Func substitute; public readonly Func cost; public readonly Func positionOffset; public PatchParams( Func equivalent, Func delete, Func insert, Func substitute, Func cost, Func positionOffset ) { this.equivalent = equivalent; this.delete = delete; this.insert = insert; this.substitute = substitute; this.cost = cost; this.positionOffset = positionOffset; } } } ================================================ FILE: LanguageExt.Core/DataTypes/Pure/Pure.Extensions.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public static class PureExtensions { extension(Pure lhs) { /// /// Value extraction /// public static A operator >>(Pure px, Lower _) => px.Value; } extension(Pure lhs) { /// /// Function composition /// /// Input /// Function /// Result of invoking the function public static Pure operator >>(Pure x, Func f) => new (f(x.Value)); } /// /// Monadic join /// /// Nested `Pure` monad /// Bound value type /// Flattened monad public static Pure Flatten(this Pure> mma) => mma.Value; public static Validation Bind(this Pure ma, Func> bind) where F : Monoid => bind(ma.Value); public static Validation ToValidation(this Pure ma) where F : Monoid => Validation.Success(ma.Value); public static Validation SelectMany( this Pure ma, Func> bind, Func project) where F : Monoid => bind(ma.Value).Map(y => project(ma.Value, y)); } ================================================ FILE: LanguageExt.Core/DataTypes/Pure/Pure.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; /// /// Represents a pure value. Usually understood to be the 'success' value. /// /// /// On its own this doesn't do much, but allows other monads to convert /// from it and provide binding extensions that mean it can be lifted into /// other monads without specifying lots of extra generic arguments. /// /// Bound value /// Bound value type public readonly record struct Pure(A Value) { //////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Standard operators // /// /// Functor map /// /// Mapping function /// Result bound value type /// Result of the applying the mapping function to the `Pure` value public Pure Map(Func f) => new (f(Value)); /// /// Monadic bind /// /// Bind function /// Result bound value type /// Result of the applying the bind function to the `Pure` value public Pure Bind(Func> f) => f(Value); /// /// Monadic bind /// /// Bind function /// Result bound value type /// Result of the applying the bind function to the `Pure` value public Either Bind(Func> f) => f(Value); /// /// Monadic bind /// /// Bind function /// Result bound value type /// Result of the applying the bind function to the `Pure` value public IO Bind(Func> f) => ToIO().Bind(f); /// /// Monadic bind and project /// /// Bind function /// Project function /// Result of the bind operation bound value type /// Result of the mapping operation bound value type /// Result of the applying the bind and mapping function to the `Pure` value public Pure SelectMany(Func> bind, Func project) => new(project(Value, bind(Value).Value)); /// /// Monadic bind and project /// /// Bind function /// Project function /// Result of the bind operation bound value type /// Result of the mapping operation bound value type /// Result of the applying the bind and mapping function to the `Pure` value public Either SelectMany(Func> bind, Func project) => bind(Value).Value; /// /// Monadic bind and project /// /// Bind function /// Project function /// Result of the bind operation bound value type /// Result of the mapping operation bound value type /// Result of the applying the bind and mapping function to the `Pure` value public IO SelectMany(Func> bind, Func project) => ToIO().SelectMany(bind, project); //////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Conversion // public Option ToOption() => Value is null ? Option.None : Option.Some(Value); public These ToThese() => These.That(Value); public Either ToEither() => Either.Right(Value); public Fin ToFin() => Fin.Succ(Value); public Try ToTry() => Try.Succ(Value); public IO ToIO() => IO.pure(Value); public Eff ToEff() => Eff.Pure(Value); public Eff ToEff() => Eff.Pure(Value); //////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Monadic binding // public Either Bind(Func> bind) => bind(Value); public Fin Bind(Func> bind) => bind(Value); public Eff Bind(Func> bind) => bind(Value); public Eff Bind(Func> bind) => bind(Value); public K Bind(Func> bind) where M : Monad => bind(Value); public Reader Bind(Func> bind) => bind(Value); public ReaderT Bind(Func> bind) where M : Monad, Alternative => bind(Value); public State Bind(Func> bind) => bind(Value); public StateT Bind(Func> bind) where M : Monad, Alternative => bind(Value); public OptionT Bind(Func> bind) where M : Monad => bind(Value); public Option Bind(Func> bind) => bind(Value); //////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Monadic binding and projection // public Either SelectMany(Func> bind, Func project) => Bind(x => bind(x).Map(y => project(x, y))); public Fin SelectMany(Func> bind, Func project) => Bind(x => bind(x).Map(y => project(x, y))); public Eff SelectMany(Func> bind, Func project) => Bind(x => bind(x).Map(y => project(x, y))); public Eff SelectMany(Func> bind, Func project) => Bind(x => bind(x).Map(y => project(x, y))); public K SelectMany(Func> bind, Func project) where M : Monad => Bind(x => M.Map(y => project(x, y), bind(x))); public Reader SelectMany(Func> bind, Func project) => Bind(x => bind(x).Map(y => project(x, y))); public Reader SelectMany(Func, B>> bind, Func project) => Bind(x => bind(x).As().Map(y => project(x, y))); public ReaderT SelectMany(Func> bind, Func project) where M : Monad, Alternative => Bind(x => bind(x).Map(y => project(x, y))); public ReaderT SelectMany(Func, B>> bind, Func project) where M : Monad, Alternative => Bind(x => bind(x).As().Map(y => project(x, y))); public State SelectMany(Func> bind, Func project) => Bind(x => bind(x).Map(y => project(x, y))); public State SelectMany(Func, B>> bind, Func project) => Bind(x => bind(x).As().Map(y => project(x, y))); public StateT SelectMany(Func> bind, Func project) where M : Monad, Alternative => Bind(x => bind(x).Map(y => project(x, y))); public StateT SelectMany(Func, B>> bind, Func project) where M : Monad, Alternative => Bind(x => bind(x).As().Map(y => project(x, y))); public OptionT SelectMany(Func> bind, Func project) where M : Monad => Bind(x => bind(x).Map(y => project(x, y))); public OptionT SelectMany(Func, B>> bind, Func project) where M : Monad => Bind(x => bind(x).As().Map(y => project(x, y))); public Option SelectMany(Func> bind, Func project) => Bind(x => bind(x).Map(y => project(x, y))); } ================================================ FILE: LanguageExt.Core/DataTypes/README.md ================================================ These are all pure, immutable, data-types apart from `SpanArray`. `SpanArray` does almost exactly what the .NET `Span` does, however this came first, and so will remain supported. It's not advised to use it. ================================================ FILE: LanguageExt.Core/DataTypes/Range/Range.Extensions.cs ================================================ using System.Diagnostics.Contracts; using System.Numerics; using LanguageExt.Traits; namespace LanguageExt; public static class RangeExtensions { /// /// Convert kind to concrete type /// public static Range As(this K ma) => (Range)ma; /// /// Returns true if the value provided is in range /// /// Value to test /// True if the value provided is in range [Pure] public static bool InRange(this K ma, A value) where A : IComparisonOperators { var range = ma.As(); var from = range.From > range.To ? range.To : range.From; var to = range.From > range.To ? range.From : range.To; return value >= from && value <= to; } /// /// Returns true if the range provided overlaps this range /// /// The range to test /// True if the range provided overlaps this range [Pure] public static bool Overlaps(this K ma, Range other) where A : IComparisonOperators { var range = ma.As(); var xfrom = range.From > range.To ? range.To : range.From; var xto = range.From > range.To ? range.From : range.To; var yfrom = other.From > other.To ? other.To : other.From; var yto = other.From > other.To ? other.From : other.To; return xfrom < yto && yfrom < xto; } } ================================================ FILE: LanguageExt.Core/DataTypes/Range/Range.Module.cs ================================================ using System.Collections.Generic; using System.Diagnostics.Contracts; using System.Numerics; namespace LanguageExt; public partial class Range { /// /// Zero range /// public static Range zero() where A : IAdditiveIdentity => new(A.AdditiveIdentity, A.AdditiveIdentity, A.AdditiveIdentity, Iterable.Empty); /// /// Construct a new range /// /// The minimum value in the range /// The maximum value in the range [Pure] public static Range fromMinMax(A min, A max) where A : INumberBase, ISubtractionOperators, IComparisonOperators, IAdditionOperators, IEqualityOperators => fromMinMax(min, max, max >= min ? A.One : A.Zero - A.One); /// /// Construct a new range /// /// The minimum value in the range /// The maximum value in the range /// The size of each step in the range [Pure] public static Range fromMinMax(A min, A max, A step) where A : IAdditionOperators, IComparisonOperators { return min > max ? new(min, max, step, Backward()) : new(min, max, step, Forward()); IEnumerable Forward() { for (var x = min; x <= max; x += step) { yield return x; } } IEnumerable Backward() { for (var x = min; x >= max; x += step) { yield return x; } } } /// /// Construct a new range /// /// The minimum value in the range /// The number of items in the range [Pure] public static Range fromCount(A min, A count) where A : IComparisonOperators, INumberBase => fromCount(min, count, A.One); /// /// Construct a new range /// /// The minimum value in the range /// The number of items in the range /// The size of each step in the range [Pure] public static Range fromCount(A min, A count, A step) where A : INumberBase, IComparisonOperators { var max = min + (count * step - step); return new(min, max, step, Go()); IEnumerable Go() { var c = count; for (var x = min; c != A.Zero; x += step, c -= A.One) { yield return x; if (x == max) yield break; } } } } ================================================ FILE: LanguageExt.Core/DataTypes/Range/Range.Monad.cs ================================================ using System; using System.Linq; using LanguageExt.Traits; namespace LanguageExt; public partial class Range : Foldable { static S Foldable.FoldWhile( Func> f, Func<(S State, A Value), bool> predicate, S state, K ta) { foreach (var x in ta.As().runRange) { if (!predicate((state, x))) return state; state = f(x)(state); } return state; } static S Foldable.FoldBackWhile( Func> f, Func<(S State, A Value), bool> predicate, S state, K ta) { foreach (var x in ta.As().runRange.Reverse()) { if (!predicate((state, x))) return state; state = f(state)(x); } return state; } static Fold Foldable.FoldStep(K ta, S initialState) { // ReSharper disable once GenericEnumeratorNotDisposed var iter = ta.As().GetEnumerator(); return go(initialState); Fold go(S state) => iter.MoveNext() ? Fold.Loop(state, iter.Current, go) : Fold.Done(state); } static Fold Foldable.FoldStepBack(K ta, S initialState) { // ReSharper disable once GenericEnumeratorNotDisposed var iter = ta.As().GetEnumerator(); return go(initialState); Fold go(S state) => iter.MoveNext() ? Fold.Loop(state, iter.Current, go) : Fold.Done(state); } } ================================================ FILE: LanguageExt.Core/DataTypes/Range/Range.cs ================================================ using LanguageExt.Traits; using System; using System.Collections.Generic; using System.Collections; using System.Diagnostics.Contracts; namespace LanguageExt; /// /// Represents a range of values /// /// Bound values type [Serializable] public record Range(A From, A To, A Step, IEnumerable runRange) : IEnumerable, K { /// /// Reference version for use in pattern-matching /// [Pure] public object? Case => Prelude.Seq(this).Case; [Pure] public Seq ToSeq() => Prelude.toSeq(AsIterable()); [Pure] public Iterable AsIterable() => runRange.AsIterable(); /* [Pure] public StreamT AsStream() where M : Monad => StreamT.lift(runRange); */ [Pure] public IEnumerator GetEnumerator() => // ReSharper disable once NotDisposedResourceIsReturned runRange.GetEnumerator(); [Pure] IEnumerator IEnumerable.GetEnumerator() => // ReSharper disable once NotDisposedResourceIsReturned runRange.GetEnumerator(); } ================================================ FILE: LanguageExt.Core/DataTypes/Ratio/Ratio.cs ================================================  namespace LanguageExt; /// /// A ratio between two values. /// /// /// This is used in the definition of Fractional. /// public readonly struct Ratio { /// /// The numerator of the ratio, in non-reduced form. /// public readonly A Numerator; /// /// The denominator of the ratio, in non-reduced form. /// public readonly A Denominator; public Ratio(A num, A den) { Numerator = num; Denominator = den; } } ================================================ FILE: LanguageExt.Core/DataTypes/Record/Attributes.cs ================================================ using System; using System.Reflection; namespace LanguageExt; public class NonRecordAttribute : Attribute; public class NonStructuralAttribute : Attribute; public class NonHashAttribute : Attribute; public class NonEqAttribute : Attribute; public class NonOrdAttribute : Attribute; public class NonShowAttribute : Attribute; public class EqAttribute : Attribute { public EqAttribute(Type type) { if (!type.GetTypeInfo() .ImplementedInterfaces .AsIterable() .Exists(i => i.ToString().StartsWith("LanguageExt.Traits.Eq`1"))) { throw new Exception("Eq attribute should have a struct type that derives from LanguageExt.Traits.Eq<> passed as its argument"); } } } public class OrdAttribute : Attribute { public OrdAttribute(Type type) { if (!type.GetTypeInfo() .ImplementedInterfaces .AsIterable() .Exists(i => i.ToString().StartsWith("LanguageExt.Traits.Ord`1"))) { throw new Exception("Ord attribute should have a struct type that derives from LanguageExt.Traits.Ord<> passed as its argument"); } } } public class HashableAttribute : Attribute { public HashableAttribute(Type type) { if (!type.GetTypeInfo() .ImplementedInterfaces .AsIterable() .Exists(i => i.ToString().StartsWith("LanguageExt.Traits.Hashable`1"))) { throw new Exception("Hashable attribute should have a struct type that derives from LanguageExt.Traits.Hashable<> passed as its argument"); } } } /// /// Stops the base type fields being used for any Record operations /// /// This is *not* used for the [Record] code-gen public class IgnoreBaseAttribute : Attribute; ================================================ FILE: LanguageExt.Core/DataTypes/Record/Record.cs ================================================ using System; using System.Runtime.Serialization; namespace LanguageExt; /// /// Base class for types that are 'records'. A record has a set of readonly *fields( /// that make up its data structure. By deriving from this you get structural equality, /// structural ordering (`IComparable`), structural hashing (`GetHashCode`) as well as the /// operators `==`, `!=`, `〈`, `〈=`, `〉`, `〉=`, /// /// [Serializable] public abstract class Record : IEquatable, IComparable, IComparable where RECORDTYPE : Record { protected Record() { } protected Record(SerializationInfo info, StreamingContext context) => RecordType.SetObjectData((RECORDTYPE)this, info); public static bool operator==(Record x, Record y) => RecordType.EqualityTyped((RECORDTYPE)x, (RECORDTYPE)y); public static bool operator !=(Record x, Record y) => !(x == y); public static bool operator >(Record x, Record y) => RecordType.Compare((RECORDTYPE)x, (RECORDTYPE)y) > 0; public static bool operator >=(Record x, Record y) => RecordType.Compare((RECORDTYPE)x, (RECORDTYPE)y) >= 0; public static bool operator <(Record x, Record y) => RecordType.Compare((RECORDTYPE)x, (RECORDTYPE)y) < 0; public static bool operator <=(Record x, Record y) => RecordType.Compare((RECORDTYPE)x, (RECORDTYPE)y) <= 0; public override int GetHashCode() => RecordType.Hash((RECORDTYPE)this); public override bool Equals(object? obj) => obj is not null && RecordType.Equality((RECORDTYPE)this, obj); public virtual int CompareTo(RECORDTYPE? other) => other is null ? 1 : RecordType.Compare((RECORDTYPE)this, other); public virtual bool Equals(RECORDTYPE? other) => other is not null && RecordType.EqualityTyped((RECORDTYPE)this, other); public override string ToString() => RecordType.ToString((RECORDTYPE)this); public virtual void GetObjectData(SerializationInfo info, StreamingContext context) => RecordType.GetObjectData((RECORDTYPE)this, info); public int CompareTo(object? obj) => obj is RECORDTYPE rt ? CompareTo(rt) : 1; } ================================================ FILE: LanguageExt.Core/DataTypes/Record/RecordType.cs ================================================ using System; using System.Runtime.Serialization; namespace LanguageExt; /// /// Supports the building of record types (classes with sets of readonly field values) /// Provides structural equality testing, ordering, and hashing /// /// Type to provide methods for public static class RecordType { /// /// General hash code function for record types /// public static Func Hash => RecordTypeHash.Hash; /// /// General equality function for record types /// public static Func Equality => RecordTypeEquality.Equality; /// /// General typed equality function for record types /// public static Func EqualityTyped => RecordTypeEqualityTyped.EqualityTyped; /// /// General typed comparison function for record types /// public static Func Compare => RecordTypeCompare.Compare; /// /// General ToString function /// public new static Func ToString => RecordTypeToString.ToString; /// /// De-serialise an A /// public static Action SetObjectData => RecordTypeSetObjectData.SetObjectData; /// /// Serialise an A /// public static Action GetObjectData => RecordTypeGetObjectData.GetObjectData; [Obsolete("Don't use Equals - use either RecordType.Equality or RecordType.EqualityTyped")] public new static bool Equals(object objA, object objB) => throw new InvalidOperationException("Don't use Equals - use either RecordType.Equality or RecordType.EqualityTyped"); } internal static class RecordTypeIncludeBase { internal static readonly bool IncludeBase; static RecordTypeIncludeBase() => IncludeBase = !typeof(A).CustomAttributes .AsIterable() .Exists(a => a.AttributeType.Name == nameof(IgnoreBaseAttribute)); } internal static class RecordTypeHash { internal static readonly Func Hash; static RecordTypeHash() => Hash = IL.GetHashCode(RecordTypeIncludeBase.IncludeBase); } internal static class RecordTypeEquality { internal static readonly Func Equality; static RecordTypeEquality() => Equality = IL.Equals(RecordTypeIncludeBase.IncludeBase); } internal static class RecordTypeEqualityTyped { internal static readonly Func EqualityTyped; static RecordTypeEqualityTyped() => EqualityTyped = IL.EqualsTyped(RecordTypeIncludeBase.IncludeBase); } internal static class RecordTypeCompare { internal static readonly Func Compare; static RecordTypeCompare() => Compare = IL.Compare(RecordTypeIncludeBase.IncludeBase); } internal static class RecordTypeToString { internal new static readonly Func ToString; static RecordTypeToString() => ToString = IL.ToString(RecordTypeIncludeBase.IncludeBase); } internal static class RecordTypeSetObjectData { internal static readonly Action SetObjectData; static RecordTypeSetObjectData() => SetObjectData = IL.SetObjectData(RecordTypeIncludeBase.IncludeBase); } internal static class RecordTypeGetObjectData { internal static readonly Action GetObjectData; static RecordTypeGetObjectData() => GetObjectData = IL.GetObjectData(RecordTypeIncludeBase.IncludeBase); } ================================================ FILE: LanguageExt.Core/DataTypes/Record/RecordTypeIgnoreBase.cs ================================================ using System; using System.Runtime.Serialization; namespace LanguageExt; /// /// Supports the building of record types (classes with sets of readonly field values) /// Provides structural equality testing, ordering, and hashing /// /// Type to provide methods for public static class RecordTypeIgnoreBase { static RecordTypeIgnoreBase() { Hash = IL.GetHashCode(false); Equality = IL.Equals(false); EqualityTyped = IL.EqualsTyped(false); Compare = IL.Compare(false); ToString = IL.ToString(false); SetObjectData = IL.SetObjectData(false); GetObjectData = IL.GetObjectData(false); } /// /// General hash code function for record types /// public static readonly Func Hash; /// /// General equality function for record types /// public static readonly Func Equality; /// /// General typed equality function for record types /// public static readonly Func EqualityTyped; /// /// General typed comparison function for record types /// public static readonly Func Compare; /// /// General ToString function /// public new static readonly Func ToString; /// /// De-serialise an A /// public static readonly Action SetObjectData; /// /// Serialise an A /// public static readonly Action GetObjectData; [Obsolete("Don't use Equals - use either RecordType.Equality or RecordType.EqualityTyped")] public new static bool Equals(object objA, object objB) => throw new InvalidOperationException("Don't use Equals - use either RecordType.Equality or RecordType.EqualityTyped"); } ================================================ FILE: LanguageExt.Core/DataTypes/Record/TypeInfoExt.cs ================================================ using System; using System.Collections.Generic; using System.Reflection; namespace LanguageExt; public static class TypeInfoAllMemberExtensions { public static IEnumerable GetAllConstructors(this TypeInfo typeInfo, bool includeBase) => GetAll(typeInfo, ti => ti.DeclaredConstructors, includeBase); public static IEnumerable GetAllEvents(this TypeInfo typeInfo, bool includeBase) => GetAll(typeInfo, ti => ti.DeclaredEvents, includeBase); public static IEnumerable GetAllFields(this TypeInfo typeInfo, bool includeBase) => GetAll(typeInfo, ti => ti.DeclaredFields, includeBase); public static IEnumerable GetAllMembers(this TypeInfo typeInfo, bool includeBase) => GetAll(typeInfo, ti => ti.DeclaredMembers, includeBase); public static IEnumerable GetAllMethods(this TypeInfo typeInfo, bool includeBase) => GetAll(typeInfo, ti => ti.DeclaredMethods, includeBase); public static IEnumerable GetAllNestedTypes(this TypeInfo typeInfo, bool includeBase) => GetAll(typeInfo, ti => ti.DeclaredNestedTypes, includeBase); public static IEnumerable GetAllProperties(this TypeInfo typeInfo, bool includeBase) => GetAll(typeInfo, ti => ti.DeclaredProperties, includeBase); private static IEnumerable GetAll(TypeInfo typeInfo, Func> accessor, bool includeBase) { if (includeBase) { var ti = typeInfo; while (ti != null) { foreach (var t in accessor(ti)) { yield return t; } ti = ti.BaseType?.GetTypeInfo(); } } else { foreach (var t in accessor(typeInfo)) { yield return t; } } } } ================================================ FILE: LanguageExt.Core/DataTypes/SpanArray/SpanArray.cs ================================================ using static LanguageExt.Prelude; using System; using System.Linq; using System.Collections.Generic; using System.Collections; namespace LanguageExt; /// /// Represents a *mutable* array type that can be sliced to return /// new versions of the array which represent subsections of the /// original but without copying to a new buffer. This means the /// underlying array is shared and vulnerable to data race issues /// and the commonly known issues of mutable structures. /// /// It is primarily to facilitate super-fast algorithms that work /// on collections. The idea being that the algorithm knows about /// the issues of mutation and can avoid the problems whilst doing /// its thing, but also benefit from a more declarative style by /// using `Take`, `Skip`, `Slice`, `Head`, `Tail`, `Elem` etc. /// public readonly struct SpanArray : IEnumerable { public readonly A[] Data; public readonly int Count; readonly int Index; readonly int EndIndex; SpanArray(A[] data, int index, int count) { Data = data; Index = index; EndIndex = index + count; Count = count; } public Option Elem(int index) => index < 0 || index + Index >= EndIndex ? None : Some(Data[index + Index]); public IEnumerator GetEnumerator() { for (int i = Index; i < EndIndex; i++) { yield return Data[i]; } } public A this[int index] { get => index < 0 || index + Index >= EndIndex ? throw new IndexOutOfRangeException() : Data[index + Index]; set => Data[index + Index] = index < 0 || index + Index >= EndIndex ? throw new IndexOutOfRangeException() : value; } public static SpanArray New(IEnumerable sequence) { var data = sequence.ToArray(); return new SpanArray(data, 0, data.Length); } public static SpanArray New(A[] data) => new (data, 0, data.Length); public static SpanArray New(int n) => new (new A[n], 0, n); public SpanArray Slice(int index, int count) { if (count < 0) throw new ArgumentOutOfRangeException(nameof(count)); var newIndex = Index + index; if (newIndex > Index + Count) throw new ArgumentOutOfRangeException(nameof(index)); if (newIndex + count > Index + Count) throw new ArgumentOutOfRangeException(nameof(count)); return new SpanArray(Data, newIndex, count); } public SpanArray Take(int n) => Slice(0, n); public SpanArray Skip(int n) => Slice(n, Count - n); public SpanArray Tail => Slice(1, Count - 1); public A Head => this[0]; public A Last => this[Count - 1]; IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); public Unit UnsafeCopy(SpanArray dest) { System.Array.Copy(Data, Index, dest.Data, dest.Index, Count); return unit; } } ================================================ FILE: LanguageExt.Core/DataTypes/StringM/OrdString.cs ================================================ // TODO: Work out where these types should live using System; using LanguageExt.Traits; using LanguageExt.Traits.Domain; namespace LanguageExt; public interface HashableString : Hashable where STRING : HashableString, DomainType { public static abstract StringComparison Comparison { get; } static int Hashable.GetHashCode(STRING x) => x.To().GetHashCode(STRING.Comparison); } public interface EqString : HashableString, Eq where STRING : EqString, DomainType { static bool Eq.Equals(STRING lhs, STRING rhs) => string.Equals(lhs.To(), rhs.To(), STRING.Comparison); } public interface OrdString : EqString, Ord where STRING : OrdString, DomainType { static int Ord.Compare(STRING lhs, STRING rhs) => string.Compare(lhs.To(), rhs.To(), STRING.Comparison); } public interface OrdinalIgnoreCase : OrdString where STRING : OrdinalIgnoreCase, DomainType { static StringComparison HashableString.Comparison => StringComparison.OrdinalIgnoreCase; } public interface Ordinal : OrdString where STRING : Ordinal, DomainType { static StringComparison HashableString.Comparison => StringComparison.Ordinal; } public interface CultureIgnoreCase : OrdString where STRING : CultureIgnoreCase, DomainType { static StringComparison HashableString.Comparison => StringComparison.CurrentCultureIgnoreCase; } public interface Culture : OrdString where STRING : Culture, DomainType { static StringComparison HashableString.Comparison => StringComparison.CurrentCulture; } public interface InvariantIgnoreCase : OrdString where STRING : InvariantIgnoreCase, DomainType { static StringComparison HashableString.Comparison => StringComparison.InvariantCultureIgnoreCase; } public interface InvariantCulture : OrdString where STRING : InvariantCulture, DomainType { static StringComparison HashableString.Comparison => StringComparison.InvariantCulture; } ================================================ FILE: LanguageExt.Core/DataTypes/StringM/StringM.Ord.cs ================================================ using System; using LanguageExt.Traits.Domain; namespace LanguageExt; public readonly record struct StringOrdinalM(string Value) : StringM { static Fin DomainType.From(string repr) => new StringOrdinalM(repr); public static StringOrdinalM From(string repr) => new (repr); public string To() => Value; public static StringComparison Comparison => StringComparison.Ordinal; public static implicit operator string(StringOrdinalM value) => value.Value; public static implicit operator StringOrdinalM(string value) => new(value); } public readonly record struct StringOrdinalIgnoreCaseM(string Value) : StringM { static Fin DomainType.From(string repr) => new StringOrdinalIgnoreCaseM(repr); public static StringInvariantM From(string repr) => new (repr); public string To() => Value; public static StringComparison Comparison => StringComparison.OrdinalIgnoreCase; public static implicit operator string(StringOrdinalIgnoreCaseM value) => value.Value; public static implicit operator StringOrdinalIgnoreCaseM(string value) => new(value); } public readonly record struct StringCultureM(string Value) : StringM { static Fin DomainType.From(string repr) => new StringCultureM(repr); public static StringCultureM From(string repr) => new (repr); public string To() => Value; public static StringComparison Comparison => StringComparison.CurrentCulture; public static implicit operator string(StringCultureM value) => value.Value; public static implicit operator StringCultureM(string value) => new(value); } public readonly record struct StringCultureIgnoreCaseM(string Value) : StringM { static Fin DomainType.From(string repr) => new StringCultureIgnoreCaseM(repr); public static StringCultureIgnoreCaseM From(string repr) => new (repr); public string To() => Value; public static StringComparison Comparison => StringComparison.CurrentCultureIgnoreCase; public static implicit operator string(StringCultureIgnoreCaseM value) => value.Value; public static implicit operator StringCultureIgnoreCaseM(string value) => new(value); } public readonly record struct StringInvariantM(string Value) : StringM { public static Fin From(string repr) => new StringInvariantM(repr); public static StringInvariantM FromUnsafe(string repr) => new (repr); public string To() => Value; public static StringComparison Comparison => StringComparison.InvariantCulture; public static implicit operator string(StringInvariantM value) => value.Value; public static implicit operator StringInvariantM(string value) => new(value); } public readonly record struct StringInvariantIgnoreCaseM(string Value) : StringM { static Fin DomainType.From(string repr) => new StringInvariantIgnoreCaseM(repr); public static StringInvariantIgnoreCaseM From(string repr) => new (repr); public string To() => Value; public static StringComparison Comparison => StringComparison.InvariantCultureIgnoreCase; public static implicit operator string(StringInvariantIgnoreCaseM value) => value.Value; public static implicit operator StringInvariantIgnoreCaseM(string value) => new(value); } ================================================ FILE: LanguageExt.Core/DataTypes/StringM/StringM.Trait.cs ================================================ using System; using LanguageExt.Traits; using LanguageExt.Traits.Domain; namespace LanguageExt; /// /// Simple wrapper around String /// /// Contained value public interface StringM : DomainType, Identifier, IComparable, ISpanParsable, OrdString, Monoid where SELF : StringM, Identifier, IComparable, ISpanParsable, OrdString, Monoid { /// /// Monoid append /// SELF Semigroup.Combine(SELF rhs) => SELF.FromUnsafe(To() + rhs.To()); /// /// Monoid empty /// static SELF Monoid.Empty => SELF.FromUnsafe(string.Empty); /// /// Length of the string /// public int Length => To().Length; /// /// ToString override /// public string ToString() => To(); public int GetHashCode() => Hashable.code((SELF)this); bool IEquatable.Equals(SELF? rhs) => rhs is not null && SELF.Equals((SELF)this, rhs); int IComparable.CompareTo(SELF? rhs) => rhs is null ? 1 : SELF.Compare((SELF)this, rhs); static SELF IParsable.Parse(string s, IFormatProvider? provider) => SELF.FromUnsafe(s); static bool IParsable.TryParse(string? s, IFormatProvider? provider, out SELF result) { result = SELF.FromUnsafe(s ?? ""); return s != null; } static SELF ISpanParsable.Parse(ReadOnlySpan s, IFormatProvider? provider) { // magic number from System.String if (s.Length > 1073741791) throw new ArgumentException(nameof(s)); return SELF.FromUnsafe(s.ToString()); } static bool ISpanParsable.TryParse(ReadOnlySpan s, IFormatProvider? provider, out SELF result) { if (s.Length <= 1073741791) // magic number from System.String { result = SELF.FromUnsafe(s.ToString()); return true; } result = default!; return false; } } ================================================ FILE: LanguageExt.Core/DataTypes/StringM/StringM.cs ================================================ using System; using LanguageExt.Traits.Domain; namespace LanguageExt; /// /// Simple wrapper around String /// /// Contained value public readonly record struct StringM(string Value) : StringM { static Fin DomainType.From(string repr) => new StringM(repr); public static StringM From(string repr) => new (repr); public static StringM FromUnsafe(string repr) => new (repr); public string To() => Value; public static StringComparison Comparison => StringComparison.Ordinal; public static implicit operator string(StringM value) => value.Value; public static implicit operator StringM(string value) => new(value); } ================================================ FILE: LanguageExt.Core/DataTypes/Unit/Unit.Prelude.cs ================================================ using System.Runtime.CompilerServices; namespace LanguageExt; public static partial class Prelude { /// /// Unit constructor /// public static Unit unit => Unit.Default; /// /// Takes any value, ignores it, returns a unit /// /// Value to ignore /// Unit [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Unit ignore(A anything) => default; /// /// Takes any value, ignores it, returns a unit /// /// Value to ignore /// Unit [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Unit Ignore(this A anything) => default; } ================================================ FILE: LanguageExt.Core/DataTypes/Unit/Unit.cs ================================================ using System; using System.Diagnostics.Contracts; using LanguageExt.Traits; namespace LanguageExt; /// /// A unit type is a type that allows only one value (and thus can hold no information) /// [Serializable] public readonly struct Unit : IEquatable, IComparable, Monoid { public static readonly Unit Default = default; [Pure] public override int GetHashCode() => 0; [Pure] public override bool Equals(object? obj) => obj is Unit; [Pure] public override string ToString() => "()"; [Pure] public bool Equals(Unit other) => true; [Pure] public static bool operator ==(Unit lhs, Unit rhs) => true; [Pure] public static bool operator !=(Unit lhs, Unit rhs) => false; [Pure] public static bool operator >(Unit lhs, Unit rhs) => false; [Pure] public static bool operator >=(Unit lhs, Unit rhs) => true; [Pure] public static bool operator <(Unit lhs, Unit rhs) => false; [Pure] public static bool operator <=(Unit lhs, Unit rhs) => true; /// /// Provide an alternative value to unit /// /// Alternative value type /// Alternative value /// Alternative value [Pure] public T Return(T anything) => anything; /// /// Provide an alternative value to unit /// /// Alternative value type /// Alternative value /// Alternative value [Pure] public T Return(Func anything) => anything(); /// /// Always equal /// [Pure] public int CompareTo(Unit other) => 0; [Pure] public Unit Combine(Unit rhs) => default; [Pure] public static Unit operator +(Unit a, Unit b) => Default; [Pure] public static implicit operator ValueTuple(Unit _) => default; [Pure] public static implicit operator Unit(ValueTuple _) => default; [Pure] public static implicit operator Unit(Seq _) => default; [Pure] public static implicit operator Unit(Lst _) => default; [Pure] public static implicit operator Unit(Iterable _) => default; [Pure] public static Unit Empty => default; } ================================================ FILE: LanguageExt.Core/DataTypes/ValueTuple/Tuple1/ValueTuple1.Extensions.cs ================================================ using System; using LanguageExt; using System.Diagnostics.Contracts; using LanguageExt.Traits; public static class ValueTuple1Extensions { /// /// Append an extra item to the tuple /// [Pure] public static (A, B) Add(this ValueTuple self, B second) => (self.Item1, second); /// /// One of the items matches the value passed /// [Pure] public static bool Contains(this ValueTuple self, A value) where EQ : Eq => EQ.Equals(self.Item1, value); /// /// Map to R /// [Pure] public static ValueTuple Map(this ValueTuple self, Func map) => new(map(self.Item1)); /// /// Map to tuple /// [Pure] public static ValueTuple Select(this ValueTuple self, Func map) => new(map(self.Item1)); /// /// Iterate /// public static Unit Iter(this ValueTuple self, Action func) { func(self.Item1); return Unit.Default; } /// /// Fold /// [Pure] public static S Fold(this ValueTuple self, S state, Func fold) => fold(state, self.Item1); } ================================================ FILE: LanguageExt.Core/DataTypes/ValueTuple/Tuple1/ValueTuple1.Prelude.cs ================================================ using System; using System.Diagnostics.Contracts; using LanguageExt.Traits; namespace LanguageExt; public static partial class Prelude { /// /// Append an extra item to the tuple /// [Pure] public static (A, B) add(ValueTuple self, B second) => (self.Item1, second); /// /// One of the items matches the value passed /// [Pure] public static bool contains(ValueTuple self, A value) where EQ : Eq => EQ.Equals(self.Item1, value); /// /// Map to R /// [Pure] public static ValueTuple map(ValueTuple self, Func map) => new(map(self.Item1)); /// /// Iterate /// public static Unit iter(ValueTuple self, Action func) { func(self.Item1); return Unit.Default; } } ================================================ FILE: LanguageExt.Core/DataTypes/ValueTuple/Tuple2/ValueTuple2.Extensions.cs ================================================ using System; using LanguageExt; using static LanguageExt.Prelude; using System.Diagnostics.Contracts; using System.Threading.Tasks; public static class ValueTuple2Extensions { /// /// Append an extra item to the tuple /// [Pure] public static (A, B, C) Add(this (A, B) self, C third) => (self.Item1, self.Item2, third); /// /// Take the first item /// [Pure] public static T1 Head(this ValueTuple self) => self.Item1; /// /// Take the last item /// [Pure] public static T2 Last(this ValueTuple self) => self.Item2; /// /// Take the second item onwards and build a new tuple /// [Pure] public static ValueTuple Tail(this ValueTuple self) => new (self.Item2); /// /// Map /// [Pure] public static R Map(this ValueTuple self, Func, R> map) => map(self); /// /// Map /// [Pure] public static R Map(this ValueTuple self, Func map) => map(self.Item1, self.Item2); /// /// Bi-map to tuple /// [Pure] public static ValueTuple BiMap(this ValueTuple self, Func firstMap, Func secondMap) => (firstMap(self.Item1), secondMap(self.Item2)); /// /// First item-map to tuple /// [Pure] public static ValueTuple MapFirst(this ValueTuple self, Func firstMap) => (firstMap(self.Item1), self.Item2); /// /// Second item-map to tuple /// [Pure] public static ValueTuple MapSecond(this ValueTuple self, Func secondMap) => (self.Item1, secondMap(self.Item2)); /// /// Map to tuple /// [Pure] public static ValueTuple Select(this ValueTuple self, Func, ValueTuple> map) => map(self); /// /// Iterate /// public static Unit Iter(this ValueTuple self, Action func) { func(self.Item1, self.Item2); return Unit.Default; } /// /// Iterate /// public static Unit Iter(this ValueTuple self, Action first, Action second) { first(self.Item1); second(self.Item2); return Unit.Default; } /// /// Fold /// [Pure] public static S Fold(this ValueTuple self, S state, Func fold) => fold(state, self.Item1, self.Item2); /// /// Bi-fold /// [Pure] public static S BiFold(this ValueTuple self, S state, Func firstFold, Func secondFold) => secondFold(firstFold(state, self.Item1), self.Item2); /// /// Bi-fold /// [Pure] public static S BiFoldBack(this ValueTuple self, S state, Func firstFold, Func secondFold) => secondFold(firstFold(state, self.Item2), self.Item1); /// /// Flip the tuple monads from inside the tuple to outside and apply a transformation function /// [Pure] public static Arr<(C, D)> Traverse(this (Arr ma, Arr mb) tuple, Func<(A a, B b), (C c, D d)> f) => from a in tuple.ma from b in tuple.mb let r = f((a, b)) select (r.Item1, r.Item2); /// /// Flip the tuple monads from inside the tuple to outside and apply a transformation function /// [Pure] public static Arr<(C, D)> Traverse(this (Arr ma, Arr mb) tuple, Func f) => from a in tuple.ma from b in tuple.mb let r = f(a, b) select (r.Item1, r.Item2); /// /// Flip the tuple monads from inside the tuple to outside and apply a transformation function /// [Pure] public static Arr<(A, B)> Sequence(this (Arr ma, Arr mb) tuple) => from a in tuple.ma from b in tuple.mb select (a, b); /// /// Flip the tuple monads from inside the tuple to outside and apply a transformation function /// [Pure] public static Either Traverse(this (Either ma, Either mb) tuple, Func<(A a, B b), (C c, D d)> f) => from a in tuple.ma from b in tuple.mb let r = f((a, b)) select (r.Item1, r.Item2); /// /// Flip the tuple monads from inside the tuple to outside and apply a transformation function /// [Pure] public static Either Traverse(this (Either ma, Either mb) tuple, Func f) => from a in tuple.ma from b in tuple.mb let r = f(a, b) select (r.Item1, r.Item2); /// /// Flip the tuple monads from inside the tuple to outside and apply a transformation function /// [Pure] public static Either Sequence(this (Either ma, Either mb) tuple) => from a in tuple.ma from b in tuple.mb select (a, b); /// /// Flip the tuple monads from inside the tuple to outside and apply a transformation function /// [Pure] public static HashSet<(C, D)> Traverse(this (HashSet ma, HashSet mb) tuple, Func<(A a, B b), (C c, D d)> f) => from a in tuple.ma from b in tuple.mb let r = f((a, b)) select (r.Item1, r.Item2); /// /// Flip the tuple monads from inside the tuple to outside and apply a transformation function /// [Pure] public static HashSet<(C, D)> Traverse(this (HashSet ma, HashSet mb) tuple, Func f) => from a in tuple.ma from b in tuple.mb let r = f(a, b) select (r.Item1, r.Item2); /// /// Flip the tuple monads from inside the tuple to outside and apply a transformation function /// [Pure] public static HashSet<(A, B)> Sequence(this (HashSet ma, HashSet mb) tuple) => from a in tuple.ma from b in tuple.mb select (a, b); /// /// Flip the tuple monads from inside the tuple to outside and apply a transformation function /// [Pure] public static Lst<(C, D)> Traverse(this (Lst ma, Lst mb) tuple, Func<(A a, B b), (C c, D d)> f) => from a in tuple.ma from b in tuple.mb let r = f((a, b)) select (r.Item1, r.Item2); /// /// Flip the tuple monads from inside the tuple to outside and apply a transformation function /// [Pure] public static Lst<(C, D)> Traverse(this (Lst ma, Lst mb) tuple, Func f) => from a in tuple.ma from b in tuple.mb let r = f(a, b) select (r.Item1, r.Item2); /// /// Flip the tuple monads from inside the tuple to outside and apply a transformation function /// [Pure] public static Lst<(A, B)> Sequence(this (Lst ma, Lst mb) tuple) => from a in tuple.ma from b in tuple.mb select (a, b); /// /// Flip the tuple monads from inside the tuple to outside and apply a transformation function /// [Pure] public static Option<(C, D)> Traverse(this (Option ma, Option mb) tuple, Func<(A a, B b), (C c, D d)> f) => from a in tuple.ma from b in tuple.mb let r = f((a, b)) select (r.Item1, r.Item2); /// /// Flip the tuple monads from inside the tuple to outside and apply a transformation function /// [Pure] public static Option<(C, D)> Traverse(this (Option ma, Option mb) tuple, Func f) => from a in tuple.ma from b in tuple.mb let r = f(a, b) select (r.Item1, r.Item2); /// /// Flip the tuple monads from inside the tuple to outside and apply a transformation function /// [Pure] public static Option<(A, B)> Sequence(this (Option ma, Option mb) tuple) => from a in tuple.ma from b in tuple.mb select (a, b); /// /// Flip the tuple monads from inside the tuple to outside and apply a transformation function /// [Pure] public static Set<(C, D)> Traverse(this (Set ma, Set mb) tuple, Func<(A a, B b), (C c, D d)> f) => from a in tuple.ma from b in tuple.mb let r = f((a, b)) select (r.Item1, r.Item2); /// /// Flip the tuple monads from inside the tuple to outside and apply a transformation function /// [Pure] public static Set<(C, D)> Traverse(this (Set ma, Set mb) tuple, Func f) => from a in tuple.ma from b in tuple.mb let r = f(a, b) select (r.Item1, r.Item2); /// /// Flip the tuple monads from inside the tuple to outside and apply a transformation function /// [Pure] public static Set<(A, B)> Sequence(this (Set ma, Set mb) tuple) => from a in tuple.ma from b in tuple.mb select (a, b); /// /// Flip the tuple monads from inside the tuple to outside and apply a transformation function /// [Pure] public static Task<(C, D)> Traverse(this (Task ma, Task mb) tuple, Func<(A a, B b), (C c, D d)> f) => apply((a, b) => f((a, b)), tuple.ma, tuple.mb); /// /// Flip the tuple monads from inside the tuple to outside and apply a transformation function /// [Pure] public static Task<(C, D)> Traverse(this (Task ma, Task mb) tuple, Func f) => apply((a, b) => f(a, b), tuple.ma, tuple.mb); /// /// Flip the tuads from inside the tuple to outside and apply a transformation function /// [Pure] public static Task<(A, B)> Sequence(this (Task ma, Task mb) tuple) => apply((a, b) => (a, b), tuple.ma, tuple.mb); } ================================================ FILE: LanguageExt.Core/DataTypes/ValueTuple/Tuple2/ValueTuple2.Prelude.cs ================================================ using System; using System.Diagnostics.Contracts; using LanguageExt.Traits; namespace LanguageExt; public static partial class Prelude { /// /// Append an extra item to the tuple /// [Pure] public static ValueTuple ad(ValueTuple self, T3 third) => self.Add(third); /// /// Take the first item /// [Pure] public static T1 head(ValueTuple self) => self.Item1; /// /// Take the last item /// [Pure] public static T2 last(ValueTuple self) => self.Item2; /// /// Take the second item onwards and build a new tuple /// [Pure] public static ValueTuple tail(ValueTuple self) => new(self.Item2); /// /// Sum of the items /// [Pure] public static A sum(this ValueTuple self) where NUM : Num => NUM.Add(self.Item1, self.Item2); /// /// Product of the items /// [Pure] public static A product(this ValueTuple self) where NUM : Num => NUM.Multiply(self.Item1, self.Item2); /// /// One of the items matches the value passed /// [Pure] public static bool contains(this ValueTuple self, A value) where EQ : Eq => EQ.Equals(self.Item1, value) || EQ.Equals(self.Item2, value); /// /// Map /// [Pure] public static R map(ValueTuple self, Func, R> map) => map(self); /// /// Map /// [Pure] public static R map(ValueTuple self, Func map) => map(self.Item1, self.Item2); /// /// Bi-map to tuple /// [Pure] public static ValueTuple bimap(ValueTuple self, Func firstMap, Func secondMap) => self.BiMap(firstMap, secondMap); /// /// Iterate /// public static Unit iter(ValueTuple self, Action first, Action second) => self.Iter(first, second); /// /// Iterate /// public static Unit iter(ValueTuple self, Action func) { func(self.Item1, self.Item2); return Unit.Default; } /// /// Fold /// [Pure] public static S fold(ValueTuple self, S state, Func fold) => self.Fold(state, fold); /// /// Bi-fold /// [Pure] public static S bifold(ValueTuple self, S state, Func firstFold, Func secondFold) => self.BiFold(state, firstFold, secondFold); /// /// Bi-fold back /// [Pure] public static S bifoldBack(ValueTuple self, S state, Func firstFold, Func secondFold) => self.BiFoldBack(state, firstFold, secondFold); } ================================================ FILE: LanguageExt.Core/DataTypes/ValueTuple/Tuple3/ValueTuple3.Extensions.cs ================================================ using System; using LanguageExt; using System.Diagnostics.Contracts; using LanguageExt.Traits; public static class ValueTuple3Extensions { /// /// Append an extra item to the tuple /// [Pure] public static ValueTuple Add(this ValueTuple self, T4 fourth) => (self.Item1, self.Item2, self.Item3, fourth); /// /// Take the first item /// [Pure] public static T1 Head(this ValueTuple self) => self.Item1; /// /// Take the last item /// [Pure] public static T3 Last(this ValueTuple self) => self.Item3; /// /// Take the second item onwards and build a new tuple /// [Pure] public static ValueTuple Tail(this ValueTuple self) => (self.Item2, self.Item3); /// /// Sum of the items /// [Pure] public static A Sum(this ValueTuple self) where NUM : Num => NUM.Add(self.Item1, NUM.Add(self.Item2, self.Item3)); /// /// Product of the items /// [Pure] public static A Product(this ValueTuple self) where NUM : Num => NUM.Multiply(self.Item1, NUM.Multiply(self.Item2, self.Item3)); /// /// One of the items matches the value passed /// [Pure] public static bool Contains(this ValueTuple self, A value) where EQ : Eq => EQ.Equals(self.Item1, value) || EQ.Equals(self.Item2, value) || EQ.Equals(self.Item3, value); /// /// Map /// [Pure] public static R Map(this ValueTuple self, Func, R> map) => map(self); /// /// Map /// [Pure] public static R Map(this ValueTuple self, Func map) => map(self.Item1, self.Item2, self.Item3); /// /// Tri-map to tuple /// [Pure] public static ValueTuple Map(this ValueTuple self, Func firstMap, Func secondMap, Func thirdMap) => (firstMap(self.Item1), secondMap(self.Item2), thirdMap(self.Item3)); /// /// First item-map to tuple /// [Pure] public static ValueTuple MapFirst(this ValueTuple self, Func firstMap) => (firstMap(self.Item1), self.Item2, self.Item3); /// /// Second item-map to tuple /// [Pure] public static ValueTuple MapSecond(this ValueTuple self, Func secondMap) => (self.Item1, secondMap(self.Item2), self.Item3); /// /// Third item-map to tuple /// [Pure] public static ValueTuple MapThird(this ValueTuple self, Func thirdMap) => (self.Item1, self.Item2, thirdMap(self.Item3)); /// /// Map to tuple /// [Pure] public static ValueTuple Select(this ValueTuple self, Func, ValueTuple> map) => map(self); /// /// Iterate /// public static Unit Iter(this ValueTuple self, Action func) { func(self.Item1, self.Item2, self.Item3); return Unit.Default; } /// /// Iterate /// public static Unit Iter(this ValueTuple self, Action first, Action second, Action third) { first(self.Item1); second(self.Item2); third(self.Item3); return Unit.Default; } /// /// Fold /// [Pure] public static S Fold(this ValueTuple self, S state, Func fold) => fold(state, self.Item1, self.Item2, self.Item3); /// /// Tri-fold /// [Pure] public static S TriFold(this ValueTuple self, S state, Func firstFold, Func secondFold, Func thirdFold) => thirdFold(secondFold(firstFold(state, self.Item1), self.Item2), self.Item3); /// /// Tri-fold /// [Pure] public static S TriFoldBack(this ValueTuple self, S state, Func firstFold, Func secondFold, Func thirdFold) => thirdFold(secondFold(firstFold(state, self.Item3), self.Item2), self.Item1); } ================================================ FILE: LanguageExt.Core/DataTypes/ValueTuple/Tuple3/ValueTuple3.Prelude.cs ================================================ using System; using System.Diagnostics.Contracts; using LanguageExt.Traits; namespace LanguageExt; public static partial class Prelude { /// /// Append an extra item to the tuple /// [Pure] public static ValueTuple add(ValueTuple self, T4 fourth) => (self.Item1, self.Item2, self.Item3, fourth); /// /// Take the first item /// [Pure] public static T1 head(ValueTuple self) => self.Item1; /// /// Take the last item /// [Pure] public static T3 last(ValueTuple self) => self.Item3; /// /// Take the second item onwards and build a new tuple /// [Pure] public static ValueTuple tail(ValueTuple self) => (self.Item2, self.Item3); /// /// Sum of the items /// [Pure] public static A sum(ValueTuple self) where NUM : Num => NUM.Add(self.Item1, NUM.Add(self.Item2, self.Item3)); /// /// Product of the items /// [Pure] public static A product(ValueTuple self) where NUM : Num => NUM.Multiply(self.Item1, NUM.Multiply(self.Item2, self.Item3)); /// /// One of the items matches the value passed /// [Pure] public static bool contains(ValueTuple self, A value) where EQ : Eq => EQ.Equals(self.Item1, value) || EQ.Equals(self.Item2, value) || EQ.Equals(self.Item3, value); /// /// Map /// [Pure] public static R map(ValueTuple self, Func, R> map) => map(self); /// /// Map /// [Pure] public static R map(ValueTuple self, Func map) => map(self.Item1, self.Item2, self.Item3); /// /// Tri-map to tuple /// [Pure] public static ValueTuple trimap(ValueTuple self, Func firstMap, Func secondMap, Func thirdMap) => ValueTuple.Create(firstMap(self.Item1), secondMap(self.Item2), thirdMap(self.Item3)); /// /// First item-map to tuple /// [Pure] public static ValueTuple mapFirst(ValueTuple self, Func firstMap) => ValueTuple.Create(firstMap(self.Item1), self.Item2, self.Item3); /// /// Second item-map to tuple /// [Pure] public static ValueTuple mapSecond(ValueTuple self, Func secondMap) => ValueTuple.Create(self.Item1, secondMap(self.Item2), self.Item3); /// /// Second item-map to tuple /// [Pure] public static ValueTuple mapThird(ValueTuple self, Func thirdMap) => ValueTuple.Create(self.Item1, self.Item2, thirdMap(self.Item3)); /// /// Iterate /// public static Unit iter(ValueTuple self, Action func) { func(self.Item1, self.Item2, self.Item3); return Unit.Default; } /// /// Iterate /// public static Unit iter(ValueTuple self, Action first, Action second, Action third) { first(self.Item1); second(self.Item2); third(self.Item3); return Unit.Default; } /// /// Fold /// [Pure] public static S fold(ValueTuple self, S state, Func fold) => fold(state, self.Item1, self.Item2, self.Item3); /// /// Tri-fold /// [Pure] public static S trifold(ValueTuple self, S state, Func firstFold, Func secondFold, Func thirdFold) => thirdFold(secondFold(firstFold(state, self.Item1), self.Item2), self.Item3); /// /// Tri-fold /// [Pure] public static S trifoldBack(ValueTuple self, S state, Func firstFold, Func secondFold, Func thirdFold) => thirdFold(secondFold(firstFold(state, self.Item3), self.Item2), self.Item1); } ================================================ FILE: LanguageExt.Core/DataTypes/ValueTuple/Tuple4/ValueTuple4.Extensions.cs ================================================ using System; using LanguageExt; using System.Diagnostics.Contracts; using LanguageExt.Traits; public static class ValueTuple4Extensions { /// /// Append an extra item to the tuple /// [Pure] public static (A, B, C, D, E) Add(this (A, B, C, D) self, E fifth) => (self.Item1, self.Item2, self.Item3, self.Item4, fifth); /// /// Take the first item /// [Pure] public static A Head(this (A, B, C, D) self) => self.Item1; /// /// Take the last item /// [Pure] public static D Last(this (A, B, C, D) self) => self.Item4; /// /// Take the second item onwards and build a new tuple /// [Pure] public static (B, C, D) Tail(this(A, B, C, D) self) => (self.Item2, self.Item3, self.Item4); /// /// One of the items matches the value passed /// [Pure] public static bool Contains(this (A, A, A, A) self, A value) where EQ : Eq => EQ.Equals(self.Item1, value) || EQ.Equals(self.Item2, value) || EQ.Equals(self.Item3, value) || EQ.Equals(self.Item4, value); /// /// Map /// [Pure] public static R Map(this ValueTuple self, Func, R> map) => map(self); /// /// Map /// [Pure] public static R Map(this ValueTuple self, Func map) => map(self.Item1, self.Item2, self.Item3, self.Item4); /// /// Tri-map to tuple /// [Pure] public static (W, X, Y, Z) Map(this(A, B, C, D) self, Func firstMap, Func secondMap, Func thirdMap, Func fourthMap) => (firstMap(self.Item1), secondMap(self.Item2), thirdMap(self.Item3), fourthMap(self.Item4)); /// /// First item-map to tuple /// [Pure] public static (R1, B, C, D) MapFirst(this (A, B, C, D) self, Func firstMap) => (firstMap(self.Item1), self.Item2, self.Item3, self.Item4); /// /// Second item-map to tuple /// [Pure] public static (A, R2, C, D) MapSecond(this (A, B, C, D) self, Func secondMap) => (self.Item1, secondMap(self.Item2), self.Item3, self.Item4); /// /// Third item-map to tuple /// [Pure] public static (A, B, R3, D) MapThird(this (A, B, C, D) self, Func thirdMap) => (self.Item1, self.Item2, thirdMap(self.Item3), self.Item4); /// /// Fourth item-map to tuple /// [Pure] public static (A, B, C, R4) MapFourth(this(A, B, C, D) self, Func fourthMap) => (self.Item1, self.Item2, self.Item3, fourthMap(self.Item4)); /// /// Map to tuple /// [Pure] public static (W, X, Y, Z) Select(this (A, B, C, D) self, Func<(A, B, C, D), (W, X, Y, Z)> map) => map(self); /// /// Iterate /// public static Unit Iter(this (A, B, C, D) self, Action func) { func(self.Item1, self.Item2, self.Item3, self.Item4); return Unit.Default; } /// /// Iterate /// public static Unit Iter(this (A, B, C, D) self, Action first, Action second, Action third, Action fourth) { first(self.Item1); second(self.Item2); third(self.Item3); fourth(self.Item4); return Unit.Default; } /// /// Fold /// [Pure] public static S Fold(this (A, B, C, D) self, S state, Func fold) => fold(state, self.Item1, self.Item2, self.Item3, self.Item4); /// /// Quad-fold /// [Pure] public static S QuadFold(this(A, B, C, D) self, S state, Func firstFold, Func secondFold, Func thirdFold, Func fourthFold) => fourthFold(thirdFold(secondFold(firstFold(state, self.Item1), self.Item2), self.Item3), self.Item4); /// /// Quad-fold back /// [Pure] public static S QuadFoldBack(this (A, B, C, D) self, S state, Func firstFold, Func secondFold, Func thirdFold, Func fourthFold) => fourthFold(thirdFold(secondFold(firstFold(state, self.Item4), self.Item3), self.Item2), self.Item1); } ================================================ FILE: LanguageExt.Core/DataTypes/ValueTuple/Tuple4/ValueTuple4.Prelude.cs ================================================ using System; using System.Diagnostics.Contracts; using LanguageExt.Traits; namespace LanguageExt; public static partial class Prelude { /// /// Append an extra item to the tuple /// [Pure] public static (A, B, C, D, E) add((A, B, C, D) self, E fifth) => (self.Item1, self.Item2, self.Item3, self.Item4, fifth); /// /// Take the first item /// [Pure] public static A head((A, B, C, D) self) => self.Item1; /// /// Take the last item /// [Pure] public static D last((A, B, C, D) self) => self.Item4; /// /// Take the second item onwards and build a new tuple /// [Pure] public static (B, C, D) tail((A, B, C, D) self) => (self.Item2, self.Item3, self.Item4); /// /// One of the items matches the value passed /// [Pure] public static bool contains((A, A, A, A) self, A value) where EQ : Eq => EQ.Equals(self.Item1, value) || EQ.Equals(self.Item2, value) || EQ.Equals(self.Item3, value) || EQ.Equals(self.Item4, value); /// /// Map /// [Pure] public static R map(ValueTuple self, Func, R> map) => map(self); /// /// Map /// [Pure] public static R map(ValueTuple self, Func map) => map(self.Item1, self.Item2, self.Item3, self.Item4); /// /// Tri-map to tuple /// [Pure] public static (W, X, Y, Z) map((A, B, C, D) self, Func firstMap, Func secondMap, Func thirdMap, Func fourthMap) => (firstMap(self.Item1), secondMap(self.Item2), thirdMap(self.Item3), fourthMap(self.Item4)); /// /// First item-map to tuple /// [Pure] public static (R1, B, C, D) mapFirst((A, B, C, D) self, Func firstMap) => (firstMap(self.Item1), self.Item2, self.Item3, self.Item4); /// /// Second item-map to tuple /// [Pure] public static (A, R2, C, D) mapSecond((A, B, C, D) self, Func secondMap) => (self.Item1, secondMap(self.Item2), self.Item3, self.Item4); /// /// Third item-map to tuple /// [Pure] public static (A, B, R3, D) mapThird((A, B, C, D) self, Func thirdMap) => (self.Item1, self.Item2, thirdMap(self.Item3), self.Item4); /// /// Fourth item-map to tuple /// [Pure] public static (A, B, C, R4) mapFourth((A, B, C, D) self, Func fourthMap) => (self.Item1, self.Item2, self.Item3, fourthMap(self.Item4)); /// /// Iterate /// public static Unit iter((A, B, C, D) self, Action func) { func(self.Item1, self.Item2, self.Item3, self.Item4); return Unit.Default; } /// /// Iterate /// public static Unit iter((A, B, C, D) self, Action first, Action second, Action third, Action fourth) { first(self.Item1); second(self.Item2); third(self.Item3); fourth(self.Item4); return Unit.Default; } /// /// Fold /// [Pure] public static S fold((A, B, C, D) self, S state, Func fold) => fold(state, self.Item1, self.Item2, self.Item3, self.Item4); /// /// Quad-fold /// [Pure] public static S quadFold((A, B, C, D) self, S state, Func firstFold, Func secondFold, Func thirdFold, Func fourthFold) => fourthFold(thirdFold(secondFold(firstFold(state, self.Item1), self.Item2), self.Item3), self.Item4); /// /// Quad-fold back /// [Pure] public static S quadFoldBack((A, B, C, D) self, S state, Func firstFold, Func secondFold, Func thirdFold, Func fourthFold) => fourthFold(thirdFold(secondFold(firstFold(state, self.Item4), self.Item3), self.Item2), self.Item1); } ================================================ FILE: LanguageExt.Core/DataTypes/ValueTuple/Tuple5/ValueTuple5.Extensions.cs ================================================ using System; using LanguageExt; using System.Diagnostics.Contracts; using LanguageExt.Traits; public static class ValueTuple5Extensions { /// /// Append an extra item to the tuple /// [Pure] public static (A, B, C, D, E, F) Add(this (A, B, C, D, E) self, F sixth) => (self.Item1, self.Item2, self.Item3, self.Item4, self.Item5, sixth); /// /// Take the first item /// [Pure] public static A Head(this (A, B, C, D, E) self) => self.Item1; /// /// Take the last item /// [Pure] public static E Last(this (A, B, C, D, E) self) => self.Item5; /// /// Take the second item onwards and build a new tuple /// [Pure] public static (B, C, D, E) Tail(this(A, B, C, D, E) self) => (self.Item2, self.Item3, self.Item4, self.Item5); /// /// One of the items matches the value passed /// [Pure] public static bool Contains(this (A, A, A, A, A) self, A value) where EQ : Eq => EQ.Equals(self.Item1, value) || EQ.Equals(self.Item2, value) || EQ.Equals(self.Item3, value) || EQ.Equals(self.Item4, value) || EQ.Equals(self.Item5, value); /// /// Map /// [Pure] public static R Map(this ValueTuple self, Func, R> map) => map(self); /// /// Map /// [Pure] public static R Map(this ValueTuple self, Func map) => map(self.Item1, self.Item2, self.Item3, self.Item4, self.Item5); /// /// Tri-map to tuple /// [Pure] public static (V, W, X, Y, Z) Map(this(A, B, C, D, E) self, Func firstMap, Func secondMap, Func thirdMap, Func fourthMap, Func fifthMap) => (firstMap(self.Item1), secondMap(self.Item2), thirdMap(self.Item3), fourthMap(self.Item4), fifthMap(self.Item5)); /// /// First item-map to tuple /// [Pure] public static (R1, B, C, D, E) MapFirst(this (A, B, C, D, E) self, Func firstMap) => (firstMap(self.Item1), self.Item2, self.Item3, self.Item4, self.Item5); /// /// Second item-map to tuple /// [Pure] public static (A, R2, C, D, E) MapSecond(this (A, B, C, D, E) self, Func secondMap) => (self.Item1, secondMap(self.Item2), self.Item3, self.Item4, self.Item5); /// /// Third item-map to tuple /// [Pure] public static (A, B, R3, D, E) MapThird(this (A, B, C, D, E) self, Func thirdMap) => (self.Item1, self.Item2, thirdMap(self.Item3), self.Item4, self.Item5); /// /// Fourth item-map to tuple /// [Pure] public static (A, B, C, R4, E) MapFourth(this(A, B, C, D, E) self, Func fourthMap) => (self.Item1, self.Item2, self.Item3, fourthMap(self.Item4), self.Item5); /// /// Fifth item-map to tuple /// [Pure] public static (A, B, C, D, R5) MapFifth(this(A, B, C, D, E) self, Func fifthMap) => (self.Item1, self.Item2, self.Item3, self.Item4, fifthMap(self.Item5)); /// /// Map to tuple /// [Pure] public static (V, W, X, Y, Z) Select(this (A, B, C, D, E) self, Func<(A, B, C, D, E), (V, W, X, Y, Z)> map) => map(self); /// /// Iterate /// public static Unit Iter(this (A, B, C, D, E) self, Action func) { func(self.Item1, self.Item2, self.Item3, self.Item4, self.Item5); return Unit.Default; } /// /// Iterate /// public static Unit Iter(this (A, B, C, D, E) self, Action first, Action second, Action third, Action fourth, Action fifth) { first(self.Item1); second(self.Item2); third(self.Item3); fourth(self.Item4); fifth(self.Item5); return Unit.Default; } /// /// Fold /// [Pure] public static S Fold(this (A, B, C, D, E) self, S state, Func fold) => fold(state, self.Item1, self.Item2, self.Item3, self.Item4, self.Item5); /// /// Fold /// [Pure] public static S QuintFold(this(A, B, C, D, E) self, S state, Func firstFold, Func secondFold, Func thirdFold, Func fourthFold, Func fifthFold) => fifthFold(fourthFold(thirdFold(secondFold(firstFold(state, self.Item1), self.Item2), self.Item3), self.Item4), self.Item5); /// /// Fold back /// [Pure] public static S QuintFoldBack(this (A, B, C, D, E) self, S state, Func firstFold, Func secondFold, Func thirdFold, Func fourthFold, Func fifthFold) => fifthFold(fourthFold(thirdFold(secondFold(firstFold(state, self.Item5), self.Item4), self.Item3), self.Item2), self.Item1); } ================================================ FILE: LanguageExt.Core/DataTypes/ValueTuple/Tuple5/ValueTuple5.Prelude.cs ================================================ using System; using System.Diagnostics.Contracts; using LanguageExt.Traits; namespace LanguageExt; public static partial class Prelude { /// /// Append an extra item to the tuple /// [Pure] public static (A, B, C, D, E, F) add(this(A, B, C, D, E) self, F sixth) => (self.Item1, self.Item2, self.Item3, self.Item4, self.Item5, sixth); /// /// Take the first item /// [Pure] public static A head((A, B, C, D, E) self) => self.Item1; /// /// Take the last item /// [Pure] public static E last((A, B, C, D, E) self) => self.Item5; /// /// Take the second item onwards and build a new tuple /// [Pure] public static (B, C, D, E) tail((A, B, C, D, E) self) => (self.Item2, self.Item3, self.Item4, self.Item5); /// /// One of the items matches the value passed /// [Pure] public static bool contains((A, A, A, A, A) self, A value) where EQ : Eq => EQ.Equals(self.Item1, value) || EQ.Equals(self.Item2, value) || EQ.Equals(self.Item3, value) || EQ.Equals(self.Item4, value) || EQ.Equals(self.Item5, value); /// /// Map /// [Pure] public static R map(ValueTuple self, Func, R> map) => map(self); /// /// Map /// [Pure] public static R map(ValueTuple self, Func map) => map(self.Item1, self.Item2, self.Item3, self.Item4, self.Item5); /// /// Tri-map to tuple /// [Pure] public static (V, W, X, Y, Z) map((A, B, C, D, E) self, Func firstMap, Func secondMap, Func thirdMap, Func fourthMap, Func fifthMap) => (firstMap(self.Item1), secondMap(self.Item2), thirdMap(self.Item3), fourthMap(self.Item4), fifthMap(self.Item5)); /// /// First item-map to tuple /// [Pure] public static (R1, B, C, D, E) mapFirst((A, B, C, D, E) self, Func firstMap) => (firstMap(self.Item1), self.Item2, self.Item3, self.Item4, self.Item5); /// /// Second item-map to tuple /// [Pure] public static (A, R2, C, D, E) mapSecond((A, B, C, D, E) self, Func secondMap) => (self.Item1, secondMap(self.Item2), self.Item3, self.Item4, self.Item5); /// /// Third item-map to tuple /// [Pure] public static (A, B, R3, D, E) mapThird((A, B, C, D, E) self, Func thirdMap) => (self.Item1, self.Item2, thirdMap(self.Item3), self.Item4, self.Item5); /// /// Fourth item-map to tuple /// [Pure] public static (A, B, C, R4, E) mapFourth((A, B, C, D, E) self, Func fourthMap) => (self.Item1, self.Item2, self.Item3, fourthMap(self.Item4), self.Item5); /// /// Fifth item-map to tuple /// [Pure] public static (A, B, C, D, R5) mapFifth((A, B, C, D, E) self, Func fifthMap) => (self.Item1, self.Item2, self.Item3, self.Item4, fifthMap(self.Item5)); /// /// Iterate /// public static Unit iter((A, B, C, D, E) self, Action func) { func(self.Item1, self.Item2, self.Item3, self.Item4, self.Item5); return Unit.Default; } /// /// Iterate /// public static Unit iter((A, B, C, D, E) self, Action first, Action second, Action third, Action fourth, Action fifth) { first(self.Item1); second(self.Item2); third(self.Item3); fourth(self.Item4); fifth(self.Item5); return Unit.Default; } /// /// Fold /// [Pure] public static S fold((A, B, C, D, E) self, S state, Func fold) => fold(state, self.Item1, self.Item2, self.Item3, self.Item4, self.Item5); /// /// Fold /// [Pure] public static S quintFold((A, B, C, D, E) self, S state, Func firstFold, Func secondFold, Func thirdFold, Func fourthFold, Func fifthFold) => fifthFold(fourthFold(thirdFold(secondFold(firstFold(state, self.Item1), self.Item2), self.Item3), self.Item4), self.Item5); /// /// Fold back /// [Pure] public static S quintFoldBack((A, B, C, D, E) self, S state, Func firstFold, Func secondFold, Func thirdFold, Func fourthFold, Func fifthFold) => fifthFold(fourthFold(thirdFold(secondFold(firstFold(state, self.Item5), self.Item4), self.Item3), self.Item2), self.Item1); } ================================================ FILE: LanguageExt.Core/DataTypes/ValueTuple/Tuple6/ValueTuple6.Extensions.cs ================================================ using System; using LanguageExt; using System.Diagnostics.Contracts; using LanguageExt.Traits; public static class ValueTuple6Extensions { /// /// Append an extra item to the tuple /// [Pure] public static (A, B, C, D, E, F, G) Add(this (A, B, C, D, E, F) self, G seventh) => (self.Item1, self.Item2, self.Item3, self.Item4, self.Item5, self.Item6, seventh); /// /// Take the first item /// [Pure] public static A Head(this (A, B, C, D, E, F) self) => self.Item1; /// /// Take the last item /// [Pure] public static F Last(this (A, B, C, D, E, F) self) => self.Item6; /// /// Take the second item onwards and build a new tuple /// [Pure] public static (B, C, D, E, F) Tail(this(A, B, C, D, E, F) self) => (self.Item2, self.Item3, self.Item4, self.Item5, self.Item6); /// /// One of the items matches the value passed /// [Pure] public static bool Contains(this (A, A, A, A, A, A) self, A value) where EQ : Eq => EQ.Equals(self.Item1, value) || EQ.Equals(self.Item2, value) || EQ.Equals(self.Item3, value) || EQ.Equals(self.Item4, value) || EQ.Equals(self.Item5, value) || EQ.Equals(self.Item6, value); /// /// Map /// [Pure] public static R Map(this ValueTuple self, Func, R> map) => map(self); /// /// Map /// [Pure] public static R Map(this ValueTuple self, Func map) => map(self.Item1, self.Item2, self.Item3, self.Item4, self.Item5, self.Item6); /// /// Tri-map to tuple /// [Pure] public static (U, V, W, X, Y, Z) Map(this(A, B, C, D, E, F) self, Func firstMap, Func secondMap, Func thirdMap, Func fourthMap, Func fifthMap, Func sixthMap) => (firstMap(self.Item1), secondMap(self.Item2), thirdMap(self.Item3), fourthMap(self.Item4), fifthMap(self.Item5), sixthMap(self.Item6)); /// /// First item-map to tuple /// [Pure] public static (R1, B, C, D, E, F) MapFirst(this (A, B, C, D, E, F) self, Func firstMap) => (firstMap(self.Item1), self.Item2, self.Item3, self.Item4, self.Item5, self.Item6); /// /// Second item-map to tuple /// [Pure] public static (A, R2, C, D, E, F) MapSecond(this (A, B, C, D, E, F) self, Func secondMap) => (self.Item1, secondMap(self.Item2), self.Item3, self.Item4, self.Item5, self.Item6); /// /// Third item-map to tuple /// [Pure] public static (A, B, R3, D, E, F) MapThird(this (A, B, C, D, E, F) self, Func thirdMap) => (self.Item1, self.Item2, thirdMap(self.Item3), self.Item4, self.Item5, self.Item6); /// /// Fourth item-map to tuple /// [Pure] public static (A, B, C, R4, E, F) MapFourth(this(A, B, C, D, E, F) self, Func fourthMap) => (self.Item1, self.Item2, self.Item3, fourthMap(self.Item4), self.Item5, self.Item6); /// /// Fifth item-map to tuple /// [Pure] public static (A, B, C, D, R5, F) MapFifth(this(A, B, C, D, E, F) self, Func fifthMap) => (self.Item1, self.Item2, self.Item3, self.Item4, fifthMap(self.Item5), self.Item6); /// /// Sixth item-map to tuple /// [Pure] public static (A, B, C, D, E, R6) MapSixth(this(A, B, C, D, E, F) self, Func sixthMap) => (self.Item1, self.Item2, self.Item3, self.Item4, self.Item5, sixthMap(self.Item6)); /// /// Map to tuple /// [Pure] public static (U, V, W, X, Y, Z) Select(this (A, B, C, D, E, F) self, Func<(A, B, C, D, E, F), (U, V, W, X, Y, Z)> map) => map(self); /// /// Iterate /// public static Unit Iter(this (A, B, C, D, E, F) self, Action func) { func(self.Item1, self.Item2, self.Item3, self.Item4, self.Item5, self.Item6); return Unit.Default; } /// /// Iterate /// public static Unit Iter(this (A, B, C, D, E, F) self, Action first, Action second, Action third, Action fourth, Action fifth, Action sixth) { first(self.Item1); second(self.Item2); third(self.Item3); fourth(self.Item4); fifth(self.Item5); sixth(self.Item6); return Unit.Default; } /// /// Fold /// [Pure] public static S Fold(this (A, B, C, D, E, F) self, S state, Func fold) => fold(state, self.Item1, self.Item2, self.Item3, self.Item4, self.Item5, self.Item6); /// /// Fold /// [Pure] public static S SextFold(this(A, B, C, D, E, F) self, S state, Func firstFold, Func secondFold, Func thirdFold, Func fourthFold, Func fifthFold, Func sixthFold) => sixthFold(fifthFold(fourthFold(thirdFold(secondFold(firstFold(state, self.Item1), self.Item2), self.Item3), self.Item4), self.Item5), self.Item6); /// /// Fold back /// [Pure] public static S SextFoldBack(this (A, B, C, D, E, F) self, S state, Func firstFold, Func secondFold, Func thirdFold, Func fourthFold, Func fifthFold, Func sixthFold) => sixthFold(fifthFold(fourthFold(thirdFold(secondFold(firstFold(state, self.Item6), self.Item5), self.Item4), self.Item3), self.Item2), self.Item1); } ================================================ FILE: LanguageExt.Core/DataTypes/ValueTuple/Tuple6/ValueTuple6.Prelude.cs ================================================ using System; using System.Diagnostics.Contracts; using LanguageExt.Traits; namespace LanguageExt; public static partial class Prelude { /// /// Append an extra item to the tuple /// [Pure] public static (A, B, C, D, E, F, G) add((A, B, C, D, E, F) self, G seventh) => (self.Item1, self.Item2, self.Item3, self.Item4, self.Item5, self.Item6, seventh); /// /// Take the first item /// [Pure] public static A head((A, B, C, D, E, F) self) => self.Item1; /// /// Take the last item /// [Pure] public static F last((A, B, C, D, E, F) self) => self.Item6; /// /// Take the second item onwards and build a new tuple /// [Pure] public static (B, C, D, E, F) tail((A, B, C, D, E, F) self) => (self.Item2, self.Item3, self.Item4, self.Item5, self.Item6); /// /// One of the items matches the value passed /// [Pure] public static bool contains((A, A, A, A, A, A) self, A value) where EQ : Eq => EQ.Equals(self.Item1, value) || EQ.Equals(self.Item2, value) || EQ.Equals(self.Item3, value) || EQ.Equals(self.Item4, value) || EQ.Equals(self.Item5, value) || EQ.Equals(self.Item6, value); /// /// Map /// [Pure] public static R map(ValueTuple self, Func, R> map) => map(self); /// /// Map /// [Pure] public static R map(ValueTuple self, Func map) => map(self.Item1, self.Item2, self.Item3, self.Item4, self.Item5, self.Item6); /// /// Tri-map to tuple /// [Pure] public static (U, V, W, X, Y, Z) map((A, B, C, D, E, F) self, Func firstMap, Func secondMap, Func thirdMap, Func fourthMap, Func fifthMap, Func sixthMap) => (firstMap(self.Item1), secondMap(self.Item2), thirdMap(self.Item3), fourthMap(self.Item4), fifthMap(self.Item5), sixthMap(self.Item6)); /// /// First item-map to tuple /// [Pure] public static (R1, B, C, D, E, F) mapFirst((A, B, C, D, E, F) self, Func firstMap) => (firstMap(self.Item1), self.Item2, self.Item3, self.Item4, self.Item5, self.Item6); /// /// Second item-map to tuple /// [Pure] public static (A, R2, C, D, E, F) mapSecond((A, B, C, D, E, F) self, Func secondMap) => (self.Item1, secondMap(self.Item2), self.Item3, self.Item4, self.Item5, self.Item6); /// /// Third item-map to tuple /// [Pure] public static (A, B, R3, D, E, F) mapThird((A, B, C, D, E, F) self, Func thirdMap) => (self.Item1, self.Item2, thirdMap(self.Item3), self.Item4, self.Item5, self.Item6); /// /// Fourth item-map to tuple /// [Pure] public static (A, B, C, R4, E, F) mapFourth((A, B, C, D, E, F) self, Func fourthMap) => (self.Item1, self.Item2, self.Item3, fourthMap(self.Item4), self.Item5, self.Item6); /// /// Fifth item-map to tuple /// [Pure] public static (A, B, C, D, R5, F) mapFifth((A, B, C, D, E, F) self, Func fifthMap) => (self.Item1, self.Item2, self.Item3, self.Item4, fifthMap(self.Item5), self.Item6); /// /// Sixth item-map to tuple /// [Pure] public static (A, B, C, D, E, R6) mapSixth((A, B, C, D, E, F) self, Func sixthMap) => (self.Item1, self.Item2, self.Item3, self.Item4, self.Item5, sixthMap(self.Item6)); /// /// Iterate /// public static Unit iter((A, B, C, D, E, F) self, Action func) { func(self.Item1, self.Item2, self.Item3, self.Item4, self.Item5, self.Item6); return Unit.Default; } /// /// Iterate /// public static Unit iter((A, B, C, D, E, F) self, Action first, Action second, Action third, Action fourth, Action fifth, Action sixth) { first(self.Item1); second(self.Item2); third(self.Item3); fourth(self.Item4); fifth(self.Item5); sixth(self.Item6); return Unit.Default; } /// /// Fold /// [Pure] public static S fold((A, B, C, D, E, F) self, S state, Func fold) => fold(state, self.Item1, self.Item2, self.Item3, self.Item4, self.Item5, self.Item6); /// /// Fold /// [Pure] public static S sextFold((A, B, C, D, E, F) self, S state, Func firstFold, Func secondFold, Func thirdFold, Func fourthFold, Func fifthFold, Func sixthFold) => sixthFold(fifthFold(fourthFold(thirdFold(secondFold(firstFold(state, self.Item1), self.Item2), self.Item3), self.Item4), self.Item5), self.Item6); /// /// Fold back /// [Pure] public static S sextFoldBack((A, B, C, D, E, F) self, S state, Func firstFold, Func secondFold, Func thirdFold, Func fourthFold, Func fifthFold, Func sixthFold) => sixthFold(fifthFold(fourthFold(thirdFold(secondFold(firstFold(state, self.Item6), self.Item5), self.Item4), self.Item3), self.Item2), self.Item1); } ================================================ FILE: LanguageExt.Core/DataTypes/ValueTuple/Tuple7/ValueTuple7.Extensions.cs ================================================ using System; using LanguageExt; using System.Diagnostics.Contracts; using LanguageExt.Traits; public static class ValueTuple7Extensions { /// /// Append an extra item to the tuple /// [Pure] public static (A, B, C, D, E, F, G, H) Add(this (A, B, C, D, E, F, G) self, H eighth) => (self.Item1, self.Item2, self.Item3, self.Item4, self.Item5, self.Item6, self.Item7, eighth); /// /// Take the first item /// [Pure] public static A Head(this (A, B, C, D, E, F, G) self) => self.Item1; /// /// Take the last item /// [Pure] public static G Last(this (A, B, C, D, E, F, G) self) => self.Item7; /// /// Take the second item onwards and build a new tuple /// [Pure] public static (B, C, D, E, F, G) Tail(this(A, B, C, D, E, F, G) self) => (self.Item2, self.Item3, self.Item4, self.Item5, self.Item6, self.Item7); /// /// One of the items matches the value passed /// [Pure] public static bool Contains(this (A, A, A, A, A, A, A) self, A value) where EQ : Eq => EQ.Equals(self.Item1, value) || EQ.Equals(self.Item2, value) || EQ.Equals(self.Item3, value) || EQ.Equals(self.Item4, value) || EQ.Equals(self.Item5, value) || EQ.Equals(self.Item6, value) || EQ.Equals(self.Item7, value); /// /// Map /// [Pure] public static R Map(this ValueTuple self, Func, R> map) => map(self); /// /// Map /// [Pure] public static R Map(this ValueTuple self, Func map) => map(self.Item1, self.Item2, self.Item3, self.Item4, self.Item5, self.Item6, self.Item7); /// /// Tri-map to tuple /// [Pure] public static (T, U, V, W, X, Y, Z) Map(this(A, B, C, D, E, F, G) self, Func firstMap, Func secondMap, Func thirdMap, Func fourthMap, Func fifthMap, Func sixthMap, Func seventhMap) => (firstMap(self.Item1), secondMap(self.Item2), thirdMap(self.Item3), fourthMap(self.Item4), fifthMap(self.Item5), sixthMap(self.Item6), seventhMap(self.Item7)); /// /// First item-map to tuple /// [Pure] public static (R1, B, C, D, E, F, G) MapFirst(this (A, B, C, D, E, F, G) self, Func firstMap) => (firstMap(self.Item1), self.Item2, self.Item3, self.Item4, self.Item5, self.Item6, self.Item7); /// /// Second item-map to tuple /// [Pure] public static (A, R2, C, D, E, F, G) MapSecond(this (A, B, C, D, E, F, G) self, Func secondMap) => (self.Item1, secondMap(self.Item2), self.Item3, self.Item4, self.Item5, self.Item6, self.Item7); /// /// Third item-map to tuple /// [Pure] public static (A, B, R3, D, E, F, G) MapThird(this (A, B, C, D, E, F, G) self, Func thirdMap) => (self.Item1, self.Item2, thirdMap(self.Item3), self.Item4, self.Item5, self.Item6, self.Item7); /// /// Fourth item-map to tuple /// [Pure] public static (A, B, C, R4, E, F, G) MapFourth(this(A, B, C, D, E, F, G) self, Func fourthMap) => (self.Item1, self.Item2, self.Item3, fourthMap(self.Item4), self.Item5, self.Item6, self.Item7); /// /// Fifth item-map to tuple /// [Pure] public static (A, B, C, D, R5, F, G) MapFifth(this(A, B, C, D, E, F, G) self, Func fifthMap) => (self.Item1, self.Item2, self.Item3, self.Item4, fifthMap(self.Item5), self.Item6, self.Item7); /// /// Sixth item-map to tuple /// [Pure] public static (A, B, C, D, E, R6, G) MapSixth(this(A, B, C, D, E, F, G) self, Func sixthMap) => (self.Item1, self.Item2, self.Item3, self.Item4, self.Item5, sixthMap(self.Item6), self.Item7); /// /// Sixth item-map to tuple /// [Pure] public static (A, B, C, D, E, F, R7) MapSeventh(this(A, B, C, D, E, F, G) self, Func seventhMap) => (self.Item1, self.Item2, self.Item3, self.Item4, self.Item5, self.Item6, seventhMap(self.Item7)); /// /// Map to tuple /// [Pure] public static (U, V, W, X, Y, Z) Select(this (A, B, C, D, E, F, G) self, Func<(A, B, C, D, E, F, G), (U, V, W, X, Y, Z)> map) => map(self); /// /// Iterate /// public static Unit Iter(this (A, B, C, D, E, F, G) self, Action func) { func(self.Item1, self.Item2, self.Item3, self.Item4, self.Item5, self.Item6, self.Item7); return Unit.Default; } /// /// Iterate /// public static Unit Iter(this (A, B, C, D, E, F, G) self, Action first, Action second, Action third, Action fourth, Action fifth, Action sixth, Action seventh) { first(self.Item1); second(self.Item2); third(self.Item3); fourth(self.Item4); fifth(self.Item5); sixth(self.Item6); seventh(self.Item7); return Unit.Default; } /// /// Fold /// [Pure] public static S Fold(this (A, B, C, D, E, F, G) self, S state, Func fold) => fold(state, self.Item1, self.Item2, self.Item3, self.Item4, self.Item5, self.Item6, self.Item7); /// /// Fold /// [Pure] public static S SeptFold(this(A, B, C, D, E, F, G) self, S state, Func firstFold, Func secondFold, Func thirdFold, Func fourthFold, Func fifthFold, Func sixthFold, Func seventhFold) => seventhFold(sixthFold(fifthFold(fourthFold(thirdFold(secondFold(firstFold(state, self.Item1), self.Item2), self.Item3), self.Item4), self.Item5), self.Item6), self.Item7); /// /// Fold back /// [Pure] public static S SeptFoldBack(this (A, B, C, D, E, F, G) self, S state, Func firstFold, Func secondFold, Func thirdFold, Func fourthFold, Func fifthFold, Func sixthFold, Func seventhFold) => seventhFold(sixthFold(fifthFold(fourthFold(thirdFold(secondFold(firstFold(state, self.Item7), self.Item6), self.Item5), self.Item4), self.Item3), self.Item2), self.Item1); } ================================================ FILE: LanguageExt.Core/DataTypes/ValueTuple/Tuple7/ValueTuple7.Prelude.cs ================================================ using System; using System.Diagnostics.Contracts; using LanguageExt.Traits; namespace LanguageExt; public static partial class Prelude { /// /// Append an extra item to the tuple /// [Pure] public static (A, B, C, D, E, F, G, H) add((A, B, C, D, E, F, G) self, H eighth) => (self.Item1, self.Item2, self.Item3, self.Item4, self.Item5, self.Item6, self.Item7, eighth); /// /// Take the first item /// [Pure] public static A head((A, B, C, D, E, F, G) self) => self.Item1; /// /// Take the last item /// [Pure] public static G last((A, B, C, D, E, F, G) self) => self.Item7; /// /// Take the second item onwards and build a new tuple /// [Pure] public static (B, C, D, E, F, G) tail((A, B, C, D, E, F, G) self) => (self.Item2, self.Item3, self.Item4, self.Item5, self.Item6, self.Item7); /// /// One of the items matches the value passed /// [Pure] public static bool contains((A, A, A, A, A, A, A) self, A value) where EQ : Eq => EQ.Equals(self.Item1, value) || EQ.Equals(self.Item2, value) || EQ.Equals(self.Item3, value) || EQ.Equals(self.Item4, value) || EQ.Equals(self.Item5, value) || EQ.Equals(self.Item6, value) || EQ.Equals(self.Item7, value); /// /// Map /// [Pure] public static R map(ValueTuple self, Func, R> map) => map(self); /// /// Map /// [Pure] public static R map(ValueTuple self, Func map) => map(self.Item1, self.Item2, self.Item3, self.Item4, self.Item5, self.Item6, self.Item7); /// /// Tri-map to tuple /// [Pure] public static (T, U, V, W, X, Y, Z) map((A, B, C, D, E, F, G) self, Func firstMap, Func secondMap, Func thirdMap, Func fourthMap, Func fifthMap, Func sixthMap, Func seventhMap) => (firstMap(self.Item1), secondMap(self.Item2), thirdMap(self.Item3), fourthMap(self.Item4), fifthMap(self.Item5), sixthMap(self.Item6), seventhMap(self.Item7)); /// /// First item-map to tuple /// [Pure] public static (R1, B, C, D, E, F, G) mapFirst((A, B, C, D, E, F, G) self, Func firstMap) => (firstMap(self.Item1), self.Item2, self.Item3, self.Item4, self.Item5, self.Item6, self.Item7); /// /// Second item-map to tuple /// [Pure] public static (A, R2, C, D, E, F, G) mapSecond((A, B, C, D, E, F, G) self, Func secondMap) => (self.Item1, secondMap(self.Item2), self.Item3, self.Item4, self.Item5, self.Item6, self.Item7); /// /// Third item-map to tuple /// [Pure] public static (A, B, R3, D, E, F, G) mapThird((A, B, C, D, E, F, G) self, Func thirdMap) => (self.Item1, self.Item2, thirdMap(self.Item3), self.Item4, self.Item5, self.Item6, self.Item7); /// /// Fourth item-map to tuple /// [Pure] public static (A, B, C, R4, E, F, G) mapFourth((A, B, C, D, E, F, G) self, Func fourthMap) => (self.Item1, self.Item2, self.Item3, fourthMap(self.Item4), self.Item5, self.Item6, self.Item7); /// /// Fifth item-map to tuple /// [Pure] public static (A, B, C, D, R5, F, G) mapFifth((A, B, C, D, E, F, G) self, Func fifthMap) => (self.Item1, self.Item2, self.Item3, self.Item4, fifthMap(self.Item5), self.Item6, self.Item7); /// /// Sixth item-map to tuple /// [Pure] public static (A, B, C, D, E, R6, G) mapSixth((A, B, C, D, E, F, G) self, Func sixthMap) => (self.Item1, self.Item2, self.Item3, self.Item4, self.Item5, sixthMap(self.Item6), self.Item7); /// /// Sixth item-map to tuple /// [Pure] public static (A, B, C, D, E, F, R7) mapSeventh((A, B, C, D, E, F, G) self, Func seventhMap) => (self.Item1, self.Item2, self.Item3, self.Item4, self.Item5, self.Item6, seventhMap(self.Item7)); /// /// Iterate /// public static Unit iter((A, B, C, D, E, F, G) self, Action func) { func(self.Item1, self.Item2, self.Item3, self.Item4, self.Item5, self.Item6, self.Item7); return Unit.Default; } /// /// Iterate /// public static Unit iter((A, B, C, D, E, F, G) self, Action first, Action second, Action third, Action fourth, Action fifth, Action sixth, Action seventh) { first(self.Item1); second(self.Item2); third(self.Item3); fourth(self.Item4); fifth(self.Item5); sixth(self.Item6); seventh(self.Item7); return Unit.Default; } /// /// Fold /// [Pure] public static S fold((A, B, C, D, E, F, G) self, S state, Func fold) => fold(state, self.Item1, self.Item2, self.Item3, self.Item4, self.Item5, self.Item6, self.Item7); /// /// Fold /// [Pure] public static S septFold((A, B, C, D, E, F, G) self, S state, Func firstFold, Func secondFold, Func thirdFold, Func fourthFold, Func fifthFold, Func sixthFold, Func seventhFold) => seventhFold(sixthFold(fifthFold(fourthFold(thirdFold(secondFold(firstFold(state, self.Item1), self.Item2), self.Item3), self.Item4), self.Item5), self.Item6), self.Item7); /// /// Fold back /// [Pure] public static S septFoldBack((A, B, C, D, E, F, G) self, S state, Func firstFold, Func secondFold, Func thirdFold, Func fourthFold, Func fifthFold, Func sixthFold, Func seventhFold) => seventhFold(sixthFold(fifthFold(fourthFold(thirdFold(secondFold(firstFold(state, self.Item7), self.Item6), self.Item5), self.Item4), self.Item3), self.Item2), self.Item1); } ================================================ FILE: LanguageExt.Core/Deriving/Alternative.cs ================================================ using LanguageExt.Traits; namespace LanguageExt; public static partial class Deriving { public interface Alternative : Alternative, Choice, Applicative where Supertype : Alternative where Subtype : Alternative; } ================================================ FILE: LanguageExt.Core/Deriving/Applicative.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public static partial class Deriving { /// /// Derived applicative functor implementation /// /// Super-type wrapper around the subtype /// The subtype that the supertype type 'wraps' public interface Applicative : Applicative, Functor where Subtype : Applicative where Supertype : Applicative { static K Applicative.Pure(A value) => Supertype.CoTransform(Subtype.Pure(value)); static K Applicative.Action(K ma, K mb) => Supertype.CoTransform(Subtype.Action(Supertype.Transform(ma), Supertype.Transform(mb))); static K Applicative.Apply(K> mf, K ma) => Supertype.CoTransform(Subtype.Apply(Supertype.Transform(mf), Supertype.Transform(ma))); static K Applicative.Apply(K> mf, Memo ma) => Supertype.CoTransform(Subtype.Apply(Supertype.Transform(mf), Memo.transform(ma))); } } ================================================ FILE: LanguageExt.Core/Deriving/Choice.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public static partial class Deriving { /// /// A semigroup on applicative functors /// public interface Choice : Choice, Traits.Natural, Traits.CoNatural where Supertype : Choice where Subtype : Choice { /// /// Where `Supertype` defines some notion of failure or choice, this function picks /// the first argument that succeeds. So, if `fa` succeeds, then `fa` is returned; /// if it fails, then `fb` is returned. /// /// First structure to test /// Second structure to return if the first one fails /// Bound value type /// First argument to succeed static K Choice.Choose(K fa, K fb) => Supertype.CoTransform(Subtype.Choose(Supertype.Transform(fa), Supertype.Transform(fb))); /// /// Where `Supertype` defines some notion of failure or choice, this function picks /// the first argument that succeeds. So, if `fa` succeeds, then `fa` is returned; /// if it fails, then `fb` is returned. /// /// First structure to test /// Second structure to return if the first one fails /// Bound value type /// First argument to succeed static K Choice.Choose(K fa, Memo fb) => Supertype.CoTransform(Subtype.Choose(Supertype.Transform(fa), Memo.transform(fb))); } } ================================================ FILE: LanguageExt.Core/Deriving/Cofunctor.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public static partial class Deriving { /// /// Derive the class of contravariant functors. /// /// Whereas one can think of a `Functor` as containing or producing values, a contravariant functor is a functor that /// can be thought of as consuming values. /// /// Contravariant functors are referred to colloquially as Cofunctor, even though the dual of a `Functor` is just /// a `Functor`. /// /// Super-type wrapper around the subtype /// The subtype that the supertype type 'wraps' public interface Cofunctor : Cofunctor, Traits.Natural, Traits.CoNatural where Subtype : Cofunctor where Supertype : Cofunctor { static K Cofunctor.Comap(Func f, K fb) => Supertype.CoTransform(Subtype.Comap(f, Supertype.Transform(fb))); } } ================================================ FILE: LanguageExt.Core/Deriving/Decidable.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public static partial class Deriving { /// /// Derive a `Decidable` contravariant functor that is the contravariant analogue of `Alternative`. /// /// Noting the superclass constraint that `f` must also be `Divisible`, a `Decidable` functor has the ability to /// "fan out" input, under the intuition that contravariant functors consume input. /// /// Self referring type public interface Decidable : Divisible, Decidable where Subtype : Decidable where Supertype : Decidable { static K Decidable.Lose(Func f) => Supertype.CoTransform(Subtype.Lose(f)); static K Decidable.Route( Func> f, K fb, K fc) => Supertype.CoTransform(Subtype.Route(f, Supertype.Transform(fb), Supertype.Transform(fc))); } } ================================================ FILE: LanguageExt.Core/Deriving/Divisible.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public static partial class Deriving { public interface Divisible : Cofunctor, Divisible where Subtype : Decidable where Supertype : Decidable { static K Divisible.Divide( Func f, K fb, K fc) => Supertype.CoTransform(Subtype.Divide(f, Supertype.Transform(fb), Supertype.Transform(fc))); static K Divisible.Conquer() => Supertype.CoTransform(Subtype.Conquer()); } } ================================================ FILE: LanguageExt.Core/Deriving/Fallible.cs ================================================ using System; using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt; public static partial class Deriving { /// /// Trait for higher-kinded structures that have a failure state `E` /// public interface Fallible : Traits.Fallible where Supertype : Fallible, Traits.Fallible, Traits.Natural, Traits.CoNatural where Subtype : Traits.Fallible { /// /// Raise a failure state in the `Fallible` structure `F` /// /// Error value /// Bound value type /// static K Traits.Fallible.Fail(E error) => Supertype.CoTransform(Subtype.Fail(error)); /// /// Run the `Fallible` structure. If in a failed state, test the failure value /// against the predicate. If, it returns `true`, run the `Fail` function with /// the failure value. /// /// `Fallible` structure /// Predicate to test any failure values /// Handler when in failed state /// Bound value type /// Either `fa` or the result of `Fail` if `fa` is in a failed state and the /// predicate returns true for the failure value static K Traits.Fallible.Catch( K fa, Func Predicate, Func> Fail) => Supertype.CoTransform(Subtype.Catch(Supertype.Transform(fa), Predicate, e => Supertype.Transform(Fail(e)))); } /// /// Trait for higher-kinded structures that have a failure state `E` /// public interface Fallible : Fallible where Supertype : Fallible, Traits.Natural, Traits.CoNatural where Subtype : Traits.Fallible; } ================================================ FILE: LanguageExt.Core/Deriving/Final.cs ================================================ using LanguageExt.Traits; namespace LanguageExt; public static partial class Deriving { /// /// Derives `finally` in a `try/finally` operation /// public interface Final : Final, Traits.Natural, Traits.CoNatural where Supertype : Final, Final where Subtype : Final { /// /// Run a `finally` operation after the `fa` operation regardless of whether `fa` succeeds or not. /// /// Primary operation /// Finally operation /// Result of primary operation static K Final.Finally(K fa, K @finally) => Supertype.CoTransform(Subtype.Finally(Supertype.Transform(fa), Supertype.Transform(@finally))); } } ================================================ FILE: LanguageExt.Core/Deriving/Foldable.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public static partial class Deriving { public interface Foldable : Foldable, Traits.Natural, Traits.CoNatural where Supertype : Foldable, Foldable where Subtype : Foldable { /// /// Same behaviour as `Fold` but allows early exit of the operation once /// the predicate function becomes `false` for the state/value pair /// static S Foldable.FoldWhile( Func> f, Func<(S State, A Value), bool> predicate, S initialState, K ta) => Subtype.FoldWhile(f, predicate, initialState, Supertype.Transform(ta)); /// /// Same behaviour as `FoldBack` but allows early exit of the operation once /// the predicate function becomes `false` for the state/value pair /// static S Foldable.FoldBackWhile( Func> f, Func<(S State, A Value), bool> predicate, S initialState, K ta) => Subtype.FoldBackWhile(f, predicate, initialState, Supertype.Transform(ta)); /// /// Fold until the `Option` returns `None` /// /// Fold function /// Initial state for the fold /// Foldable structure /// Value type /// State type /// Aggregated value static S Foldable.FoldMaybe( Func>> f, S initialState, K ta) => Subtype.FoldMaybe(f, initialState, Supertype.Transform(ta)); /// /// Fold until the `Option` returns `None` /// /// Fold function /// Initial state for the fold /// Foldable structure /// Value type /// State type /// Aggregated value static S Foldable.FoldBackMaybe( Func>> f, S initialState, K ta) => Subtype.FoldBackMaybe(f, initialState, Supertype.Transform(ta)); /// /// Same behaviour as `Fold` but the fold operation returns a monadic type and allows /// early exit of the operation once the predicate function becomes `false` for the /// state/value pair /// static K Foldable.FoldWhileM( Func>> f, Func<(S State, A Value), bool> predicate, S initialState, K ta) => Subtype.FoldWhileM(f, predicate, initialState, Supertype.Transform(ta)); /// /// Same behaviour as `FoldBack` but the fold operation returns a monadic type and allows /// early exit of the operation once the predicate function becomes `false` for the /// state/value pair /// static K Foldable.FoldBackWhileM( Func>> f, Func<(S State, A Value), bool> predicate, S initialState, K ta) => Subtype.FoldBackWhileM(f, predicate, initialState, Supertype.Transform(ta)); /// /// Same behaviour as `Fold` but allows early exit of the operation once /// the predicate function becomes `false` for the state/value pair /// static S Foldable.FoldUntil( Func> f, Func<(S State, A Value), bool> predicate, S initialState, K ta) => Subtype.FoldUntil(f, predicate, initialState, Supertype.Transform(ta)); /// /// Same behaviour as `Fold` but the fold operation returns a monadic type and allows /// early exit of the operation once the predicate function becomes `false` for the /// state/value pair /// static K Foldable.FoldUntilM( Func>> f, Func<(S State, A Value), bool> predicate, S initialState, K ta) => Subtype.FoldUntilM(f, predicate, initialState, Supertype.Transform(ta)); /// /// Same behaviour as `FoldBack` but allows early exit of the operation once /// the predicate function becomes `false` for the state/value pair /// static S Foldable.FoldBackUntil( Func> f, Func<(S State, A Value), bool> predicate, S initialState, K ta) => Subtype.FoldBackUntil(f, predicate, initialState, Supertype.Transform(ta)); /// /// Same behaviour as `FoldBack` but the fold operation returns a monadic type and allows /// early exit of the operation once the predicate function becomes `false` for the /// state/value pair /// static K Foldable.FoldBackUntilM( Func>> f, Func<(S State, A Value), bool> predicate, S initialState, K ta) => Subtype.FoldBackUntilM(f, predicate, initialState, Supertype.Transform(ta)); /// /// Right-associative fold of a structure, lazy in the accumulator. /// /// In the case of lists, 'Fold', when applied to a binary operator, a /// starting value (typically the right-identity of the operator), and a /// list, reduces the list using the binary operator, from right to left. /// static S Foldable.Fold(Func> f, S initialState, K ta) => Subtype.Fold(f, initialState, Supertype.Transform(ta)); /// /// Right-associative fold of a structure, lazy in the accumulator. /// /// In the case of lists, 'Fold', when applied to a binary operator, a /// starting value (typically the right-identity of the operator), and a /// list, reduces the list using the binary operator, from right to left. /// static K Foldable.FoldM( Func>> f, S initialState, K ta) => Subtype.FoldM(f, initialState, Supertype.Transform(ta)); /// /// Left-associative fold of a structure, lazy in the accumulator. This /// is rarely what you want, but can work well for structures with efficient /// right-to-left sequencing and an operator that is lazy in its left /// argument. /// /// In the case of lists, 'FoldLeft', when applied to a binary operator, a /// starting value (typically the left-identity of the operator), and a /// list, reduces the list using the binary operator, from left to right /// /// /// Note that to produce the outermost application of the operator the /// entire input list must be traversed. Like all left-associative folds, /// `FoldBack` will diverge if given an infinite list. /// static S Foldable.FoldBack(Func> f, S initialState, K ta) => Subtype.FoldBack(f, initialState, Supertype.Transform(ta)); /// /// Left-associative fold of a structure, lazy in the accumulator. This /// is rarely what you want, but can work well for structures with efficient /// right-to-left sequencing and an operator that is lazy in its left /// argument. /// /// In the case of lists, 'FoldLeft', when applied to a binary operator, a /// starting value (typically the left-identity of the operator), and a /// list, reduces the list using the binary operator, from left to right /// /// /// Note that to produce the outermost application of the operator the /// entire input list must be traversed. Like all left-associative folds, /// `FoldBack` will diverge if given an infinite list. /// static K Foldable.FoldBackM( Func>> f, S initialState, K ta) => Subtype.FoldBackM(f, initialState, Supertype.Transform(ta)); /// /// Given a structure with elements whose type is a `Monoid`, combine them /// via the monoid's `Append` operator. This fold is right-associative and /// lazy in the accumulator. When you need a strict left-associative fold, /// use 'foldMap'' instead, with 'id' as the map. /// static A Foldable.Fold(K tm) => Subtype.Fold(Supertype.Transform(tm)); /// /// Given a structure with elements whose type is a `Monoid`, combine them /// via the monoid's `Append` operator. This fold is right-associative and /// lazy in the accumulator. When you need a strict left-associative fold, /// use 'foldMap'' instead, with 'id' as the map. /// static A Foldable.FoldWhile(Func<(A State, A Value), bool> predicate, K tm) => Subtype.FoldWhile(predicate, Supertype.Transform(tm)); /// /// Given a structure with elements whose type is a `Monoid`, combine them /// via the monoid's `Append` operator. This fold is right-associative and /// lazy in the accumulator. When you need a strict left-associative fold, /// use 'foldMap'' instead, with 'id' as the map. /// static A Foldable.FoldUntil(Func<(A State, A Value), bool> predicate, K tm) => Subtype.FoldUntil(predicate, Supertype.Transform(tm)); /// /// Map each element of the structure into a monoid, and combine the /// results with `Append`. This fold is right-associative and lazy in the /// accumulator. For strict left-associative folds consider `FoldMapBack` /// instead. /// static B Foldable.FoldMap(Func f, K ta) => Subtype.FoldMap(f, Supertype.Transform(ta)); /// /// Map each element of the structure into a monoid, and combine the /// results with `Append`. This fold is right-associative and lazy in the /// accumulator. For strict left-associative folds consider `FoldMapBack` /// instead. /// static B Foldable.FoldMapWhile( Func f, Func<(B State, A Value), bool> predicate, K ta) => Subtype.FoldMapWhile(f, predicate, Supertype.Transform(ta)); /// /// Map each element of the structure into a monoid, and combine the /// results with `Append`. This fold is right-associative and lazy in the /// accumulator. For strict left-associative folds consider `FoldMapBack` /// instead. /// static B Foldable.FoldMapUntil( Func f, Func<(B State, A Value), bool> predicate, K ta) => Subtype.FoldMapUntil(f, predicate, Supertype.Transform(ta)); /// /// A left-associative variant of 'FoldMap' that is strict in the /// accumulator. Use this method for strict reduction when partial /// results are merged via `Append`. /// static B Foldable.FoldMapBack(Func f, K ta) => Subtype.FoldMapBack(f, Supertype.Transform(ta)); /// /// A left-associative variant of 'FoldMap' that is strict in the /// accumulator. Use this method for strict reduction when partial /// results are merged via `Append`. /// static B Foldable.FoldMapWhileBack( Func f, Func<(B State, A Value), bool> predicate, K ta) => Subtype.FoldMapWhileBack(f, predicate, Supertype.Transform(ta)); /// /// A left-associative variant of 'FoldMap' that is strict in the /// accumulator. Use this method for strict reduction when partial /// results are merged via `Append`. /// static B Foldable.FoldMapUntilBack(Func f, Func<(B State, A Value), bool> predicate, K ta) => Subtype.FoldMapUntilBack(f, predicate, Supertype.Transform(ta)); /// /// List of elements of a structure, from left to right /// static Seq Foldable.ToSeq(K ta) => Subtype.ToSeq(Supertype.Transform(ta)); /// /// List of elements of a structure, from left to right /// static Lst Foldable.ToLst(K ta) => Subtype.ToLst(Supertype.Transform(ta)); /// /// List of elements of a structure, from left to right /// static Arr Foldable.ToArr(K ta) => Subtype.ToArr(Supertype.Transform(ta)); /// /// List of elements of a structure, from left to right /// static Iterable Foldable.ToIterable(K ta) => Subtype.ToIterable(Supertype.Transform(ta)); /// /// List of elements of a structure, from left to right /// static bool Foldable.IsEmpty(K ta) => Subtype.IsEmpty(Supertype.Transform(ta)); /// /// Returns the size/length of a finite structure as an `int`. The /// default implementation just counts elements starting with the leftmost. /// /// Instances for structures that can compute the element count faster /// than via element-by-element counting, should provide a specialised /// implementation. /// static int Foldable.Count(K ta) => Subtype.Count(Supertype.Transform(ta)); /// /// Does an element that fits the predicate occur in the structure? /// static bool Foldable.Exists(Func predicate, K ta) => Subtype.Exists(predicate, Supertype.Transform(ta)); /// /// Does the predicate hold for all elements in the structure? /// static bool Foldable.ForAll(Func predicate, K ta) => Subtype.ForAll(predicate, Supertype.Transform(ta)); /// /// Does the element exist in the structure? /// static bool Foldable.Contains(A value, K ta) => Subtype.Contains(value, Supertype.Transform(ta)); /// /// Does the element exist in the structure? /// static bool Foldable.Contains(A value, K ta) => Subtype.Contains(value, Supertype.Transform(ta)); /// /// Find the first element that match the predicate /// static Option Foldable.Find(Func predicate, K ta) => Subtype.Find(predicate, Supertype.Transform(ta)); /// /// Find the last element that match the predicate /// static Option Foldable.FindBack(Func predicate, K ta) => Subtype.FindBack(predicate, Supertype.Transform(ta)); /// /// Find the elements that match the predicate /// static Iterable Foldable.FindAll(Func predicate, K ta) => Subtype.FindAll(predicate, Supertype.Transform(ta)); /// /// Find the elements that match the predicate /// static Iterable Foldable.FindAllBack(Func predicate, K ta) => Subtype.FindAllBack(predicate, Supertype.Transform(ta)); /// /// Computes the sum of the numbers of a structure. /// static A Foldable.Sum(K ta) => Subtype.Sum(Supertype.Transform(ta)); /// /// Computes the product of the numbers of a structure. /// static A Foldable.Product(K ta) => Subtype.Product(Supertype.Transform(ta)); /// /// Get the head item in the foldable or `None` /// static Option Foldable.Head(K ta) => Subtype.Head(Supertype.Transform(ta)); /// /// Get the head item in the foldable or `None` /// static Option Foldable.Last(K ta) => Subtype.Last(Supertype.Transform(ta)); /// /// Map each element of a structure to an 'Applicative' action, evaluate these /// actions from left to right, and ignore the results. For a version that /// doesn't ignore the results see `Traversable.traverse`. /// static K Foldable.Iter(Func> f, K ta) => Subtype.Iter(f, Supertype.Transform(ta)); /// /// Map each element of a structure to an action, evaluate these /// actions from left to right, and ignore the results. For a version that /// doesn't ignore the results see `Traversable.traverse`. /// static Unit Foldable.Iter(Action f, K ta) => Subtype.Iter(f, Supertype.Transform(ta)); /// /// Map each element of a structure to an action, evaluate these /// actions from left to right, and ignore the results. For a version that /// doesn't ignore the results see `Traversable.traverse`. /// static Unit Foldable.Iter(Action f, K ta) => Subtype.Iter(f, Supertype.Transform(ta)); /// /// Find the minimum value in the structure /// static Option Foldable.Min(K ta) => Subtype.Min(Supertype.Transform(ta)); /// /// Find the minimum value in the structure /// static Option Foldable.Min(K ta) => Subtype.Min(Supertype.Transform(ta)); /// /// Find the maximum value in the structure /// static Option Foldable.Max(K ta) => Subtype.Max(Supertype.Transform(ta)); /// /// Find the maximum value in the structure /// static Option Foldable.Max(K ta) => Subtype.Max(Supertype.Transform(ta)); /// /// Find the minimum value in the structure /// static A Foldable.Min(K ta, A initialMin) => Subtype.Min(Supertype.Transform(ta), initialMin); /// /// Find the minimum value in the structure /// static A Foldable.Min(K ta, A initialMin) => Subtype.Min(Supertype.Transform(ta), initialMin); /// /// Find the maximum value in the structure /// static A Foldable.Max(K ta, A initialMax) => Subtype.Max(Supertype.Transform(ta), initialMax); /// /// Find the maximum value in the structure /// static A Foldable.Max(K ta, A initialMax) => Subtype.Max(Supertype.Transform(ta), initialMax); /// /// Find the average of all the values in the structure /// static A Foldable.Average(K ta) => Subtype.Average(Supertype.Transform(ta)); /// /// Find the average of all the values in the structure /// static B Foldable.Average(Func f, K ta) => Subtype.Average(f, Supertype.Transform(ta)); /// /// Find the element at the specified index or `None` if out of range /// static Option Foldable.At(K ta, Index index) => Subtype.At(Supertype.Transform(ta), index); /// /// Partition a foldable into two sequences based on a predicate /// /// Predicate function /// Foldable structure /// Bound value type /// Partitioned structure static (Seq True, Seq False) Foldable.Partition(Func f, K ta) => Subtype.Partition(f, Supertype.Transform(ta)); } } ================================================ FILE: LanguageExt.Core/Deriving/Functor.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public static partial class Deriving { /// /// Derived functor implementation /// /// Super-type wrapper around the subtype /// The subtype that the supertype type 'wraps' public interface Functor : Functor, Traits.Natural, Traits.CoNatural where Subtype : Functor where Supertype : Functor { /// /// Functor map operation /// /// Mapping function /// Functor structure to map /// Input bound value type /// Output bound value type /// Mapped functor static K Functor.Map(Func f, K ma) => Supertype.CoTransform(Subtype.Map(f, Supertype.Transform(ma))); } } ================================================ FILE: LanguageExt.Core/Deriving/Maybe/MonadIO.cs ================================================ /* using System; using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt; public static partial class Deriving { public static partial class Maybe { /// /// Derived `MonadIO` implementation /// /// Super-type wrapper around the subtype /// The subtype that the supertype type 'wraps' // ReSharper disable once MemberHidesStaticFromOuterClass public interface MonadIO : Monad, MonadIO where Subtype : Traits.Maybe.MonadIO, Monad where Supertype : MonadIO, Monad { /// /// Lift an IO operation into the `Self` monad /// /// IO structure to lift /// Bound value type /// Monad with an `IO` structure lifted into it /// If this method isn't overloaded in /// the inner monad or any monad in the stack on the way to the inner monad, /// then it will throw an exception. static K Traits.Maybe.MonadIO.LiftIO(K ma) => Supertype.CoTransform(Subtype.LiftIO(ma)); /// /// Lift an IO operation into the `Self` monad /// /// IO structure to lift /// Bound value type /// Monad with an `IO` structure lifted into it /// If this method isn't overloaded in /// the inner monad or any monad in the stack on the way to the inner monad, /// then it will throw an exception. static K Traits.Maybe.MonadIO.LiftIO(IO ma) => Supertype.CoTransform(Subtype.LiftIO(ma)); } } } */ ================================================ FILE: LanguageExt.Core/Deriving/Maybe/MonadUnliftIO.cs ================================================ /* using System; using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt; public static partial class Deriving { public static partial class Maybe { /// /// Derived `MonadIO` implementation /// /// Super-type wrapper around the subtype /// The subtype that the supertype type 'wraps' // ReSharper disable once MemberHidesStaticFromOuterClass public interface MonadUnliftIO : MonadIO, MonadUnliftIO where Subtype : Traits.Maybe.MonadUnliftIO, MonadIO where Supertype : MonadUnliftIO, MonadIO { /// /// Extract the inner `IO` monad from the `Self` structure provided /// /// `Self` structure to extract the `IO` monad from /// Bound value type /// `Self` structure with the `IO` structure as the bound value static K> Traits.Maybe.MonadUnliftIO.ToIO(K ma) => Supertype.CoTransform(Subtype.ToIO(Supertype.Transform(ma))); /// /// Map the inner `IO` monad within the `Self` structure provided /// /// `Self` structure to extract the `IO` monad from /// `IO` structure mapping function /// Input bound value type /// Output bound value type /// `Self` structure that has had its inner `IO` monad mapped /// If this method isn't overloaded in /// the inner monad or any monad in the stack on the way to the inner monad /// then it will throw an exception. static K Traits.Maybe.MonadUnliftIO.MapIO(K ma, Func, IO> f) => Supertype.CoTransform(Subtype.MapIO(Supertype.Transform(ma), f)); } } } */ ================================================ FILE: LanguageExt.Core/Deriving/Monad.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public static partial class Deriving { /// /// Derived monad implementation /// /// Super-type wrapper around the subtype /// The subtype that the supertype type 'wraps' public interface Monad : Applicative, Monad where Subtype : Monad where Supertype : Monad { /// /// Monad bind operation. Chains two operations together in sequence. /// /// First monad to run /// Bind function that yields the second monad to run /// Input bound value type /// Output bound value type /// Composed chained monad operation static K Monad.Bind(K ma, Func> f) => Supertype.CoTransform(Subtype.Bind(Supertype.Transform(ma), x => Supertype.Transform(f(x)))); static K Monad.Flatten(K> mma) => Supertype.CoTransform(Subtype.Flatten(Supertype.Transform(mma.Map(Supertype.Transform)))); static K Monad.Recur( A value, Func>> f) => Supertype.CoTransform(Subtype.Recur(value, x => Supertype.Transform(f(x)))); } } ================================================ FILE: LanguageExt.Core/Deriving/MonadIO.cs ================================================ using System; using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt; public static partial class Deriving { /// /// Derived `MonadIO` implementation /// /// Super-type wrapper around the subtype /// The subtype that the supertype type 'wraps' public interface MonadIO : Monad, MonadIO where Subtype : MonadIO, Monad where Supertype : MonadIO, Monad { /// /// Lift an IO operation into the `Self` monad /// /// IO structure to lift /// Bound value type /// Monad with an `IO` structure lifted into it /// If this method isn't overloaded in /// the inner monad or any monad in the stack on the way to the inner monad /// then it will throw an exception. static K Traits.MonadIO.LiftIO(K ma) => Supertype.CoTransform(Subtype.LiftIO(ma)); /// /// Lift an IO operation into the `Self` monad /// /// IO structure to lift /// Bound value type /// Monad with an `IO` structure lifted into it /// If this method isn't overloaded in /// the inner monad or any monad in the stack on the way to the inner monad /// then it will throw an exception. static K Traits.MonadIO.LiftIO(IO ma) => Supertype.CoTransform(Subtype.LiftIO(ma)); } } ================================================ FILE: LanguageExt.Core/Deriving/MonadT.cs ================================================ using LanguageExt.Traits; namespace LanguageExt; public static partial class Deriving { /// /// Derived monad-transformer implementation /// /// Super-type wrapper around the subtype /// The subtype that the supertype type 'wraps' public interface MonadT : Monad, MonadT where Subtype : MonadT where Supertype : MonadT, MonadT where M : Monad { static K MonadT.Lift(K ma) => Supertype.CoTransform(Subtype.Lift(ma)); } } ================================================ FILE: LanguageExt.Core/Deriving/MonadUnliftIO.cs ================================================ using System; using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt; public static partial class Deriving { /// /// Derived `MonadUnliftIO` implementation /// /// Super-type wrapper around the subtype /// The subtype that the supertype type 'wraps' public interface MonadUnliftIO : MonadIO, MonadUnliftIO where Subtype : MonadUnliftIO, MonadIO where Supertype : MonadUnliftIO, Monad { /// /// Extract the inner `IO` monad from the `Self` structure provided /// /// `Self` structure to extract the `IO` monad from /// Bound value type /// `Self` structure with the `IO` structure as the bound value static K> Traits.MonadUnliftIO.ToIO(K ma) => Supertype.CoTransform(Subtype.ToIO(Supertype.Transform(ma))); /// /// Map the inner `IO` monad within the `Self` structure provided /// /// `Self` structure to extract the `IO` monad from /// `IO` structure mapping function /// Input bound value type /// Output bound value type /// `Self` structure that has had its inner `IO` monad mapped /// If this method isn't overloaded in /// the inner monad or any monad in the stack on the way to the inner-monad /// then it will throw an exception. static K Traits.MonadUnliftIO.MapIO(K ma, Func, IO> f) => Supertype.CoTransform(Subtype.MapIO(Supertype.Transform(ma), f)); /// /// Creates a local cancellation environment /// /// /// A local cancellation environment stops other IO computations, that rely on the same /// environmental cancellation token, from being taken down by a regional cancellation. /// /// If an `IO.cancel` is invoked locally then it will still create an exception that /// propagates upwards and so catching cancellations is still important. /// /// Computation to run within the local context /// Bound value /// Result of the computation static K MonadUnliftIO.LocalIO(K ma) => Supertype.CoTransform(Subtype.LocalIO(Supertype.Transform(ma))); /// /// Make this IO computation run on the `SynchronizationContext` that was captured at the start /// of the IO chain (i.e. the one embedded within the `EnvIO` environment that is passed through /// all IO computations) /// static K MonadUnliftIO.PostIO(K ma) => Supertype.CoTransform(Subtype.PostIO(Supertype.Transform(ma))); /// /// Timeout operation if it takes too long /// static K MonadUnliftIO.TimeoutIO(K ma, TimeSpan timeout) => Supertype.CoTransform(Subtype.TimeoutIO(Supertype.Transform(ma), timeout)); /// /// The IO monad tracks resources automatically; this creates a local resource environment /// to run this computation in. Once the computation is completed, any resources acquired /// are automatically released. Imagine this as the ultimate `using` statement. /// static K MonadUnliftIO.BracketIO(K ma) => Supertype.CoTransform(Subtype.BracketIO(Supertype.Transform(ma))); /// /// When acquiring, using, and releasing various resources, it can be quite convenient to write a function to manage /// the acquisition and releasing, taking a function of the acquired value that specifies an action to be performed /// in between. /// /// Resource acquisition /// Function to use the acquired resource /// Function to invoke to release the resource static K MonadUnliftIO.BracketIO( K Acq, Func> Use, Func> Fin) => Supertype.CoTransform(Subtype.BracketIO(Supertype.Transform(Acq), Use, Fin)); /// /// When acquiring, using, and releasing various resources, it can be quite convenient to write a function to manage /// the acquisition and releasing, taking a function of the acquired value that specifies an action to be performed /// in between. /// /// Resource acquisition /// Function to use the acquired resource /// Function to run to handle any exceptions /// Function to invoke to release the resource static K MonadUnliftIO.BracketIO( K Acq, Func> Use, Func> Catch, Func> Fin) => Supertype.CoTransform(Subtype.BracketIO(Supertype.Transform(Acq), Use, Catch, Fin)); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Repeating the effect // /// /// Keeps repeating the computation forever, or until an error occurs /// /// /// Any resources acquired within a repeated IO computation will automatically be released. This also means you can't /// acquire resources and return them from within a repeated computation. /// /// The result of the last invocation static K MonadUnliftIO.RepeatIO(K ma) => Supertype.CoTransform(Subtype.RepeatIO(Supertype.Transform(ma))); /// /// Keeps repeating the computation until the scheduler expires, or an error occurs /// /// /// Any resources acquired within a repeated IO computation will automatically be released. This also means you can't /// acquire resources and return them from within a repeated computation. /// /// Scheduler strategy for repeating /// The result of the last invocation static K MonadUnliftIO.RepeatIO( K ma, Schedule schedule) => Supertype.CoTransform(Subtype.RepeatIO(Supertype.Transform(ma), schedule)); /// /// Keeps repeating the computation until the predicate returns false, or an error occurs /// /// /// Any resources acquired within a repeated IO computation will automatically be released. This also means you can't /// acquire resources and return them from within a repeated computation. /// /// Keep repeating while this predicate returns `true` for each computed value /// The result of the last invocation static K MonadUnliftIO.RepeatWhileIO( K ma, Func predicate) => Supertype.CoTransform(Subtype.RepeatWhileIO(Supertype.Transform(ma), predicate)); /// /// Keeps repeating the computation until the scheduler expires, or the predicate returns false, or an error occurs /// /// /// Any resources acquired within a repeated IO computation will automatically be released. This also means you can't /// acquire resources and return them from within a repeated computation. /// /// Scheduler strategy for repeating /// Keep repeating while this predicate returns `true` for each computed value /// The result of the last invocation static K MonadUnliftIO.RepeatWhileIO( K ma, Schedule schedule, Func predicate) => Supertype.CoTransform(Subtype.RepeatWhileIO(Supertype.Transform(ma), schedule, predicate)); /// /// Keeps repeating the computation until the predicate returns true, or an error occurs /// /// /// Any resources acquired within a repeated IO computation will automatically be released. This also means you can't /// acquire resources and return them from within a repeated computation. /// /// Keep repeating until this predicate returns `true` for each computed value /// The result of the last invocation static K MonadUnliftIO.RepeatUntilIO( K ma, Func predicate) => Supertype.CoTransform(Subtype.RepeatUntilIO(Supertype.Transform(ma), predicate)); /// /// Keeps repeating the computation until the scheduler expires, or the predicate returns true, or an error occurs /// /// /// Any resources acquired within a repeated IO computation will automatically be released. This also means you can't /// acquire resources and return them from within a repeated computation. /// /// Scheduler strategy for repeating /// Keep repeating until this predicate returns `true` for each computed value /// The result of the last invocation static K MonadUnliftIO.RepeatUntilIO( K ma, Schedule schedule, Func predicate) => Supertype.CoTransform(Subtype.RepeatUntilIO(Supertype.Transform(ma), schedule, predicate)); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Retrying the effect when it fails // /// /// Retry if the IO computation fails /// /// /// This variant will retry forever /// /// /// Any resources acquired within a retrying IO computation will automatically be released *if* the operation fails. /// So, successive retries will not grow the acquired resources on each retry iteration. Any successful operation that /// acquires resources will have them tracked in the usual way. /// static K MonadUnliftIO.RetryIO(K ma) => Supertype.CoTransform(Subtype.RetryIO(Supertype.Transform(ma))); /// /// Retry if the IO computation fails /// /// /// This variant will retry until the schedule expires /// /// /// Any resources acquired within a retrying IO computation will automatically be released *if* the operation fails. /// So, successive retries will not grow the acquired resources on each retry iteration. Any successful operation that /// acquires resources will have them tracked in the usual way. /// static K MonadUnliftIO.RetryIO( K ma, Schedule schedule) => Supertype.CoTransform(Subtype.RetryIO(Supertype.Transform(ma), schedule)); /// /// Retry if the IO computation fails /// /// /// This variant will keep retrying whilst the predicate returns `true` for the error generated at each iteration; /// at which point the last raised error will be thrown. /// /// /// Any resources acquired within a retrying IO computation will automatically be released *if* the operation fails. /// So, successive retries will not grow the acquired resources on each retry iteration. Any successful operation that /// acquires resources will have them tracked in the usual way. /// static K MonadUnliftIO.RetryWhileIO( K ma, Func predicate) => Supertype.CoTransform(Subtype.RetryWhileIO(Supertype.Transform(ma), predicate)); /// /// Retry if the IO computation fails /// /// /// This variant will keep retrying whilst the predicate returns `true` for the error generated at each iteration; /// or, until the schedule expires; at which point the last raised error will be thrown. /// /// /// Any resources acquired within a retrying IO computation will automatically be released *if* the operation fails. /// So, successive retries will not grow the acquired resources on each retry iteration. Any successful operation that /// acquires resources will have them tracked in the usual way. /// static K MonadUnliftIO.RetryWhileIO( K ma, Schedule schedule, Func predicate) => Supertype.CoTransform(Subtype.RetryWhileIO(Supertype.Transform(ma), schedule, predicate)); /// /// Retry if the IO computation fails /// /// /// This variant will keep retrying until the predicate returns `true` for the error generated at each iteration; /// at which point the last raised error will be thrown. /// /// /// Any resources acquired within a retrying IO computation will automatically be released *if* the operation fails. /// So, successive retries will not grow the acquired resources on each retry iteration. Any successful operation that /// acquires resources will have them tracked in the usual way. /// static K MonadUnliftIO.RetryUntilIO( K ma, Func predicate) => Supertype.CoTransform(Subtype.RetryUntilIO(Supertype.Transform(ma), predicate)); /// /// Retry if the IO computation fails /// /// /// This variant will keep retrying until the predicate returns `true` for the error generated at each iteration; /// or, until the schedule expires; at which point the last raised error will be thrown. /// /// /// Any resources acquired within a retrying IO computation will automatically be released *if* the operation fails. /// So, successive retries will not grow the acquired resources on each retry iteration. Any successful operation that /// acquires resources will have them tracked in the usual way. /// static K MonadUnliftIO.RetryUntilIO( K ma, Schedule schedule, Func predicate) => Supertype.CoTransform(Subtype.RetryUntilIO(Supertype.Transform(ma), schedule, predicate)); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Folding // static K MonadUnliftIO.FoldIO( K ma, Schedule schedule, S initialState, Func folder) => Supertype.CoTransform(Subtype.FoldIO(Supertype.Transform(ma), schedule, initialState, folder)); static K MonadUnliftIO.FoldIO( K ma, S initialState, Func folder) => Supertype.CoTransform(Subtype.FoldIO(Supertype.Transform(ma), initialState, folder)); static K MonadUnliftIO.FoldWhileIO( K ma, Schedule schedule, S initialState, Func folder, Func stateIs) => Supertype.CoTransform(Subtype.FoldWhileIO(Supertype.Transform(ma), schedule, initialState, folder, stateIs)); static K MonadUnliftIO.FoldWhileIO( K ma, S initialState, Func folder, Func stateIs) => Supertype.CoTransform(Subtype.FoldWhileIO(Supertype.Transform(ma), initialState, folder, stateIs)); static K MonadUnliftIO.FoldWhileIO( K ma, Schedule schedule, S initialState, Func folder, Func valueIs) => Supertype.CoTransform(Subtype.FoldWhileIO(Supertype.Transform(ma), schedule, initialState, folder, valueIs)); static K MonadUnliftIO.FoldWhileIO( K ma, S initialState, Func folder, Func valueIs) => Supertype.CoTransform(Subtype.FoldWhileIO(Supertype.Transform(ma), initialState, folder, valueIs)); static K MonadUnliftIO.FoldWhileIO( K ma, Schedule schedule, S initialState, Func folder, Func<(S State, A Value), bool> predicate) => Supertype.CoTransform(Subtype.FoldWhileIO(Supertype.Transform(ma), schedule, initialState, folder, predicate)); static K MonadUnliftIO.FoldWhileIO( K ma, S initialState, Func folder, Func<(S State, A Value), bool> predicate) => Supertype.CoTransform(Subtype.FoldWhileIO(Supertype.Transform(ma), initialState, folder, predicate)); static K MonadUnliftIO.FoldUntilIO( K ma, Schedule schedule, S initialState, Func folder, Func stateIs) => Supertype.CoTransform(Subtype.FoldUntilIO(Supertype.Transform(ma), schedule, initialState, folder, stateIs)); static K MonadUnliftIO.FoldUntilIO( K ma, S initialState, Func folder, Func stateIs) => Supertype.CoTransform(Subtype.FoldUntilIO(Supertype.Transform(ma), initialState, folder, stateIs)); static K MonadUnliftIO.FoldUntilIO( K ma, Schedule schedule, S initialState, Func folder, Func valueIs) => Supertype.CoTransform(Subtype.FoldUntilIO(Supertype.Transform(ma), schedule, initialState, folder, valueIs)); static K MonadUnliftIO.FoldUntilIO( K ma, S initialState, Func folder, Func valueIs) => Supertype.CoTransform(Subtype.FoldUntilIO(Supertype.Transform(ma), initialState, folder, valueIs)); static K MonadUnliftIO.FoldUntilIO( K ma, S initialState, Func folder, Func<(S State, A Value), bool> predicate) => Supertype.CoTransform(Subtype.FoldUntilIO(Supertype.Transform(ma), initialState, folder, predicate)); static K MonadUnliftIO.FoldUntilIO( K ma, Schedule schedule, S initialState, Func folder, Func<(S State, A Value), bool> predicate) => Supertype.CoTransform(Subtype.FoldUntilIO(Supertype.Transform(ma), schedule, initialState, folder, predicate)); } } ================================================ FILE: LanguageExt.Core/Deriving/MonoidK.cs ================================================ using LanguageExt.Traits; namespace LanguageExt; public static partial class Deriving { /// /// A monoid for higher-kinds /// /// Higher kind public interface MonoidK : MonoidK, SemigroupK where Supertype : MonoidK where Subtype : MonoidK { /// /// Identity /// static K MonoidK.Empty() => Supertype.CoTransform(Subtype.Empty()); } } ================================================ FILE: LanguageExt.Core/Deriving/Readable.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public static partial class Deriving { /// /// Derived `Readable` implementation /// /// Super-type wrapper around the subtype /// Reader environment /// The subtype that the supertype type 'wraps' public interface Readable : Readable, Traits.Natural, Traits.CoNatural where Supertype : Readable, Readable where Subtype : Readable { static K Readable.Ask => Supertype.CoTransform(Subtype.Ask); static K Readable.Asks(Func f) => Supertype.CoTransform(Subtype.Asks(f)); static K Readable.Local(Func f, K ma) => Supertype.CoTransform(Supertype.Transform(ma).Local(f)); } } ================================================ FILE: LanguageExt.Core/Deriving/SemigroupK.cs ================================================ using LanguageExt.Traits; namespace LanguageExt; public static partial class Deriving { /// /// Derived equivalent of semigroups for working with higher-kinded types /// public interface SemigroupK : SemigroupK, Traits.Natural, Traits.CoNatural where Supertype : SemigroupK where Subtype : SemigroupK { /// /// An associative binary operation. /// /// The first operand to the operation /// The second operand to the operation /// The result of the operation static K SemigroupK.Combine(K lhs, K rhs) => Supertype.CoTransform(Supertype.Transform(lhs).Combine(Supertype.Transform(rhs))); } } ================================================ FILE: LanguageExt.Core/Deriving/Stateful.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public static partial class Deriving { public interface Stateful : Stateful, Traits.Natural, Traits.CoNatural where Supertype : Stateful, Stateful where Subtype : Stateful { static K Stateful.Put(S value) => Supertype.CoTransform(Subtype.Put(value)); static K Stateful.Modify(Func modify) => Supertype.CoTransform(Subtype.Modify(modify)); static K Stateful.Gets(Func f) => Supertype.CoTransform(Subtype.Gets(f)); static K Stateful.Get => Supertype.CoTransform(Subtype.Get); } } ================================================ FILE: LanguageExt.Core/Deriving/Traversable.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public static partial class Deriving { /// /// Functors representing data structures that can be transformed to structures of the same /// shape by performing an `Applicative` (or, therefore, `Monad`) action on each element from /// left to right. /// /// A more detailed description of what same shape means, the various methods, how traversals /// are constructed, and example advanced use-cases can be found in the Overview section of Data.Traversable. /// public interface Traversable : Functor, Foldable, Traversable where Supertype : Traversable, Traversable where Subtype : Traversable { static K> Traversable.Traverse(Func> f, K ta) => Subtype.Traverse(f, Supertype.Transform(ta)).Map(Supertype.CoTransform); static K> Traversable.TraverseM(Func> f, K ta) => Subtype.TraverseM(f, Supertype.Transform(ta)).Map(Supertype.CoTransform); static K> Traversable.Sequence(K> ta) => Subtype.Sequence(Supertype.Transform(ta)).Map(Supertype.CoTransform); static K> Traversable.SequenceM(K> ta) => Subtype.SequenceM(Supertype.Transform(ta)).Map(Supertype.CoTransform); static K> Traversable.TraverseDefault(Func> f, K ta) => Subtype.TraverseDefault(f, Supertype.Transform(ta)).Map(Supertype.CoTransform); } } ================================================ FILE: LanguageExt.Core/Deriving/Writable.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public static partial class Deriving { public interface Writable : Writable, Traits.Natural, Traits.CoNatural where Supertype : Writable, Writable where Subtype : Writable where W : Monoid { /// /// Tell is an action that produces the writer output /// /// Item to tell /// Writer type /// Structure with the told item static K Writable.Tell(W item) => Supertype.CoTransform(Subtype.Tell(item)); /// /// Writes an item and returns a value at the same time /// static K Writable.Listen(K ma) => Supertype.CoTransform(Subtype.Listen(Supertype.Transform(ma))); /// /// `Pass` is an action that executes the `action`, which returns a value and a /// function; it then returns the value with the output having been applied to /// the function. /// /// /// For usage, see `Writer.censor` for how it's used to filter the output. /// static K Writable.Pass(K Function)> action) => Supertype.CoTransform(Subtype.Pass(Supertype.Transform(action))); } } ================================================ FILE: LanguageExt.Core/Effects/Eff/Eff no runtime/Eff.Module.cs ================================================ using System; using LanguageExt.Common; using System.Threading.Tasks; using System.Diagnostics.Contracts; using System.Runtime.CompilerServices; using static LanguageExt.Prelude; namespace LanguageExt; public partial class Eff { /// /// Construct a successful effect with a pure value /// /// Pure value to construct the monad with /// Bound value type /// Synchronous IO monad that captures the pure value [Pure, MethodImpl(Opt.Default)] public static Eff Success(A value) => Eff.Pure(value); /// /// Construct a failed effect /// /// Error that represents the failure /// Bound value type /// Synchronous IO monad that captures the failure [Pure, MethodImpl(Opt.Default)] public static Eff Fail(Error error) => Eff.Fail(error); /// /// Unit effect /// public static Eff unit() => unitEff; /// /// Create a new cancellation context and run the provided Aff in that context /// /// Operation to run in the next context /// Runtime environment /// Bound value type /// An asynchronous effect that captures the operation running in context public static Eff localCancel(Eff ma) => ma.LocalIO().As(); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Lifting // /// /// Lift an effect into the `Eff` monad /// [Pure, MethodImpl(Opt.Default)] public static Eff lift(Func> f) => Eff.Lift(f); /// /// Lift an effect into the `Eff` monad /// [Pure, MethodImpl(Opt.Default)] public static Eff lift(Func> f) => Eff.Lift(f); /// /// Lift an effect into the `Eff` monad /// [Pure, MethodImpl(Opt.Default)] public static Eff lift(Func> f) => +envIO.Bind(e => Eff.Lift(() => f(e))); /// /// Lift an effect into the `Eff` monad /// [Pure, MethodImpl(Opt.Default)] public static Eff lift(Func f) => Eff.Lift(f); /// /// Lift an effect into the `Eff` monad /// [Pure, MethodImpl(Opt.Default)] public static Eff lift(Func f) => +envIO.Bind(e => Eff.Lift(() => f(e))); /// /// Lift an effect into the `Eff` monad /// [Pure, MethodImpl(Opt.Default)] public static Eff lift(Func> f) => Eff.LiftIO(f); /// /// Lift an effect into the `Eff` monad /// [Pure, MethodImpl(Opt.Default)] public static Eff lift(Func> f) => +envIO.Bind(e => Eff.LiftIO(() => f(e))); /// /// Lift an effect into the `Eff` monad /// [Pure, MethodImpl(Opt.Default)] public static Eff lift(Func>> f) => Eff.LiftIO(f); /// /// Lift an effect into the `Eff` monad /// [Pure, MethodImpl(Opt.Default)] public static Eff lift(Func>> f) => +envIO.Bind(e => Eff.LiftIO(() => f(e))); /// /// Lift an effect into the `Eff` monad /// [Pure, MethodImpl(Opt.Default)] public static Eff lift(IO ma) => Eff.LiftIO(ma); } ================================================ FILE: LanguageExt.Core/Effects/Eff/Eff no runtime/Eff.Monad.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using LanguageExt.Async.Linq; using LanguageExt.Common; using LanguageExt.Effects; using LanguageExt.Traits; namespace LanguageExt; public partial class Eff : MonadUnliftIO, Final, Fallible, Readable, MonoidK, Alternative, Natural>, CoNatural> { static K Monad.Bind(K ma, Func> f) => new Eff(ma.As().effect.Bind(f)); static K Monad.Recur(A value, Func>> f) => lift(async env => { while (true) { var mnext = await f(value).As().RunAsync(env); if (mnext.IsFail) return Fin.Fail(mnext.FailValue); var next = (Next)mnext; if (next.IsDone) return Fin.Succ(next.Done); value = next.Loop; } }); static K Functor.Map(Func f, K ma) => new Eff(ma.As().effect.Map(f)); static K Applicative.Pure(A value) => Eff.Pure(value); static K Applicative.Apply(K> mf, K ma) => new Eff(mf.As().effect.Apply(ma.As().effect)); static K Applicative.Apply(K> mf, Memo ma) => new Eff(mf.As().effect.Apply(Memo.transform, A>(ma)).As()); static K Applicative.Actions(IterableNE> fas) => new Eff(fas.Map(fa => fa.As().effect.Kind()).Actions().As()); static K MonoidK.Empty() => Eff.Fail(Errors.None); static K Alternative.Empty() => Eff.Fail(Errors.None); static K Choice.Choose(K ma, K mb) => new Eff(ma.As().effect.Choose(mb.As().effect).As()); static K Choice.Choose(K ma, Memo mb) => new Eff(ma.As().effect.Choose(Memo.transform, A>(mb)).As()); static K Readable.Asks(Func f) => new Eff(Readable.asks, MinRT, A>(f).As()); static K Readable.Local(Func f, K ma) => new Eff(Readable.local(f, ma.As().effect).As()); static K MonadIO.LiftIO(IO ma) => Eff.LiftIO(ma); static K> MonadUnliftIO.ToIO(K ma) => new Eff>(ma.As().effect.ToIO().As()); static K MonadUnliftIO.MapIO(K ma, Func, IO> f) => new Eff(ma.As().effect.MapIO(f).As()); static K Fallible.Fail(Error error) => Eff.Fail(error); static K Fallible.Catch( K fa, Func Predicate, Func> Fail) => new Eff(fa.As().effect.Catch(Predicate, e => Fail(e).As().effect).As()); static K SemigroupK.Combine(K lhs, K rhs) => lhs.Choose(rhs); static K Final.Finally(K fa, K @finally) => new Eff(fa.As().effect.Finally(@finally.As().effect).As()); static K, A> Natural>.Transform(K fa) => fa.As().effect; static K CoNatural>.CoTransform(K, A> fa) => new Eff(fa.As()); } ================================================ FILE: LanguageExt.Core/Effects/Eff/Eff no runtime/Eff.cs ================================================ using System; using System.Collections.Generic; using LanguageExt.Common; using System.Diagnostics.Contracts; using System.Linq; using System.Runtime.CompilerServices; using System.Threading.Tasks; using LanguageExt.Effects; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; /// /// This monad is used to encapsulate side effects and exception capture /// /// Runtime type /// Bound value type public record Eff(Eff effect) : Fallible>, K, Alternative>, MonoidK>, Final>, Deriving.Readable, A, ReaderT>, Deriving.MonadUnliftIO, ReaderT>, Natural, Eff>, CoNatural, Eff> { //////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Lifting // /// /// Lift a value into the `Eff` monad /// [Pure, MethodImpl(Opt.Default)] public static Eff Pure(A value) => new(Eff.Pure(value)); /// /// Lift a failure into the `Eff` monad /// [Pure, MethodImpl(Opt.Default)] public static Eff Fail(Error error) => new(Eff.Fail(error)); /// /// Convert to an `Eff` monad with a runtime /// public Eff WithRuntime() => MonadIO.liftIO, A>(effect.RunIO(new MinRT())).As(); /// /// Lift an effect into the `Eff` monad /// [Pure, MethodImpl(Opt.Default)] public static Eff Lift(Func> f) => new(Eff.Lift(f)); /// /// Lift an effect into the `Eff` monad /// [Pure, MethodImpl(Opt.Default)] public static Eff Lift(Func> f) => new(Eff.Lift(f)); /// /// Lift an effect into the `Eff` monad /// [Pure, MethodImpl(Opt.Default)] public static Eff Lift(Func f) => new(Eff.Lift(f)); /// /// Lift an effect into the `Eff` monad /// [Pure, MethodImpl(Opt.Default)] public static Eff LiftIO(Func> f) => new(Eff.LiftIO(f)); /// /// Lift an effect into the `Eff` monad /// [Pure, MethodImpl(Opt.Default)] public static Eff LiftIO(Func>> f) => new(Eff.LiftIO(rt => f(rt).Map(r => r.ThrowIfFail()))); /// /// Lift an effect into the `Eff` monad /// [Pure, MethodImpl(Opt.Default)] public static Eff LiftIO(Func> f) => new(Eff.LiftIO(f)); /// /// Lift an effect into the `Eff` monad /// [Pure, MethodImpl(Opt.Default)] public static Eff LiftIO(IO ma) => new(Eff.LiftIO(ma)); /// /// Lift an effect into the `Eff` monad /// [Pure, MethodImpl(Opt.Default)] public static Eff Lift(Func> f) => new(Eff.Lift(_ => f())); /// /// Lift an effect into the `Eff` monad /// [Pure, MethodImpl(Opt.Default)] public static Eff Lift(Func> f) => new(Eff.Lift(_ => f())); /// /// Lift an effect into the `Eff` monad /// [Pure, MethodImpl(Opt.Default)] public static Eff Lift(Func f) => new(Eff.Lift(_ => f())); /// /// Lift an effect into the `Eff` monad /// [Pure, MethodImpl(Opt.Default)] public static Eff LiftIO(Func> f) => new(Eff.LiftIO(_ => f())); /// /// Lift an effect into the `Eff` monad /// [Pure, MethodImpl(Opt.Default)] public static Eff LiftIO(Func>> f) => new(Eff.LiftIO(_ => f().Map(r => r.ThrowIfFail()))); //////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Map and map-left // /// /// Maps the `Eff` monad if it's in a success state /// /// Function to map the success value with /// Mapped `Eff` monad [Pure, MethodImpl(Opt.Default)] public Eff Map(Func f) => new(effect.Map(f)); /// /// Maps the `Eff` monad if it's in a success state /// /// Function to map the success value with /// Mapped `Eff` monad [Pure, MethodImpl(Opt.Default)] public Eff Select(Func f) => new(effect.Map(f)); /// /// Maps the `Eff` monad if it's in a success state /// /// Function to map the success value with /// Mapped `Eff` monad [Pure, MethodImpl(Opt.Default)] public Eff MapFail(Func f) => this.Catch(f).As(); /// /// Maps the inner IO monad /// /// Function to map with /// Mapped `Eff` monad [Pure, MethodImpl(Opt.Default)] public Eff MapIO(Func, IO> f) => new(effect.MapIO(f)); //////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Bi-map // /// /// Mapping of either the Success state or the Failure state depending on what /// state this `Eff` monad is in. /// /// Mapping to use if the `Eff` monad if in a success state /// Mapping to use if the `Eff` monad if in a failure state /// Mapped `Eff` monad [Pure, MethodImpl(Opt.Default)] public Eff BiMap(Func Succ, Func Fail) => Map(Succ).Catch(Fail).As(); //////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Matching // /// /// Pattern match the success or failure values and collapse them down to a success value /// /// Success value mapping /// Failure value mapping /// IO in a success state [Pure] public Eff Match(Func Succ, Func Fail) => Map(Succ).Catch(Fail).As(); /// /// Map the failure to a success value /// /// Function to map the fail value /// IO in a success state [Pure, MethodImpl(Opt.Default)] public Eff IfFail(Func Fail) => this.Catch(Fail).As(); /// /// Map the failure to a new IO effect /// /// Function to map the fail value /// IO that encapsulates that IfFail [Pure, MethodImpl(Opt.Default)] public Eff IfFailEff(Func> Fail) => this.Catch(Fail).As(); //////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Filter // /// /// Only allow values through the effect if the predicate returns `true` for the bound value /// /// Predicate to apply to the bound value> /// Filtered IO [Pure, MethodImpl(Opt.Default)] public Eff Filter(Func predicate) => new(effect.Filter(predicate)); /// /// Only allow values through the effect if the predicate returns `true` for the bound value /// /// Predicate to apply to the bound value> /// Filtered IO [Pure, MethodImpl(Opt.Default)] public Eff Where(Func predicate) => new(effect.Where(predicate)); //////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Monadic binding // /// /// Monadic bind operation. This runs the current `Eff` monad and feeds its result to the /// function provided; which in turn returns a new `Eff` monad. This can be thought of as /// chaining IO operations sequentially. /// /// Bind operation /// Composition of this monad and the result of the function provided [Pure, MethodImpl(Opt.Default)] public Eff Bind(Func> f) => new(effect.Bind(x => f(x).effect)); /// /// Monadic bind operation. This runs the current `Eff` monad and feeds its result to the /// function provided; which in turn returns a new `Eff` monad. This can be thought of as /// chaining IO operations sequentially. /// /// Bind operation /// Composition of this monad and the result of the function provided [Pure, MethodImpl(Opt.Default)] public Eff Bind(Func> f) => new(effect.Bind(f)); /// /// Monadic bind operation. This runs the current `Eff` monad and feeds its result to the /// function provided; which in turn returns a new `Eff` monad. This can be thought of as /// chaining IO operations sequentially. /// /// Bind operation /// Composition of this monad and the result of the function provided [Pure, MethodImpl(Opt.Default)] public Eff Bind(Func> f) => new(effect.Bind(f)); /// /// Monadic bind operation. This runs the current `Eff` monad and feeds its result to the /// function provided; which in turn returns a new `Eff` monad. This can be thought of as /// chaining IO operations sequentially. /// /// Bind operation /// Composition of this monad and the result of the function provided [Pure, MethodImpl(Opt.Default)] public Eff Bind(Func> f) => WithRuntime().Bind(f); /// /// Monadic bind operation. This runs the current `Eff` monad and feeds its result to the /// function provided; which in turn returns a new `Eff` monad. This can be thought of as /// chaining IO operations sequentially. /// /// Bind operation /// Composition of this monad and the result of the function provided [Pure, MethodImpl(Opt.Default)] public Eff Bind(Func, B>> f) => Bind(a => f(a).As()); /// /// Monadic bind operation. This runs the current `Eff` monad and feeds its result to the /// function provided; which in turn returns a new `Eff` monad. This can be thought of as /// chaining IO operations sequentially. /// /// Bind operation /// Composition of this monad and the result of the function provided [Pure, MethodImpl(Opt.Default)] public Eff Bind(Func> f) => Bind(a => f(a).As()); /// /// Monadic bind operation. This runs the current `Eff` monad and feeds its result to the /// function provided; which in turn returns a new `Eff` monad. This can be thought of as /// chaining IO operations sequentially. /// /// Bind operation /// Composition of this monad and the result of the function provided [Pure, MethodImpl(Opt.Default)] public Eff Bind(Func> f) => Map(x => f(x).Value); /// /// Monadic bind operation. This runs the current `Eff` monad and feeds its result to the /// function provided; which in turn returns a new `Eff` monad. This can be thought of as /// chaining IO operations sequentially. /// /// Bind operation /// Composition of this monad and the result of the function provided [Pure, MethodImpl(Opt.Default)] public Eff Bind(Func> f) => Bind(x => Fail(f(x).Value)); //////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Monadic binding and projection // /// /// Monadic bind operation. This runs the current `Eff` monad and feeds its result to the /// function provided; which in turn returns a new `Eff` monad. This can be thought of as /// chaining IO operations sequentially. /// /// Bind operation /// Composition of this monad and the result of the function provided [Pure, MethodImpl(Opt.Default)] public Eff SelectMany(Func> bind, Func project) => Bind(x => bind(x).Map(y => project(x, y))); /// /// Monadic bind operation. This runs the current `Eff` monad and feeds its result to the /// function provided; which in turn returns a new `Eff` monad. This can be thought of as /// chaining IO operations sequentially. /// /// Bind operation /// Composition of this monad and the result of the function provided [Pure, MethodImpl(Opt.Default)] public Eff SelectMany(Func> bind, Func project) => Bind(x => bind(x).Map(y => project(x, y))); /// /// Monadic bind operation. This runs the current `Eff` monad and feeds its result to the /// function provided; which in turn returns a new `Eff` monad. This can be thought of as /// chaining IO operations sequentially. /// /// Bind operation /// Composition of this monad and the result of the function provided [Pure, MethodImpl(Opt.Default)] public Eff SelectMany(Func, B>> bind, Func project) => SelectMany(x => bind(x).As(), project); /// /// Monadic bind operation. This runs the current `Eff` monad and feeds its result to the /// function provided; which in turn returns a new `Eff` monad. This can be thought of as /// chaining IO operations sequentially. /// /// Bind operation /// Composition of this monad and the result of the function provided [Pure, MethodImpl(Opt.Default)] public Eff SelectMany(Func> bind, Func project) => SelectMany(x => bind(x).As(), project); /// /// Monadic bind operation. This runs the current `Eff` monad and feeds its result to the /// function provided; which in turn returns a new `Eff` monad. This can be thought of as /// chaining IO operations sequentially. /// /// Bind operation /// Composition of this monad and the result of the function provided [Pure, MethodImpl(Opt.Default)] public Eff SelectMany(Func> bind, Func project) => new(effect.SelectMany(bind, project)); /// /// Monadic bind operation. This runs the current `Eff` monad and feeds its result to the /// function provided; which in turn returns a new `Eff` monad. This can be thought of as /// chaining IO operations sequentially. /// /// Bind operation /// Composition of this monad and the result of the function provided [Pure, MethodImpl(Opt.Default)] public Eff SelectMany(Func> bind, Func project) => new(effect.SelectMany(bind, project)); /// /// Monadic bind operation. This runs the current `Eff` monad and feeds its result to the /// function provided; which in turn returns a new `Eff` monad. This can be thought of as /// chaining IO operations sequentially. /// /// Bind operation /// Composition of this monad and the result of the function provided [Pure, MethodImpl(Opt.Default)] public Eff SelectMany(Func> bind, Func project) => new(effect.SelectMany(bind, project)); /// /// Monadic bind operation. This runs the current `Eff` monad and feeds its result to the /// function provided; which in turn returns a new `Eff` monad. This can be thought of as /// chaining IO operations sequentially. /// /// Bind operation /// Composition of this monad and the result of the function provided [Pure, MethodImpl(Opt.Default)] public Eff SelectMany(Func> bind, Func project) => SelectMany(x => Eff.Fail(bind(x).Value), project); /// /// Monadic bind operation. This runs the current `Eff` monad and feeds its result to the /// function provided; which in turn returns a new `Eff` monad. This can be thought of as /// chaining IO operations sequentially. /// /// Bind operation /// Composition of this monad and the result of the function provided [Pure, MethodImpl(Opt.Default)] public Eff SelectMany(Func> bind, Func project) => new(effect.SelectMany(bind, project)); /// /// Monadic bind operation. This runs the current `Eff` monad and feeds its result to the /// function provided; which in turn returns a new `Eff` monad. This can be thought of as /// chaining IO operations sequentially. /// /// Bind operation /// Composition of this monad and the result of the function provided [Pure, MethodImpl(Opt.Default)] public Eff SelectMany(Func, Unit>> bind, Func project) => new(effect.SelectMany(bind, project)); //////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Conversion operators // /// /// Convert to an `Eff` monad /// [Pure, MethodImpl(Opt.Default)] public static implicit operator Eff(Pure ma) => ma.ToEff(); /// /// Convert to an `Eff` monad /// [Pure, MethodImpl(Opt.Default)] public static implicit operator Eff(Fail ma) => ma.ToEff(); /// /// Convert to an `Eff` monad /// [Pure, MethodImpl(Opt.Default)] public static implicit operator Eff(in Lift ma) => Lift(ma.Function); /// /// Convert to an `Eff` monad /// [Pure, MethodImpl(Opt.Default)] public static implicit operator Eff(in Lift> ma) => Lift(ma.Function); /// /// Convert to an `Eff` monad /// [Pure, MethodImpl(Opt.Default)] public static implicit operator Eff(in Lift ma) => Lift(ma.Function); /// /// Convert to an `Eff` monad /// [Pure, MethodImpl(Opt.Default)] public static implicit operator Eff(in Lift> ma) => Lift(ma.Function); /// /// Convert to an `Eff` monad /// [Pure, MethodImpl(Opt.Default)] public static implicit operator Eff(in Either ma) => ma.Match(Left: Fail, Right: Pure); /// /// Convert to an `Eff` monad /// [Pure, MethodImpl(Opt.Default)] public static implicit operator Eff(in Fin ma) => ma.Match(Succ: Pure, Fail: Fail); /// /// Convert to an `Eff` monad /// [Pure, MethodImpl(Opt.Default)] public static implicit operator Eff(in IO ma) => LiftIO(ma); [Pure, MethodImpl(Opt.Default)] public static implicit operator Eff(in Error fail) => Fail(fail); public override string ToString() => "Eff"; //////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Trait implementations for `Eff` // // It's important to remember that the code below is the trait implementations for `Eff`, and not // related to `Eff` in any way at all. `A` in this instance is the `RT` in `Eff`. // // It is this way to make it easier to work with Eff traits, even if this is a bit ugly. // static K, T> Fallible>.Fail(Error error) => FailEff(error); static K, T> Fallible>.Catch( K, T> ma, Func pred, Func, T>> f) => new Eff( new ReaderT( env => ma.As().RunIO(env).Catch(pred, e => f(e).As().effect.Run(env)))); static K, T> Final>.Finally(K, T> fa, K, X> @finally) => new Eff( new ReaderT( env => fa.As().RunIO(env).Finally(@finally.As().effect.Run(env)))); static K, T> Applicative>.Actions(IterableNE, T>> fas) => new Eff( new ReaderT( rt => fas.Select(fa => fa.RunIO(rt).Kind()).Actions())); static K, T> MonoidK>.Empty() => Eff.Fail(Errors.None); static K, A1> Natural, ReaderT>.Transform(K, A1> fa) => fa.As().effect; static K, T> CoNatural, ReaderT>.CoTransform(K, T> fa) => new Eff(fa.As()); static K, A1> SemigroupK>.Combine(K, A1> lhs, K, A1> rhs) => lhs.Catch(e1 => rhs.Catch(e2 => e1 + e2)); static K, A1> Choice>.Choose(K, A1> lhs, K, A1> rhs) => lhs.Catch(rhs); static K, A1> Choice>.Choose(K, A1> lhs, Memo, A1> rhs) => lhs.Catch(_ => rhs.Value); static K, T> Alternative>.Empty() => Eff.Fail(Errors.None); static K Natural, Eff>.Transform(K, T> fa) => new Eff(fa.As()); static K, T> CoNatural, Eff>.CoTransform(K fa) => fa.As().effect; static K, B> Monad>.Recur(X value, Func, Next>> f) => Eff.lift(async (env, rt) => { while (true) { var mnext = await f(value).As().RunAsync(rt, env); if (mnext.IsFail) return Fin.Fail(mnext.FailValue); var next = (Next)mnext; if (next.IsDone) return Fin.Succ(next.Done); value = next.Loop; } }); //////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Transformer helpers // internal static ReaderT getsM(Func> f) => from e in ReaderT.ask() from r in ReaderT.liftIO(IO.lift(() => f(e)).Flatten()) select r; internal static ReaderT getsIO(Func> f) => from e in ReaderT.ask() from r in ReaderT.liftIO(IO.liftAsync(() => f(e))) select r; internal static ReaderT gets(Func f) => from e in ReaderT.ask() from r in ReaderT.liftIO(IO.lift(() => f(e))) select r; internal static ReaderT gets(Func> f) => from e in ReaderT.ask() from r in ReaderT.liftIO(IO.lift(() => f(e))) select r; internal static ReaderT gets(Func> f) => from e in ReaderT.ask() from r in ReaderT.liftIO(IO.lift(() => f(e))) select r; internal static ReaderT fail(Error value) => ReaderT.liftIO(IO.fail(value)); internal static ReaderT pure(X value) => ReaderT.Pure(value); internal static readonly ReaderT getState = from rt in ReaderT.ask() from io in IO.env select (rt, io); //////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Obsolete // /// /// Lift a value into the `Eff` monad /// [Obsolete("Use either: `Prelude.Pure` or `Eff.Pure`")] [Pure, MethodImpl(Opt.Default)] public static Eff Success(A value) => Pure(value); /// /// Lift a synchronous effect into the `Eff` monad /// [Obsolete("Use either: `Prelude.lift` or `Eff.Lift`")] [Pure, MethodImpl(Opt.Default)] public static Eff Effect(Func f) => Lift(_ => f()); /// /// Lift a synchronous effect into the `Eff` monad /// [Obsolete("Use either: `Prelude.lift` or `Eff.Lift`")] [Pure, MethodImpl(Opt.Default)] public static Eff EffectMaybe(Func> f) => Lift(_ => f()); } ================================================ FILE: LanguageExt.Core/Effects/Eff/Eff no runtime/Extensions/Eff.Extensions.MapApply.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public static partial class EffExtensions { /// /// Functor map operation /// /// /// Unwraps the value within the functor, passes it to the map function `f` provided, and /// then takes the mapped value and wraps it back up into a new functor. /// /// Functor to map /// Mapping function /// Mapped functor public static Eff Map(this Func f, K ma) => Functor.map(f, ma).As(); /// /// Functor map operation /// /// /// Unwraps the value within the functor, passes it to the map function `f` provided, and /// then takes the mapped value and wraps it back up into a new functor. /// /// Functor to map /// Mapping function /// Mapped functor public static Eff Map(this Func f, Eff ma) => Functor.map(f, ma).As(); /// /// Applicative action: runs the first applicative, ignores the result, and returns the second applicative /// public static Eff Action(this Eff ma, K mb) => Applicative.action(ma, mb).As(); /// /// Applicative action: runs the first applicative, ignores the result, and returns the second applicative /// public static Eff Action(this K ma, K mb) => Applicative.action(ma, mb).As(); /// /// Applicative functor apply operation /// /// /// Unwraps the value within the `ma` applicative-functor, passes it to the unwrapped function(s) within `mf`, and /// then takes the resulting value and wraps it back up into a new applicative-functor. /// /// Value(s) applicative functor /// Mapping function(s) /// Mapped applicative functor public static Eff Apply(this Eff> mf, K ma) => Applicative.apply(mf, ma).As(); /// /// Applicative functor apply operation /// /// /// Unwraps the value within the `ma` applicative-functor, passes it to the unwrapped function(s) within `mf`, and /// then takes the resulting value and wraps it back up into a new applicative-functor. /// /// Value(s) applicative functor /// Mapping function(s) /// Mapped applicative functor public static Eff Apply(this K> mf, K ma) => Applicative.apply(mf, ma).As(); } ================================================ FILE: LanguageExt.Core/Effects/Eff/Eff no runtime/Extensions/Eff.Extensions.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics.Contracts; using System.Runtime.CompilerServices; using System.Threading.Tasks; using LanguageExt.Common; using LanguageExt.Effects; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; public static partial class EffExtensions { /// /// Cast type to its Kind /// public static Eff As(this K ma) => (Eff)ma; /// /// Cast type to its Kind /// public static Eff As(this Eff ma) => new(ma); //////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Invoking // /// /// Invoke the effect /// /// /// Returns the result value only /// [Pure, MethodImpl(Opt.Default)] public static Fin Run(this K ma) => ma.As().effect.Run(default); /// /// Invoke the effect /// /// /// Returns the result value only /// [Pure, MethodImpl(Opt.Default)] public static Fin Run(this K ma, EnvIO envIO) => ma.As().effect.Run(default, envIO); /// /// Invoke the effect /// /// /// This is labelled 'unsafe' because it can throw an exception, whereas /// `Run` will capture any errors and return a `Fin` type. /// [Pure, MethodImpl(Opt.Default)] public static A RunUnsafe(this K ma) => ma.As().effect.RunUnsafe(default); /// /// Invoke the effect /// /// /// This is labelled 'unsafe' because it can throw an exception, whereas /// `Run` will capture any errors and return a `Fin` type. /// [Pure, MethodImpl(Opt.Default)] public static A RunUnsafe(this K ma, EnvIO envIO) => ma.As().effect.RunUnsafe(default, envIO); /// /// Invoke the effect to leave the inner IO monad /// [Pure, MethodImpl(Opt.Default)] public static IO RunIO(this K ma) => ma.As().effect.RunIO(default); /// /// Invoke the effect /// /// /// Returns the result value only /// [Pure, MethodImpl(Opt.Default)] public static Task> RunAsync(this K ma) => ma.As().effect.RunAsync(default); /// /// Invoke the effect /// /// /// Returns the result value only /// [Pure, MethodImpl(Opt.Default)] public static Task> RunAsync(this K ma, EnvIO envIO) => ma.As().effect.RunAsync(default, envIO); /// /// Invoke the effect /// /// /// This is labelled 'unsafe' because it can throw an exception, whereas /// `Run` will capture any errors and return a `Fin` type. /// [Pure, MethodImpl(Opt.Default)] public static ValueTask RunUnsafeAsync(this K ma) => ma.As().effect.RunUnsafeAsync(default); /// /// Invoke the effect /// /// /// This is labelled 'unsafe' because it can throw an exception, whereas /// `Run` will capture any errors and return a `Fin` type. /// [Pure, MethodImpl(Opt.Default)] public static ValueTask RunUnsafeAsync(this K ma, EnvIO envIO) => ma.As().effect.RunUnsafeAsync(default, envIO); //////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Monadic join // /// /// Monadic join operator /// /// /// Collapses a nested IO monad so there is no nesting. /// /// Nest IO monad to flatten /// Runtime /// Bound value /// Flattened IO monad public static Eff Flatten(this K> mma) => mma.As().Bind(ma => ma); /// /// Monadic join operator /// /// /// Collapses a nested IO monad so there is no nesting. /// /// Nest IO monad to flatten /// Runtime /// Bound value /// Flattened IO monad public static Eff Flatten(this K> mma) => mma.As().Bind(ma => ma); //////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // SelectMany extensions // /// /// Monadic bind and project with paired IO monads /// public static Eff SelectMany( this (K First, K Second) self, Func<(A First, B Second), K> bind, Func<(A First, B Second), C, D> project) => self.Zip().Bind(ab => bind(ab).Map(c => project(ab, c))).As(); /// /// Monadic bind and project with paired IO monads /// public static Eff SelectMany( this K self, Func First, K Second)> bind, Func project) => self.As().Bind(a => bind(a).Zip().Map(cd => project(a, cd))); /// /// Monadic bind and project with paired IO monads /// public static Eff SelectMany( this (K First, K Second, K Third) self, Func<(A First, B Second, C Third), K> bind, Func<(A First, B Second, C Third), D, E> project) => self.Zip().Bind(ab => bind(ab).Map(c => project(ab, c))).As(); /// /// Monadic bind and project with paired IO monads /// public static Eff SelectMany( this K self, Func First, K Second, K Third)> bind, Func project) => self.As().Bind(a => bind(a).Zip().Map(cd => project(a, cd))); } ================================================ FILE: LanguageExt.Core/Effects/Eff/Eff no runtime/Extensions/Eff.Guard.cs ================================================ using System; using LanguageExt.Common; using static LanguageExt.Prelude; namespace LanguageExt; public static partial class EffGuardExtensions { /// /// Natural transformation to `Eff` /// public static Eff ToEff(this Guard guard) => guard.Flag ? Pure(unit) : Fail(guard.OnFalse()); /// /// Monadic binding support for `Eff` /// public static Eff Bind( this Guard guard, Func> f) => guard.Flag ? f(default).As() : Fail(guard.OnFalse()); /// /// Monadic binding support for `Eff` /// public static Eff SelectMany( this Guard guard, Func> bind, Func project) => guard.Flag ? bind(default).As().Map(b => project(default, b)) : Fail(guard.OnFalse()); } ================================================ FILE: LanguageExt.Core/Effects/Eff/Eff no runtime/Operators/Eff.Operators.Applicative.cs ================================================ using System; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; public static partial class EffExtensions { extension(K self) { /// /// Applicative sequence operator /// public static Eff operator >>> (K ma, K mb) => ma.Action(mb).As(); /// /// Applicative apply operator /// public static Eff operator * (K> mf, K ma) => mf.Apply(ma); /// /// Applicative apply operator /// public static Eff operator * (K ma, K> mf) => mf.Apply(ma); } extension(K self) { /// /// Applicative apply operator /// public static Eff> operator * ( K> mf, K ma) => curry * mf * ma; /// /// Applicative apply operator /// public static Eff> operator * ( K ma, K> mf) => curry * mf * ma; } extension(K self) { /// /// Applicative apply operator /// public static Eff>> operator * ( K> mf, K ma) => curry * mf * ma; /// /// Applicative apply operator /// public static Eff>> operator * ( K ma, K> mf) => curry * mf * ma; } extension(K self) { /// /// Applicative apply operator /// public static Eff>>> operator * ( K> mf, K ma) => curry * mf * ma; /// /// Applicative apply operator /// public static Eff>>> operator * ( K ma, K> mf) => curry * mf * ma; } extension(K self) { /// /// Applicative apply operator /// public static Eff>>>> operator * ( K> mf, K ma) => curry * mf * ma; /// /// Applicative apply operator /// public static Eff>>>> operator * ( K ma, K> mf) => curry * mf * ma; } extension(K self) { /// /// Applicative apply operator /// public static Eff>>>>> operator * ( K> mf, K ma) => curry * mf * ma; /// /// Applicative apply operator /// public static Eff>>>>> operator * ( K ma, K> mf) => curry * mf * ma; } extension(K self) { /// /// Applicative apply operator /// public static Eff>>>>>> operator * ( K> mf, K ma) => curry * mf * ma; /// /// Applicative apply operator /// public static Eff>>>>>> operator * ( K ma, K> mf) => curry * mf * ma; } extension(K self) { /// /// Applicative apply operator /// public static Eff>>>>>>> operator * ( K> mf, K ma) => curry * mf * ma; /// /// Applicative apply operator /// public static Eff>>>>>>> operator * ( K ma, K> mf) => curry * mf * ma; } extension(K self) { /// /// Applicative apply operator /// public static Eff>>>>>>>> operator * ( K> mf, K ma) => curry * mf * ma; /// /// Applicative apply operator /// public static Eff>>>>>>>> operator * ( K ma, K> mf) => curry * mf * ma; } extension(K self) { /// /// Applicative apply operator /// public static Eff>>>>>>>>> operator * ( K> mf, K ma) => curry * mf * ma; /// /// Applicative apply operator /// public static Eff>>>>>>>>> operator *( K ma, K> mf) => curry * mf * ma; } } ================================================ FILE: LanguageExt.Core/Effects/Eff/Eff no runtime/Operators/Eff.Operators.Choice.cs ================================================ using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt; public static partial class EffExtensions { extension(K self) { public static Eff operator |(K lhs, K rhs) => lhs.Choose(rhs).As(); public static Eff operator |(K lhs, Pure rhs) => lhs.Choose(rhs.ToEff()).As(); } extension(K self) { public static Eff operator |(K lhs, K, A> rhs) => lhs.As().WithRuntime().Choose(rhs).As(); } } ================================================ FILE: LanguageExt.Core/Effects/Eff/Eff no runtime/Operators/Eff.Operators.Fallible.cs ================================================ using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt; public static partial class EffExtensions { extension(K self) { public static Eff operator |(K lhs, CatchM rhs) => lhs.Catch(rhs).As(); public static Eff operator |(K lhs, Fail rhs) => lhs.Catch(rhs).As(); public static Eff operator |(K lhs, Error rhs) => lhs.Catch(rhs).As(); } } ================================================ FILE: LanguageExt.Core/Effects/Eff/Eff no runtime/Operators/Eff.Operators.Final.cs ================================================ namespace LanguageExt.Traits; public static partial class EffExtensions { extension(K _) { /// /// Run a `finally` operation after the main operation regardless of whether it succeeds or not. /// /// Primary operation /// Finally operation /// Result of primary operation public static Eff operator |(K lhs, Finally rhs) => lhs.Finally(rhs.Operation).As(); } } ================================================ FILE: LanguageExt.Core/Effects/Eff/Eff no runtime/Operators/Eff.Operators.Functor.cs ================================================ using System; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; public static partial class EffExtensions { extension(K _) { /// /// Functor map operator /// public static Eff operator *(Func f, K ma) => ma.Map(f).As(); /// /// Functor map operator /// public static Eff operator *(K ma, Func f) => ma.Map(f).As(); } extension(K _) { /// /// Functor map operator /// public static Eff> operator * ( Func f, K ma) => curry(f) * ma; /// /// Functor map operator /// public static Eff> operator * ( K ma, Func f) => curry(f) * ma; } extension(K _) { /// /// Functor map operator /// public static Eff>> operator * ( Func f, K ma) => curry(f) * ma; /// /// Functor map operator /// public static Eff>> operator * ( K ma, Func f) => curry(f) * ma; } extension(K _) { /// /// Functor map operator /// public static Eff>>> operator * ( Func f, K ma) => curry(f) * ma; /// /// Functor map operator /// public static Eff>>> operator * ( K ma, Func f) => curry(f) * ma; } extension(K _) { /// /// Functor map operator /// public static Eff>>>> operator * ( Func f, K ma) => curry(f) * ma; /// /// Functor map operator /// public static Eff>>>> operator * ( K ma, Func f) => curry(f) * ma; } extension(K _) { /// /// Functor map operator /// public static Eff>>>>> operator * ( Func f, K ma) => curry(f) * ma; /// /// Functor map operator /// public static Eff>>>>> operator * ( K ma, Func f) => curry(f) * ma; } extension(K _) { /// /// Functor map operator /// public static Eff>>>>>> operator * ( Func f, K ma) => curry(f) * ma; /// /// Functor map operator /// public static Eff>>>>>> operator * ( K ma, Func f) => curry(f) * ma; } extension(K _) { /// /// Functor map operator /// public static Eff>>>>>>> operator * ( Func f, K ma) => curry(f) * ma; /// /// Functor map operator /// public static Eff>>>>>>> operator * ( K ma, Func f) => curry(f) * ma; } extension(K _) { /// /// Functor map operator /// public static Eff>>>>>>>> operator * ( Func f, K ma) => curry(f) * ma; /// /// Functor map operator /// public static Eff>>>>>>>> operator * ( K ma, Func f) => curry(f) * ma; } extension(K _) { /// /// Functor map operator /// public static Eff>>>>>>>>> operator * ( Func f, K ma) => curry(f) * ma; /// /// Functor map operator /// public static Eff>>>>>>>>> operator * ( K ma, Func f) => curry(f) * ma; } } ================================================ FILE: LanguageExt.Core/Effects/Eff/Eff no runtime/Operators/Eff.Operators.Monad.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public static partial class EffExtensions { extension(K self) { /// /// Monad bind operator /// /// Monad to bind /// Binding function /// Mapped monad public static Eff operator >> (K ma, Func> f) => +ma.Bind(f); /// /// Sequentially compose two actions, discarding any value produced by the first, like sequencing operators (such /// as the semicolon) in C#. /// /// First action to run /// Second action to run /// Result of the second action public static Eff operator >> (K lhs, K rhs) => lhs >> (_ => rhs); /// /// Monad bind operator /// /// Monad to bind /// Binding function /// Mapped monad public static Eff operator >> (K ma, Func> f) => +ma.Bind(x => Eff.lift(+f(x))); /// /// Sequentially compose two actions, discarding any value produced by the first, like sequencing operators (such /// as the semicolon) in C#. /// /// First action to run /// Second action to run /// Result of the second action public static Eff operator >> (K lhs, K rhs) => lhs >> (_ => rhs); } extension(K self) { /// /// Sequentially compose two actions. The second action is a unit-returning action, so the result of the /// first action is propagated. /// /// First action to run /// Second action to run /// Result of the first action public static Eff operator >> (K lhs, K rhs) => lhs >> (x => (_ => x) * rhs); /// /// Sequentially compose two actions. The second action is a unit-returning action, so the result of the /// first action is propagated. /// /// First action to run /// Second action to run /// Result of the first action public static Eff operator >> (K lhs, K rhs) => lhs >> (x => (_ => x) * rhs); } } ================================================ FILE: LanguageExt.Core/Effects/Eff/Eff no runtime/Operators/Eff.Operators.cs ================================================ using LanguageExt.Traits; namespace LanguageExt; public static partial class EffExtensions { extension(K _) { /// /// Downcast operator /// public static Eff operator +(K ma) => (Eff)ma; public static Eff operator >> (K ma, Lower lower) => (Eff)ma; } } ================================================ FILE: LanguageExt.Core/Effects/Eff/Eff no runtime/Prelude/Eff.Prelude.cs ================================================ using System; using LanguageExt.Common; using System.Threading.Tasks; using System.Diagnostics.Contracts; using System.Runtime.CompilerServices; using System.Threading; using LanguageExt.Effects; namespace LanguageExt; public static partial class Prelude { /// /// Effect that always returns `unit`. /// public static readonly Eff unitEff = Pure(unit); /// /// Timeout operation if it takes too long /// [Pure] [MethodImpl(Opt.Default)] public static Eff timeout(TimeSpan timeout, Eff ma) => ma.MapIO(io => io.Timeout(timeout)); /// /// Construct an successful effect with a pure value /// /// Pure value to construct the monad with /// Bound value type /// Synchronous IO monad that captures the pure value [Pure, MethodImpl(Opt.Default)] public static Eff SuccessEff(A value) => LanguageExt.Eff.Pure(value); /// /// Construct a failed effect /// /// Error that represents the failure /// Bound value type /// Synchronous IO monad that captures the failure [Pure, MethodImpl(Opt.Default)] public static Eff FailEff(Error error) => LanguageExt.Eff.Fail(error); //////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Runtime helpers // /// /// Make the runtime into the bound value /// [Pure] internal static Eff runtimeMinRT => LanguageExt.Eff.Lift(rt => rt); /// /// Create a new cancellation context and run the provided Aff in that context /// /// Operation to run in the next context /// Bound value type /// An asynchronous effect that captures the operation running in context public static Eff localCancel(Eff ma) => localCancel(ma.effect).As(); /// /// Cancellation token /// /// CancellationToken public static Eff cancelTokenEff => IO.token; //////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Monadic join // /// /// Monadic join operator /// /// /// Collapses a nested IO monad so there is no nesting. /// /// Nest IO monad to flatten /// Runtime /// Error type /// Bound value /// Flattened IO monad [Pure, MethodImpl(Opt.Default)] public static Eff flatten(Eff> mma) => mma.Flatten(); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Lifting // /// /// Lift a synchronous effect into the IO monad /// [Pure, MethodImpl(Opt.Default)] public static Eff liftEff(Action action) => LanguageExt.Eff.Lift(() => { action(); return unit; }); /// /// Lift a synchronous effect into the IO monad /// [Pure, MethodImpl(Opt.Default)] public static Eff liftEff(Func f) => LanguageExt.Eff.Lift(f); /// /// Lift a synchronous effect into the IO monad /// [Pure, MethodImpl(Opt.Default)] public static Eff liftEff(Func> f) => LanguageExt.Eff.Lift(f); /// /// Lift a synchronous effect into the IO monad /// [Pure, MethodImpl(Opt.Default)] public static Eff liftEff(Func> f) => LanguageExt.Eff.Lift(f); /// /// Lift a synchronous effect into the IO monad /// [Pure, MethodImpl(Opt.Default)] public static Eff liftEff(Func> f) => LanguageExt.Eff.Lift(f); /// /// Lift a synchronous effect into the IO monad /// [Pure, MethodImpl(Opt.Default)] public static Eff liftEff(Func f) => LanguageExt.Eff.Lift(f); /// /// Lift a synchronous effect into the IO monad /// [Pure, MethodImpl(Opt.Default)] public static Eff liftEff(Func> f) => LanguageExt.Eff.Lift(f); /// /// Lift a asynchronous effect into the IO monad /// [Pure, MethodImpl(Opt.Default)] public static Eff liftEff(Func> f) => LanguageExt.Eff.LiftIO(f); /// /// Lift a asynchronous effect into the IO monad /// [Pure, MethodImpl(Opt.Default)] public static Eff liftEff(Func>> f) => LanguageExt.Eff.LiftIO(f); /// /// Lift a asynchronous effect into the IO monad /// [Pure, MethodImpl(Opt.Default)] public static Eff liftEff(Func> f) => LanguageExt.Eff.LiftIO(f); /// /// Lift a asynchronous effect into the IO monad /// [Pure, MethodImpl(Opt.Default)] public static Eff liftEff(Func>> f) => LanguageExt.Eff.LiftIO(f); //////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Map and map-left // /// /// Maps the IO monad if it's in a success state /// /// Function to map the success value with /// Mapped IO monad [Pure, MethodImpl(Opt.Default)] public static Eff map(Eff ma, Func f) => ma.Map(f); /// /// Maps the IO monad if it's in a success state /// /// Function to map the success value with /// Mapped IO monad [Pure, MethodImpl(Opt.Default)] public static Eff mapFail(Eff ma, Func f) => ma.MapFail(f); //////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Bi-map // /// /// Mapping of either the Success state or the Failure state depending on what /// state this IO monad is in. /// /// Mapping to use if the IO monad is in a success state /// Mapping to use if the IO monad is in a failure state /// Mapped IO monad [Pure, MethodImpl(Opt.Default)] public static Eff bimap(Eff ma, Func Succ, Func Fail) => ma.BiMap(Succ, Fail); //////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Matching // /// /// Pattern match the success or failure values and collapse them down to a success value /// /// Success value mapping /// Failure value mapping /// IO in a success state [Pure, MethodImpl(Opt.Default)] public static Eff match(Eff ma, Func Succ, Func Fail) => ma.Match(Succ, Fail); /// /// Map the failure to a success value /// /// Function to map the fail value /// IO in a success state [Pure, MethodImpl(Opt.Default)] public static Eff ifFail(Eff ma, Func Fail) => ma.IfFail(Fail); /// /// Map the failure to a new IO effect /// /// Function to map the fail value /// IO that encapsulates that IfFail [Pure, MethodImpl(Opt.Default)] public static Eff ifFailEff(Eff ma, Func> Fail) => ma.IfFailEff(Fail); //////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Filter // /// /// Only allow values through the effect if the predicate returns `true` for the bound value /// /// Predicate to apply to the bound value> /// Filtered IO [Pure, MethodImpl(Opt.Default)] public static Eff filter(Eff ma, Func predicate) => ma.Filter(predicate); } ================================================ FILE: LanguageExt.Core/Effects/Eff/Eff no runtime/Prelude/Eff.Prelude.mapapply.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public static partial class Prelude { /// /// Functor map operation /// /// /// Unwraps the value within the functor, passes it to the map function `f` provided, and /// then takes the mapped value and wraps it back up into a new functor. /// /// Functor to map /// Mapping function /// Mapped functor public static Eff map(Func f, K ma) => Functor.map(f, ma).As(); /// /// Applicative action: runs the first applicative, ignores the result, and returns the second applicative /// public static Eff action(K ma, K mb) => Applicative.action(ma, mb).As(); /// /// Applicative functor apply operation /// /// /// Unwraps the value within the `ma` applicative-functor, passes it to the unwrapped function(s) within `mf`, and /// then takes the resulting value and wraps it back up into a new applicative-functor. /// /// Value(s) applicative functor /// Mapping function(s) /// Mapped applicative functor public static Eff apply(K> mf, K ma) => Applicative.apply(mf, ma).As(); } ================================================ FILE: LanguageExt.Core/Effects/Eff/Eff with runtime/Eff.Module.cs ================================================ using System; using LanguageExt.Common; using System.Threading.Tasks; using System.Diagnostics.Contracts; using System.Runtime.CompilerServices; using static LanguageExt.Prelude; namespace LanguageExt; public partial class Eff { /// /// Construct a successful effect with a pure value /// /// Pure value to construct the monad with /// Bound value type /// Synchronous IO monad that captures the pure value [Pure, MethodImpl(Opt.Default)] public static Eff Success(A value) => Eff.Pure(value); /// /// Construct a failed effect /// /// Error that represents the failure /// Bound value type /// Synchronous IO monad that captures the failure [Pure, MethodImpl(Opt.Default)] public static Eff Fail(Error error) => Eff.Fail(error); /// /// Unit effect /// public static Eff unit() => Success(default); //////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Runtime helpers // /// /// Make the runtime into the bound value /// [Pure, MethodImpl(Opt.Default)] public static Eff runtime() => liftEff(identity); /// /// Get all the internal state of the `Eff` /// [Pure, MethodImpl(Opt.Default)] public static Eff getState() => new(Eff.getState); /// /// Create a new cancellation context and run the provided Aff in that context /// /// Operation to run in the next context /// Runtime environment /// Bound value type /// An asynchronous effect that captures the operation running in context public static Eff localCancel(Eff ma) => ma.LocalIO().As(); /// /// Create a new local context for the environment by mapping the outer environment and then /// using the result as a new context when running the IO monad provided /// /// Function to map the outer environment into a new one to run `ma` /// IO monad to run in the new context [Pure, MethodImpl(Opt.Default)] public static Eff local(Func f, Eff ma) => // Get the current state of the Eff from st in getState() // Run the local operation from rs in IO.local(ma.effect.Run(f(st.Runtime))).As() // Ignore any changes to the state and just return the result of the local operation select rs; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Lifting // /// /// Lift an effect into the `Eff` monad /// [Pure, MethodImpl(Opt.Default)] public static Eff lift(Func> f) => +envIO.Bind(e => Eff.Lift(rt => f(e, rt))); /// /// Lift an effect into the `Eff` monad /// [Pure, MethodImpl(Opt.Default)] public static Eff lift(Func> f) => Eff.Lift(f); /// /// Lift an effect into the `Eff` monad /// [Pure, MethodImpl(Opt.Default)] public static Eff lift(Func> f) => +envIO.Bind(e => Eff.Lift(rt => f(e, rt))); /// /// Lift an effect into the `Eff` monad /// [Pure, MethodImpl(Opt.Default)] public static Eff lift(Func f) => Eff.Lift(f); /// /// Lift an effect into the `Eff` monad /// [Pure, MethodImpl(Opt.Default)] public static Eff lift(Func f) => +envIO.Bind(e => Eff.Lift(rt => f(e, rt))); /// /// Lift an effect into the `Eff` monad /// [Pure, MethodImpl(Opt.Default)] public static Eff lift(Func> f) => Eff.LiftIO(f); /// /// Lift an effect into the `Eff` monad /// [Pure, MethodImpl(Opt.Default)] public static Eff lift(Func> f) => +envIO.Bind(e => Eff.LiftIO(rt => f(e, rt))); /// /// Lift an effect into the `Eff` monad /// [Pure, MethodImpl(Opt.Default)] public static Eff lift(Func>> f) => Eff.LiftIO(f); /// /// Lift an effect into the `Eff` monad /// [Pure, MethodImpl(Opt.Default)] public static Eff lift(Func>> f) => +envIO.Bind(e => Eff.LiftIO(rt => f(e, rt))); /// /// Lift an effect into the `Eff` monad /// [Pure, MethodImpl(Opt.Default)] public static Eff lift(IO ma) => Eff.LiftIO(ma); } ================================================ FILE: LanguageExt.Core/Effects/Eff/Eff with runtime/Eff.cs ================================================ using System; using LanguageExt.Common; using static LanguageExt.Prelude; using System.Diagnostics.Contracts; using System.Runtime.CompilerServices; using System.Threading.Tasks; using LanguageExt.Traits; namespace LanguageExt; /// /// This monad is used to encapsulate side effects, exception capture, and dependency-injection via the `RT` runtime. /// /// Runtime type /// Bound value type public record Eff(ReaderT effect) : K, A> { //////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Constructors // /// /// Constructor /// [MethodImpl(Opt.Default)] internal Eff(Func> effect) : this(Eff.getsIO(effect)) { } /// /// Constructor /// [MethodImpl(Opt.Default)] Eff(A value) : this(Eff.pure(value)) { } /// /// Constructor /// [MethodImpl(Opt.Default)] Eff(Error value) : this(Eff.fail(value)) { } /// /// Constructor /// [MethodImpl(Opt.Default)] Eff(Func effect) : this(Eff.gets(effect)) { } /// /// Constructor /// [MethodImpl(Opt.Default)] Eff(Func> effect) : this(Eff.gets(effect)) { } /// /// Constructor /// [MethodImpl(Opt.Default)] Eff(Func> effect) : this(Eff.gets(effect)) { } /// /// Constructor /// [MethodImpl(Opt.Default)] Eff(IO effect) : this(ReaderT.liftIO(effect)) { } //////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Lifting // /// /// Lift a value into the `Eff` monad /// [Pure, MethodImpl(Opt.Default)] public static Eff Pure(A value) => new(value); /// /// Lift a failure into the `Eff` monad /// [Pure, MethodImpl(Opt.Default)] public static Eff Fail(Error error) => new(error); /// /// Lift an effect into the `Eff` monad /// [Pure, MethodImpl(Opt.Default)] public static Eff Lift(Func> f) => new (f); /// /// Lift an effect into the `Eff` monad /// [Pure, MethodImpl(Opt.Default)] public static Eff Lift(Func> f) => new (f); /// /// Lift an effect into the `Eff` monad /// [Pure, MethodImpl(Opt.Default)] public static Eff Lift(Func f) => new (f); /// /// Lift an effect into the `Eff` monad /// [Pure, MethodImpl(Opt.Default)] public static Eff LiftIO(Func> f) => new (f); /// /// Lift an effect into the `Eff` monad /// [Pure, MethodImpl(Opt.Default)] public static Eff LiftIO(Func>> f) => new (rt => f(rt).Map(r => r.ThrowIfFail())); /// /// Lift an effect into the `Eff` monad /// [Pure, MethodImpl(Opt.Default)] public static Eff LiftIO(Func> f) => new(Eff.getsM(f)); /// /// Lift an effect into the `Eff` monad /// [Pure, MethodImpl(Opt.Default)] public static Eff Lift(Func> f) => new (_ => f()); /// /// Lift an effect into the `Eff` monad /// [Pure, MethodImpl(Opt.Default)] public static Eff Lift(Func> f) => new (_ => f()); /// /// Lift an effect into the `Eff` monad /// [Pure, MethodImpl(Opt.Default)] public static Eff Lift(Func f) => new (_ => f()); /// /// Lift an effect into the `Eff` monad /// [Pure, MethodImpl(Opt.Default)] public static Eff LiftIO(Func> f) => new (_ => f()); /// /// Lift an effect into the `Eff` monad /// [Pure, MethodImpl(Opt.Default)] public static Eff LiftIO(Func>> f) => new(_ => f().Map(r => r.ThrowIfFail())); /// /// Lift an effect into the `Eff` monad /// [Pure, MethodImpl(Opt.Default)] public static Eff LiftIO(IO ma) => new(ma); //////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Map and map-left // /// /// Maps the `Eff` monad if it's in a success state /// /// Function to map the success value with /// Mapped `Eff` monad [Pure, MethodImpl(Opt.Default)] public Eff Map(Func f) => new (effect.Map(f)); /// /// Maps the `Eff` monad if it's in a success state /// /// Function to map the success value with /// Mapped `Eff` monad [Pure, MethodImpl(Opt.Default)] public Eff Select(Func f) => new (effect.Map(f)); /// /// Maps the `Eff` monad if it's in a success state /// /// Function to map the success value with /// Mapped `Eff` monad [Pure, MethodImpl(Opt.Default)] public Eff MapFail(Func f) => this.Catch(f).As(); /// /// Maps the inner IO monad /// /// Function to map with /// Mapped `Eff` monad [Pure, MethodImpl(Opt.Default)] public Eff MapIO(Func, IO> f) => mapIO(this, f).As(); //////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Bi-map // /// /// Mapping of either the Success state or the Failure state depending on what /// state this `Eff` monad is in. /// /// Mapping to use if the `Eff` monad if in a success state /// Mapping to use if the `Eff` monad if in a failure state /// Mapped `Eff` monad [Pure, MethodImpl(Opt.Default)] public Eff BiMap(Func Succ, Func Fail) => Map(Succ).Catch(Fail).As(); //////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Matching // /// /// Pattern match the success or failure values and collapse them down to a success value /// /// Success value mapping /// Failure value mapping /// IO in a success state [Pure] public Eff Match(Func Succ, Func Fail) => Map(Succ).Catch(Fail).As(); /// /// Map the failure to a success value /// /// Function to map the fail value /// IO in a success state [Pure, MethodImpl(Opt.Default)] public Eff IfFail(Func Fail) => this.Catch(Fail).As(); /// /// Map the failure to a new IO effect /// /// Function to map the fail value /// IO that encapsulates that IfFail [Pure, MethodImpl(Opt.Default)] public Eff IfFailEff(Func, A>> Fail) => this.Catch(Fail).As(); /// /// Map the failure to a new IO effect /// /// Function to map the fail value /// IO that encapsulates that IfFail [Pure, MethodImpl(Opt.Default)] public Eff IfFailEff(Func> Fail) => IfFailEff(e => Fail(e).As().WithRuntime()); //////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Filter // /// /// Only allow values through the effect if the predicate returns `true` for the bound value /// /// Predicate to apply to the bound value> /// Filtered IO [Pure, MethodImpl(Opt.Default)] public Eff Filter(Func predicate) => Bind(x => predicate(x) ? Pure(x) : Fail(Errors.None)); /// /// Only allow values through the effect if the predicate returns `true` for the bound value /// /// Predicate to apply to the bound value> /// Filtered IO [Pure, MethodImpl(Opt.Default)] public Eff Where(Func predicate) => Bind(x => predicate(x) ? Pure(x) : Fail(Errors.None)); //////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Monadic binding // /// /// Monadic bind operation. This runs the current `Eff` monad and feeds its result to the /// function provided; which in turn returns a new `Eff` monad. This can be thought of as /// chaining IO operations sequentially. /// /// Bind operation /// Composition of this monad and the result of the function provided [Pure, MethodImpl(Opt.Default)] public Eff Bind(Func> f) => new(effect.Bind(x => f(x).effect)); /// /// Monadic bind operation. This runs the current `Eff` monad and feeds its result to the /// function provided; which in turn returns a new `Eff` monad. This can be thought of as /// chaining IO operations sequentially. /// /// Bind operation /// Composition of this monad and the result of the function provided [Pure, MethodImpl(Opt.Default)] public Eff Bind(Func> f) => new(effect.Bind(f)); /// /// Monadic bind operation. This runs the current `Eff` monad and feeds its result to the /// function provided; which in turn returns a new `Eff` monad. This can be thought of as /// chaining IO operations sequentially. /// /// Bind operation /// Composition of this monad and the result of the function provided [Pure, MethodImpl(Opt.Default)] public Eff Bind(Func> f) => new(effect.Bind(f)); /// /// Monadic bind operation. This runs the current `Eff` monad and feeds its result to the /// function provided; which in turn returns a new `Eff` monad. This can be thought of as /// chaining IO operations sequentially. /// /// Bind operation /// Composition of this monad and the result of the function provided [Pure, MethodImpl(Opt.Default)] public Eff Bind(Func, B>> f) => Bind(a => f(a).As()); /// /// Monadic bind operation. This runs the current `Eff` monad and feeds its result to the /// function provided; which in turn returns a new `Eff` monad. This can be thought of as /// chaining IO operations sequentially. /// /// Bind operation /// Composition of this monad and the result of the function provided [Pure, MethodImpl(Opt.Default)] public Eff Bind(Func> f) => Map(x => f(x).Value); /// /// Monadic bind operation. This runs the current `Eff` monad and feeds its result to the /// function provided; which in turn returns a new `Eff` monad. This can be thought of as /// chaining IO operations sequentially. /// /// Bind operation /// Composition of this monad and the result of the function provided [Pure, MethodImpl(Opt.Default)] public Eff Bind(Func> f) => Bind(x => Fail(f(x).Value)); /// /// Monadic bind operation. This runs the current `Eff` monad and feeds its result to the /// function provided; which in turn returns a new `Eff` monad. This can be thought of as /// chaining IO operations sequentially. /// /// Bind operation /// Composition of this monad and the result of the function provided [Pure, MethodImpl(Opt.Default)] public Eff Bind(Func> f) => Bind(x => f(x).WithRuntime()); /// /// Monadic bind operation. This runs the current `Eff` monad and feeds its result to the /// function provided; which in turn returns a new `Eff` monad. This can be thought of as /// chaining IO operations sequentially. /// /// Bind operation /// Composition of this monad and the result of the function provided [Pure, MethodImpl(Opt.Default)] public Eff Bind(Func> f) => Bind(a => f(a).As()); //////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Monadic binding and projection // /// /// Monadic bind operation. This runs the current `Eff` monad and feeds its result to the /// function provided; which in turn returns a new `Eff` monad. This can be thought of as /// chaining IO operations sequentially. /// /// Bind operation /// Composition of this monad and the result of the function provided [Pure, MethodImpl(Opt.Default)] public Eff SelectMany(Func> bind, Func project) => Bind(x => bind(x).Map(y => project(x, y))); /// /// Monadic bind operation. This runs the current `Eff` monad and feeds its result to the /// function provided; which in turn returns a new `Eff` monad. This can be thought of as /// chaining IO operations sequentially. /// /// Bind operation /// Composition of this monad and the result of the function provided [Pure, MethodImpl(Opt.Default)] public Eff SelectMany(Func, B>> bind, Func project) => SelectMany(x => bind(x).As(), project); /// /// Monadic bind operation. This runs the current `Eff` monad and feeds its result to the /// function provided; which in turn returns a new `Eff` monad. This can be thought of as /// chaining IO operations sequentially. /// /// Bind operation /// Composition of this monad and the result of the function provided [Pure, MethodImpl(Opt.Default)] public Eff SelectMany(Func> bind, Func project) => new(effect.SelectMany(bind, project)); /// /// Monadic bind operation. This runs the current `Eff` monad and feeds its result to the /// function provided; which in turn returns a new `Eff` monad. This can be thought of as /// chaining IO operations sequentially. /// /// Bind operation /// Composition of this monad and the result of the function provided [Pure, MethodImpl(Opt.Default)] public Eff SelectMany(Func> bind, Func project) => new(effect.SelectMany(bind, project)); /// /// Monadic bind operation. This runs the current `Eff` monad and feeds its result to the /// function provided; which in turn returns a new `Eff` monad. This can be thought of as /// chaining IO operations sequentially. /// /// Bind operation /// Composition of this monad and the result of the function provided [Pure, MethodImpl(Opt.Default)] public Eff SelectMany(Func> bind, Func project) => new(effect.SelectMany(bind, project)); /// /// Monadic bind operation. This runs the current `Eff` monad and feeds its result to the /// function provided; which in turn returns a new `Eff` monad. This can be thought of as /// chaining IO operations sequentially. /// /// Bind operation /// Composition of this monad and the result of the function provided [Pure, MethodImpl(Opt.Default)] public Eff SelectMany(Func> bind, Func project) => SelectMany(x => Eff.Fail(bind(x).Value), project); /// /// Monadic bind operation. This runs the current `Eff` monad and feeds its result to the /// function provided; which in turn returns a new `Eff` monad. This can be thought of as /// chaining IO operations sequentially. /// /// Bind operation /// Composition of this monad and the result of the function provided [Pure, MethodImpl(Opt.Default)] public Eff SelectMany(Func> bind, Func project) => from x in this from r in bind(x) switch { { Flag: true } => Eff.Pure(unit), var g => Eff.Fail(g.OnFalse()) } select project(x, unit); /// /// Monadic bind operation. This runs the current `Eff` monad and feeds its result to the /// function provided; which in turn returns a new `Eff` monad. This can be thought of as /// chaining IO operations sequentially. /// /// Bind operation /// Composition of this monad and the result of the function provided [Pure, MethodImpl(Opt.Default)] public Eff SelectMany(Func, Unit>> bind, Func project) => from x in this from r in bind(x) switch { { Flag: true } => Eff.Pure(unit), var g => Eff.Fail(g.OnFalse().Value) } select project(x, unit); /// /// Monadic bind operation. This runs the current `Eff` monad and feeds its result to the /// function provided; which in turn returns a new `Eff` monad. This can be thought of as /// chaining IO operations sequentially. /// /// Bind operation /// Composition of this monad and the result of the function provided [Pure, MethodImpl(Opt.Default)] public Eff SelectMany(Func> bind, Func project) => Bind(x => bind(x).Map(y => project(x, y))); /// /// Monadic bind operation. This runs the current `Eff` monad and feeds its result to the /// function provided; which in turn returns a new `Eff` monad. This can be thought of as /// chaining IO operations sequentially. /// /// Bind operation /// Composition of this monad and the result of the function provided [Pure, MethodImpl(Opt.Default)] public Eff SelectMany(Func> bind, Func project) => SelectMany(x => bind(x).As(), project); //////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Conversion operators // /// /// Convert to an `Eff` monad /// [Pure, MethodImpl(Opt.Default)] public static implicit operator Eff(Pure ma) => ma.ToEff(); /// /// Convert to an `Eff` monad /// [Pure, MethodImpl(Opt.Default)] public static implicit operator Eff(Fail ma) => ma.ToEff(); /// /// Convert to an `Eff` monad /// [Pure, MethodImpl(Opt.Default)] public static implicit operator Eff(in Lift ma) => Lift(ma.Function); /// /// Convert to an `Eff` monad /// [Pure, MethodImpl(Opt.Default)] public static implicit operator Eff(in Lift> ma) => Lift(ma.Function); /// /// Convert to an `Eff` monad /// [Pure, MethodImpl(Opt.Default)] public static implicit operator Eff(in Lift ma) => Lift(ma.Function); /// /// Convert to an `Eff` monad /// [Pure, MethodImpl(Opt.Default)] public static implicit operator Eff(in Lift> ma) => Lift(ma.Function); /// /// Convert to an `Eff` monad /// [Pure, MethodImpl(Opt.Default)] public static implicit operator Eff(in Either ma) => ma.Match(Left: Fail, Right: Pure); /// /// Convert to an `Eff` monad /// [Pure, MethodImpl(Opt.Default)] public static implicit operator Eff(in Fin ma) => ma.Match(Succ: Pure, Fail: Fail); /// /// Convert to an `Eff` monad /// [Pure, MethodImpl(Opt.Default)] public static implicit operator Eff(in Eff ma) => ma.WithRuntime(); /// /// Convert to an `Eff` monad /// [Pure, MethodImpl(Opt.Default)] public static implicit operator Eff(in IO ma) => LiftIO(ma); [Pure, MethodImpl(Opt.Default)] public static implicit operator Eff(Error fail) => Fail(fail); //////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Obsolete // /// /// Lift a value into the `Eff` monad /// [Obsolete("Use either: `Eff.Lift`, `Prelude.liftEff`, or `lift`")] [Pure, MethodImpl(Opt.Default)] public static Eff Success(A value) => Pure(value); /// /// Lift a synchronous effect into the `Eff` monad /// [Obsolete("Use either: `Eff.Lift`, `Prelude.liftEff`, or `lift`")] [Pure, MethodImpl(Opt.Default)] public static Eff Effect(Func f) => Lift(f); /// /// Lift a synchronous effect into the `Eff` monad /// [Obsolete("Use either: `Eff.Lift`, `Prelude.liftEff`, or `lift`")] [Pure, MethodImpl(Opt.Default)] public static Eff EffectMaybe(Func> f) => Lift(f); public override string ToString() => "Eff"; } ================================================ FILE: LanguageExt.Core/Effects/Eff/Eff with runtime/Extensions/Eff.Extensions.MapApply.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public static partial class EffExtensions { /// /// Functor map operation /// /// /// Unwraps the value within the functor, passes it to the map function `f` provided, and /// then takes the mapped value and wraps it back up into a new functor. /// /// Functor to map /// Mapping function /// Mapped functor public static Eff Map(this Func f, K, A> ma) => Functor.map(f, ma).As(); /// /// Functor map operation /// /// /// Unwraps the value within the functor, passes it to the map function `f` provided, and /// then takes the mapped value and wraps it back up into a new functor. /// /// Functor to map /// Mapping function /// Mapped functor public static Eff Map(this Func f, Eff ma) => Functor.map(f, ma).As(); /// /// Applicative action: runs the first applicative, ignores the result, and returns the second applicative /// public static Eff Action( this Eff ma, Eff mb) => Applicative.action(ma, mb).As(); /// /// Applicative functor apply operation /// /// /// Unwraps the value within the `ma` applicative-functor, passes it to the unwrapped function(s) within `mf`, and /// then takes the resulting value and wraps it back up into a new applicative-functor. /// /// Value(s) applicative functor /// Mapping function(s) /// Mapped applicative functor public static Eff Apply(this Eff> mf, K, A> ma) => Applicative.apply(mf, ma).As(); /// /// Applicative functor apply operation /// /// /// Unwraps the value within the `ma` applicative-functor, passes it to the unwrapped function(s) within `mf`, and /// then takes the resulting value and wraps it back up into a new applicative-functor. /// /// Value(s) applicative functor /// Mapping function(s) /// Mapped applicative functor public static Eff Apply(this K, Func> mf, K, A> ma) => Applicative.apply(mf, ma).As(); } ================================================ FILE: LanguageExt.Core/Effects/Eff/Eff with runtime/Extensions/Eff.Extensions.cs ================================================ using System; using System.Diagnostics.Contracts; using System.Runtime.CompilerServices; using System.Threading.Tasks; using LanguageExt.Traits; namespace LanguageExt; public static partial class EffExtensions { extension(K, A> ma) { /// /// Cast type to its Kind /// public Eff As() => (Eff)ma; /// /// Invoke the effect /// /// /// Returns the result value only /// [Pure, MethodImpl(Opt.Default)] public Fin Run(RT env) => ma.As().Run(env, EnvIO.New()); /// /// Invoke the effect /// /// /// Returns the result value only /// [Pure, MethodImpl(Opt.Default)] public Fin Run(RT env, EnvIO envIO) { try { return ma.As().effect.Run(env).Run(envIO); } catch(Exception e) { return Fin.Fail(e); } } /// /// Invoke the effect /// /// /// This is labelled 'unsafe' because it can throw an exception, whereas /// `Run` will capture any errors and return a `Fin` type. /// [Pure, MethodImpl(Opt.Default)] public A RunUnsafe(RT env) => ma.As().effect.Run(env).Run(); /// /// Invoke the effect /// /// /// This is labelled 'unsafe' because it can throw an exception, whereas /// `Run` will capture any errors and return a `Fin` type. /// [Pure, MethodImpl(Opt.Default)] public A RunUnsafe(RT env, EnvIO envIO) => ma.As().effect.Run(env).Run(envIO); /// /// Invoke the effect to leave the inner IO monad /// [Pure, MethodImpl(Opt.Default)] public IO RunIO(RT env) => ma.As().effect.Run(env).As(); /// /// Invoke the effect /// /// /// Returns the result value only /// [Pure, MethodImpl(Opt.Default)] public async Task> RunAsync(RT env) { try { return await ma.As().effect.Run(env).RunAsync(); } catch(Exception e) { return Fin.Fail(e); } } /// /// Invoke the effect /// /// /// Returns the result value only /// [Pure, MethodImpl(Opt.Default)] public async Task> RunAsync(RT env, EnvIO envIO) { try { return await ma.As().effect.Run(env).RunAsync(envIO); } catch(Exception e) { return Fin.Fail(e); } } /// /// Invoke the effect /// /// /// This is labelled 'unsafe' because it can throw an exception, whereas /// `Run` will capture any errors and return a `Fin` type. /// [Pure, MethodImpl(Opt.Default)] public ValueTask RunUnsafeAsync(RT env) => ma.As().effect.Run(env).RunAsync(EnvIO.New()); /// /// Invoke the effect /// /// /// This is labelled 'unsafe' because it can throw an exception, whereas /// `Run` will capture any errors and return a `Fin` type. /// [Pure, MethodImpl(Opt.Default)] public ValueTask RunUnsafeAsync(RT env, EnvIO envIO) => ma.As().effect.Run(env).RunAsync(envIO); } //////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Invoking // //////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Monadic join // /// /// Monadic join operator /// /// /// Collapses a nested IO monad so there is no nesting. /// /// Nest IO monad to flatten /// Runtime /// Bound value /// Flattened IO monad public static Eff Flatten(this K, Eff> mma) => mma.As().Bind(ma => ma); /// /// Monadic join operator /// /// /// Collapses a nested IO monad so there is no nesting. /// /// Nest IO monad to flatten /// Runtime /// Bound value /// Flattened IO monad public static Eff Flatten(this K, K, A>> mma) => mma.As().Bind(ma => ma); //////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // SelectMany extensions // /// /// Monadic bind and project with paired IO monads /// public static Eff SelectMany( this (K, A> First, K, B> Second) self, Func<(A First, B Second), K, C>> bind, Func<(A First, B Second), C, D> project) => self.Zip().Bind(ab => bind(ab).Map(c => project(ab, c))).As(); /// /// Monadic bind and project with paired IO monads /// public static Eff SelectMany( this K, A> self, Func, B> First, K, C> Second)> bind, Func project) => self.As().Bind(a => bind(a).Zip().Map(cd => project(a, cd))); /// /// Monadic bind and project with paired IO monads /// public static Eff SelectMany( this (K, A> First, K, B> Second, K, C> Third) self, Func<(A First, B Second, C Third), K, D>> bind, Func<(A First, B Second, C Third), D, E> project) => self.Zip().Bind(ab => bind(ab).Map(c => project(ab, c))).As(); /// /// Monadic bind and project with paired IO monads /// public static Eff SelectMany( this K, A> self, Func, B> First, K, C> Second, K, D> Third)> bind, Func project) => self.As().Bind(a => bind(a).Zip().Map(cd => project(a, cd))); } ================================================ FILE: LanguageExt.Core/Effects/Eff/Eff with runtime/Extensions/Eff.Guard.cs ================================================ using System; using LanguageExt.Common; using static LanguageExt.Prelude; namespace LanguageExt; public static partial class EffExtensions { /// /// Natural transformation to `Eff` /// public static Eff ToEff(this Guard guard) => guard.Flag ? Pure(unit) : Fail(guard.OnFalse()); /// /// Monadic binding support for `Eff` /// public static Eff Bind( this Guard guard, Func> f) => guard.Flag ? f(default).As() : Fail(guard.OnFalse()); /// /// Monadic binding support for `Eff` /// public static Eff SelectMany( this Guard guard, Func> bind, Func project) => guard.Flag ? bind(default).As().Map(b => project(default, b)) : Fail(guard.OnFalse()); } ================================================ FILE: LanguageExt.Core/Effects/Eff/Eff with runtime/Operators/Eff.Operators.Applicative.cs ================================================ using System; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; public static partial class EffExtensions { extension(K, A> self) { /// /// Applicative sequence operator /// public static Eff operator >>> (K, A> ma, K, B> mb) => ma.Action(mb).As(); /// /// Applicative apply operator /// public static Eff operator * (K, Func> mf, K, A> ma) => mf.Apply(ma); /// /// Applicative apply operator /// public static Eff operator * (K, A> ma, K, Func> mf) => mf.Apply(ma); } extension(K, A> self) { /// /// Applicative apply operator /// public static Eff> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static Eff> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) { /// /// Applicative apply operator /// public static Eff>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static Eff>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) { /// /// Applicative apply operator /// public static Eff>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static Eff>>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) { /// /// Applicative apply operator /// public static Eff>>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static Eff>>>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) { /// /// Applicative apply operator /// public static Eff>>>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static Eff>>>>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) { /// /// Applicative apply operator /// public static Eff>>>>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static Eff>>>>>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) { /// /// Applicative apply operator /// public static Eff>>>>>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static Eff>>>>>>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) { /// /// Applicative apply operator /// public static Eff>>>>>>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static Eff>>>>>>>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) { /// /// Applicative apply operator /// public static Eff>>>>>>>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static Eff>>>>>>>>> operator *( K, A> ma, K, Func> mf) => curry * mf * ma; } } ================================================ FILE: LanguageExt.Core/Effects/Eff/Eff with runtime/Operators/Eff.Operators.Choice.cs ================================================ using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt; public static partial class EffExtensions { extension(K, A> self) { public static Eff operator |(K, A> lhs, K, A> rhs) => lhs.Choose(rhs).As(); public static Eff operator |(K, A> lhs, K rhs) => lhs.Choose(rhs.As().WithRuntime()).As(); public static Eff operator |(K, A> lhs, Pure rhs) => lhs.Choose(rhs.ToEff()).As(); } } ================================================ FILE: LanguageExt.Core/Effects/Eff/Eff with runtime/Operators/Eff.Operators.Fallible.cs ================================================ using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt; public static partial class EffExtensions { extension(K, A> self) { public static Eff operator |(K, A> lhs, CatchM, A> rhs) => lhs.Catch(rhs).As(); public static Eff operator |(K, A> lhs, Fail rhs) => lhs.Catch(rhs).As(); public static Eff operator |(K, A> lhs, Error rhs) => lhs.Catch(rhs).As(); } } ================================================ FILE: LanguageExt.Core/Effects/Eff/Eff with runtime/Operators/Eff.Operators.Final.cs ================================================ namespace LanguageExt.Traits; public static partial class EffExtensions { extension(K, A> _) { /// /// Run a `finally` operation after the main operation regardless of whether it succeeds or not. /// /// Primary operation /// Finally operation /// Result of primary operation public static Eff operator |(K, A> lhs, Finally, X> rhs) => lhs.Finally(rhs.Operation).As(); } } ================================================ FILE: LanguageExt.Core/Effects/Eff/Eff with runtime/Operators/Eff.Operators.Functor.cs ================================================ using System; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; public static partial class EffExtensions { extension(K, A> _) { /// /// Functor map operator /// public static Eff operator *(Func f, K, A> ma) => ma.Map(f).As(); /// /// Functor map operator /// public static Eff operator *(K, A> ma, Func f) => ma.Map(f).As(); } extension(K, A> _) { /// /// Functor map operator /// public static Eff> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static Eff> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) { /// /// Functor map operator /// public static Eff>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static Eff>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) { /// /// Functor map operator /// public static Eff>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static Eff>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) { /// /// Functor map operator /// public static Eff>>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static Eff>>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) { /// /// Functor map operator /// public static Eff>>>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static Eff>>>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) { /// /// Functor map operator /// public static Eff>>>>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static Eff>>>>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) { /// /// Functor map operator /// public static Eff>>>>>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static Eff>>>>>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) { /// /// Functor map operator /// public static Eff>>>>>>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static Eff>>>>>>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) { /// /// Functor map operator /// public static Eff>>>>>>>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static Eff>>>>>>>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } } ================================================ FILE: LanguageExt.Core/Effects/Eff/Eff with runtime/Operators/Eff.Operators.Monad.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public static partial class EffExtensions { extension(K, A> self) { /// /// Monad bind operator /// /// Monad to bind /// Binding function /// Mapped monad public static Eff operator >> (K, A> ma, Func, B>> f) => +ma.Bind(f); /// /// Sequentially compose two actions, discarding any value produced by the first, like sequencing operators (such /// as the semicolon) in C#. /// /// First action to run /// Second action to run /// Result of the second action public static Eff operator >> (K, A> lhs, K, B> rhs) => lhs >> (_ => rhs); /// /// Monad bind operator /// /// Monad to bind /// Binding function /// Mapped monad public static Eff operator >> (K, A> ma, Func> f) => +ma.Bind(x => Eff.lift(+f(x))); /// /// Sequentially compose two actions, discarding any value produced by the first, like sequencing operators (such /// as the semicolon) in C#. /// /// First action to run /// Second action to run /// Result of the second action public static Eff operator >> (K, A> lhs, K rhs) => lhs >> (_ => rhs); } extension(K, A> self) { /// /// Sequentially compose two actions. The second action is a unit-returning action, so the result of the /// first action is propagated. /// /// First action to run /// Second action to run /// Result of the first action public static Eff operator >> (K, A> lhs, K, Unit> rhs) => lhs >> (x => (_ => x) * rhs); /// /// Sequentially compose two actions. The second action is a unit-returning action, so the result of the /// first action is propagated. /// /// First action to run /// Second action to run /// Result of the first action public static Eff operator >> (K, A> lhs, K rhs) => lhs >> (x => (_ => x) * rhs); } } ================================================ FILE: LanguageExt.Core/Effects/Eff/Eff with runtime/Operators/Eff.Operators.cs ================================================ using LanguageExt.Traits; namespace LanguageExt; public static partial class EffExtensions { extension(K, A> _) { /// /// Downcast operator /// public static Eff operator +(K, A> ma) => (Eff)ma; public static Eff operator >> (K, A> ma, Lower lower) => (Eff)ma; } } ================================================ FILE: LanguageExt.Core/Effects/Eff/Eff with runtime/Prelude/Eff.Prelude.cs ================================================ using System; using LanguageExt.Common; using System.Threading.Tasks; using System.Diagnostics.Contracts; using System.Runtime.CompilerServices; namespace LanguageExt; public static partial class Prelude { /// /// Construct a successful effect with a pure value /// /// Pure value to construct the monad with /// Bound value type /// Synchronous IO monad that captures the pure value [Pure, MethodImpl(Opt.Default)] public static Eff SuccessEff(A value) => LanguageExt.Eff.Pure(value); /// /// Construct a failed effect /// /// Error that represents the failure /// Bound value type /// Synchronous IO monad that captures the failure [Pure, MethodImpl(Opt.Default)] public static Eff FailEff(Error error) => LanguageExt.Eff.Fail(error); //////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Runtime helpers // /// /// Make the runtime into the bound value /// [Pure, MethodImpl(Opt.Default)] public static Eff runtime() => liftEff(identity); /// /// Get all the internal state of the `Eff` /// [Pure, MethodImpl(Opt.Default)] public static Eff getState() => new(LanguageExt.Eff.getState); /// /// Create a new cancellation context and run the provided Aff in that context /// /// Operation to run in the next context /// Runtime environment /// Bound value type /// An asynchronous effect that captures the operation running in context public static Eff localCancel(Eff ma) => ma.LocalIO().As(); /// /// Create a new local context for the environment by mapping the outer environment and then /// using the result as a new context when running the IO monad provided /// /// Function to map the outer environment into a new one to run `ma` /// IO monad to run in the new context [Pure, MethodImpl(Opt.Default)] public static Eff localEff(Func f, Eff ma) => // Get the current state of the Eff from st in getState() // Run the local operation from rs in IO.local(ma.effect.Run(f(st.Runtime))).As() // Ignore any changes to the state and just return the result of the local operation select rs; //////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Monadic join // /// /// Monadic join operator /// /// /// Collapses a nested IO monad so there is no nesting. /// /// Nest IO monad to flatten /// Runtime /// Error type /// Bound value /// Flattened IO monad [Pure, MethodImpl(Opt.Default)] public static Eff flatten(Eff> mma) => mma.Bind(x => x); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Lifting // /// /// Lift an effect into the `Eff` monad /// [Pure, MethodImpl(Opt.Default)] public static Eff liftEff(Func> f) => LanguageExt.Eff.Lift(f); /// /// Lift an effect into the `Eff` monad /// [Pure, MethodImpl(Opt.Default)] public static Eff liftEff(Func> f) => LanguageExt.Eff.Lift(f); /// /// Lift an effect into the `Eff` monad /// [Pure, MethodImpl(Opt.Default)] public static Eff liftEff(Func f) => LanguageExt.Eff.Lift(f); /// /// Lift an effect into the `Eff` monad /// [Pure, MethodImpl(Opt.Default)] public static Eff liftEff(Func> f) => LanguageExt.Eff.LiftIO(f); /// /// Lift an effect into the `Eff` monad /// [Pure, MethodImpl(Opt.Default)] public static Eff liftEff(Func>> f) => LanguageExt.Eff.LiftIO(f); /// /// Lift an effect into the `Eff` monad /// [Pure, MethodImpl(Opt.Default)] public static Eff liftEff(IO ma) => LanguageExt.Eff.LiftIO(ma); //////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Map and map-left // /// /// Maps the IO monad if it's in a success state /// /// Function to map the success value with /// Mapped IO monad [Pure, MethodImpl(Opt.Default)] public static Eff map(Eff ma, Func f) => ma.Map(f); /// /// Maps the IO monad if it's in a success state /// /// Function to map the success value with /// Mapped IO monad [Pure, MethodImpl(Opt.Default)] public static Eff mapFail(Eff ma, Func f) => ma.MapFail(f); //////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Bi-map // /// /// Mapping of either the Success state or the Failure state depending on what /// state this IO monad is in. /// /// Mapping to use if the IO monad is in a success state /// Mapping to use if the IO monad is in a failure state /// Mapped IO monad [Pure, MethodImpl(Opt.Default)] public static Eff bimap(Eff ma, Func Succ, Func Fail) => ma.BiMap(Succ, Fail); //////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Matching // /// /// Pattern match the success or failure values and collapse them down to a success value /// /// Success value mapping /// Failure value mapping /// IO in a success state [Pure, MethodImpl(Opt.Default)] public static Eff match(Eff ma, Func Succ, Func Fail) => ma.Match(Succ, Fail); /// /// Map the failure to a success value /// /// Function to map the fail value /// IO in a success state [Pure, MethodImpl(Opt.Default)] public static Eff ifFail(Eff ma, Func Fail) => ma.IfFail(Fail); /// /// Map the failure to a new IO effect /// /// Function to map the fail value /// IO that encapsulates that IfFail [Pure, MethodImpl(Opt.Default)] public static Eff ifFailEff(Eff ma, Func> Fail) => ma.IfFailEff(Fail); //////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Filter // /// /// Only allow values through the effect if the predicate returns `true` for the bound value /// /// Predicate to apply to the bound value> /// Filtered IO [Pure, MethodImpl(Opt.Default)] public static Eff filter(Eff ma, Func predicate) => ma.Filter(predicate); } ================================================ FILE: LanguageExt.Core/Effects/Eff/Eff with runtime/Prelude/Eff.Prelude.mapapply.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public static partial class Prelude { /// /// Functor map operation /// /// /// Unwraps the value within the functor, passes it to the map function `f` provided, and /// then takes the mapped value and wraps it back up into a new functor. /// /// Functor to map /// Mapping function /// Mapped functor public static Eff map(Func f, K, A> ma) => Functor.map(f, ma).As(); /// /// Applicative action: runs the first applicative, ignores the result, and returns the second applicative /// public static Eff action(K, A> ma, K, B> mb) => Applicative.action(ma, mb).As(); /// /// Applicative functor apply operation /// /// /// Unwraps the value within the `ma` applicative-functor, passes it to the unwrapped function(s) within `mf`, and /// then takes the resulting value and wraps it back up into a new applicative-functor. /// /// Value(s) applicative functor /// Mapping function(s) /// Mapped applicative functor public static Eff apply(K, Func> mf, K, A> ma) => Applicative.apply(mf, ma).As(); } ================================================ FILE: LanguageExt.Core/Effects/IO/DSL/IOAction.cs ================================================ using System; using System.Threading.Tasks; using LanguageExt.Traits; namespace LanguageExt.DSL; record IOAction(K Fa, K Fb, Func> Next) : InvokeSyncIO { public override IO Map(Func f) => new IOAction(Fa, Fb, b => Next(b).Map(f)); public override IO Bind(Func> f) => new IOAction(Fa, Fb, b => Next(b).Bind(f)); public override IO BindAsync(Func>> f) => new IOAction(Fa, Fb, b => Next(b).BindAsync(f)); public override IO Invoke(EnvIO envIO) { var taskA = Fa.As().RunAsync(envIO); if (taskA.IsCompleted) { return Fb.Bind(Next).As(); } else { return new IOActionAsync(taskA, Fb, Next); } } } record IOActionAsync(ValueTask Fa, K Fb, Func> Next) : InvokeAsyncIO { public override IO Map(Func f) => new IOActionAsync(Fa, Fb, b => Next(b).Map(f)); public override IO Bind(Func> f) => new IOActionAsync(Fa, Fb, b => Next(b).Bind(f)); public override IO BindAsync(Func>> f) => new IOActionAsync(Fa, Fb, b => Next(b).BindAsync(f)); public override async ValueTask> Invoke(EnvIO envIO) { await Fa; return Fb.Bind(Next).As(); } } record IOActionLazy(K Fa, Memo Fb, Func> Next) : InvokeSyncIO { public override IO Map(Func f) => new IOActionLazy(Fa, Fb, b => Next(b).Map(f)); public override IO Bind(Func> f) => new IOActionLazy(Fa, Fb, b => Next(b).Bind(f)); public override IO BindAsync(Func>> f) => new IOActionLazy(Fa, Fb, b => Next(b).BindAsync(f)); public override IO Invoke(EnvIO envIO) { var taskA = Fa.As().RunAsync(envIO); if (taskA.IsCompleted) { return Fb.Value.Bind(Next).As(); } else { return new IOActionLazyAsync(taskA, Fb, Next); } } } record IOActionLazyAsync(ValueTask Fa, Memo Fb, Func> Next) : InvokeAsyncIO { public override IO Map(Func f) => new IOActionLazyAsync(Fa, Fb, b => Next(b).Map(f)); public override IO Bind(Func> f) => new IOActionLazyAsync(Fa, Fb, b => Next(b).Bind(f)); public override IO BindAsync(Func>> f) => new IOActionLazyAsync(Fa, Fb, b => Next(b).BindAsync(f)); public override async ValueTask> Invoke(EnvIO envIO) { await Fa; return Fb.Value.Bind(Next).As(); } } ================================================ FILE: LanguageExt.Core/Effects/IO/DSL/IOActions.cs ================================================ using System; using System.Threading.Tasks; using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt.DSL; record IOActions(IterableNE> Fas, Func> Next) : InvokeSyncIO { public override IO Map(Func f) => new IOActions(Fas, x => Next(x).Map(f)); public override IO Bind(Func> f) => new IOActions(Fas, x => Next(x).Bind(f)); public override IO BindAsync(Func>> f) => new IOActions(Fas, x => Next(x).BindAsync(f)); public override IO Invoke(EnvIO envIO) { var iter = Fas.GetIterator(); var head = iter.Head; var task = head.RunAsync(envIO); if (task.IsCompleted) { return new IOActionsSync(task.Result, iter.Tail.Split(), Next); } else { return new IOActionsAsync(task, iter.Tail.Split(), Next); } } } record IOActions2(Iterator> Fas, Func> Next) : InvokeSyncIO { public override IO Map(Func f) => new IOActions2(Fas, x => Next(x).Map(f)); public override IO Bind(Func> f) => new IOActions2(Fas, x => Next(x).Bind(f)); public override IO BindAsync(Func>> f) => new IOActions2(Fas, x => Next(x).BindAsync(f)); public override IO Invoke(EnvIO envIO) { var iter = Fas.GetIterator(); var head = iter.Head; var task = head.RunAsync(envIO); if (task.IsCompleted) { return new IOActionsSync(task.Result, iter.Tail.Split(), Next); } else { return new IOActionsAsync(task, iter.Tail.Split(), Next); } } } record IOActionsSync(A Value, Iterator> Fas, Func> Next) : InvokeSyncIO { public override IO Map(Func f) => new IOActionsSync(Value, Fas, x => Next(x).Map(f)); public override IO Bind(Func> f) => new IOActionsSync(Value, Fas, x => Next(x).Bind(f)); public override IO BindAsync(Func>> f) => new IOActionsSync(Value, Fas, x => Next(x).BindAsync(f)); public override IO Invoke(EnvIO envIO) { if (Fas.IsEmpty) { return Next(Value); } else { return new IOActions2(Fas.Clone(), Next); } } } record IOActionsAsync(ValueTask Value, Iterator> Fas, Func> Next) : InvokeAsyncIO { public override IO Map(Func f) => new IOActionsAsync(Value, Fas, x => Next(x).Map(f)); public override IO Bind(Func> f) => new IOActionsAsync(Value, Fas, x => Next(x).Bind(f)); public override IO BindAsync(Func>> f) => new IOActionsAsync(Value, Fas, x => Next(x).BindAsync(f)); public override async ValueTask> Invoke(EnvIO envIO) { var value = await Value; if (Fas.IsEmpty) { return Next(value); } else { return new IOActions2(Fas.Clone(), Next); } } } ================================================ FILE: LanguageExt.Core/Effects/IO/DSL/IOActionsAsync.cs ================================================ using System; using System.Threading.Tasks; using LanguageExt.Common; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt.DSL; record IOAsyncActions(IteratorAsync> Fas, Func> Next) : InvokeAsyncIO { public override IO Map(Func f) => new IOAsyncActions(Fas, x => Next(x).Map(f)); public override IO Bind(Func> f) => new IOAsyncActions(Fas, x => Next(x).Bind(f)); public override IO BindAsync(Func>> f) => new IOAsyncActions(Fas, x => Next(x).BindAsync(f)); public override async ValueTask> Invoke(EnvIO envIO) { if (await Fas.IsEmpty) { return IO.fail(Error.New("Actions is empty")); } else { ignore(await (await Fas.Head).RunAsync(envIO)); return new IOAsyncActions(await Fas.Tail, Next); } } } ================================================ FILE: LanguageExt.Core/Effects/IO/DSL/IOApply.cs ================================================ using System; using System.Threading.Tasks; using LanguageExt.Traits; namespace LanguageExt.DSL; record IOApply(K> Ff, K Fa, Func> Next) : InvokeSyncIO { public override IO Map(Func f) => new IOApply(Ff, Fa, x => Next(x).As().Map(f)); public override IO Bind(Func> f) => new IOApply(Ff, Fa, x => Next(x).As().Bind(f)); public override IO BindAsync(Func>> f) => new IOApply(Ff, Fa, x => Next(x).As().BindAsync(f)); public override IO Invoke(EnvIO envIO) { var tf = Ff.RunAsync(envIO); var ta = Fa.RunAsync(envIO); switch (tf.IsCompleted, ta.IsCompleted) { case (true, true): return Next(tf.Result(ta.Result)).As(); default: return new IOApplyFunctionAsync(tf, ta, Next); } } public override string ToString() => "IO apply"; } record IOApplyFunctionAsync(ValueTask> Ff, ValueTask Fa, Func> Next) : InvokeAsyncIO { public override IO Map(Func f) => new IOApplyFunctionAsync(Ff, Fa, x => Next(x).As().Map(f)); public override IO Bind(Func> f) => new IOApplyFunctionAsync(Ff, Fa, x => Next(x).As().Bind(f)); public override IO BindAsync(Func>> f) => new IOApplyFunctionAsync(Ff, Fa, x => Next(x).As().BindAsync(f)); public override async ValueTask> Invoke(EnvIO envIO) { switch (Ff.IsCompleted, Fa.IsCompleted) { case (true, true): return Next(Ff.Result(Fa.Result)).As(); case (false, true): return Next((await Ff)(Fa.Result)).As(); case (true, false): return Next(Ff.Result(await Fa)).As(); default: var tf = Ff.AsTask(); var ta = Fa.AsTask(); await Task.WhenAll(tf, ta); return Next(Ff.Result(Fa.Result)).As(); } } public override string ToString() => "IO apply"; } ================================================ FILE: LanguageExt.Core/Effects/IO/DSL/IOBind.cs ================================================ using System; using System.Threading.Tasks; using LanguageExt.Traits; namespace LanguageExt.DSL; record IOBind(A Value, Func> F) : InvokeSyncIO { public override IO Map(Func f) => new IOBindMap(Value, F, f); public override IO Bind(Func> f) => new IOBindBind(Value, F, f); public override IO BindAsync(Func>> f) => new IOBindBindAsync2(Value.AsValueTask(), x => F(x).AsValueTask(), f); public override IO Invoke(EnvIO envIO) => F(Value).As(); public override string ToString() => "IO bind"; } ================================================ FILE: LanguageExt.Core/Effects/IO/DSL/IOBindAsync.cs ================================================ using System; using System.Threading.Tasks; using LanguageExt.Traits; namespace LanguageExt.DSL; record IOBindAsync(ValueTask Value, Func> F) : InvokeAsyncIO { public override IO Map(Func f) => new IOBindMapAsync(Value, F, f); public override IO Bind(Func> f) => new IOBindBindAsync(Value, F, f); public override IO BindAsync(Func>> f) => new IOBindBindAsync2(Value, x => new ValueTask>(F(x)), f); public override async ValueTask> Invoke(EnvIO envIO) => F(await Value).As(); public override string ToString() => "IO bind async"; } record IOBindBindAsync(ValueTask Value, Func> F, Func> G) : InvokeAsyncIO { public override IO Map(Func f) => new IOBindBindAsync(Value, F, x => G(x).As().Map(f)); public override IO Bind(Func> f) => new IOBindBindAsync(Value, F, x => G(x).As().Bind(f)); public override IO BindAsync(Func>> f) => new IOBindBindAsync(Value, F, x => G(x).As().BindAsync(f)); public override async ValueTask> Invoke(EnvIO envIO) => F(await Value).Bind(G).As(); public override string ToString() => "IO bind bind async"; } record IOBindMapAsync(ValueTask Value, Func> F, Func G) : InvokeAsyncIO { public override IO Map(Func f) => new IOBindMapAsync(Value, F, x => f(G(x))); public override IO Bind(Func> f) => new IOBindBindAsync(Value, F, x => f(G(x))); public override IO BindAsync(Func>> f) => new IOBindBindAsync(Value, F, x => IO.pure(G(x)).BindAsync(f)); public override async ValueTask> Invoke(EnvIO envIO) => F(await Value).Map(G).As(); public override string ToString() => "IO bind map async"; } record IOBindAsync2(ValueTask Value, Func>> F) : InvokeAsyncIO { public override IO Map(Func f) => new IOBindAsync2(Value, async x => (await F(x)).As().Map(f)); public override IO Bind(Func> f) => new IOBindBindSync2(Value, F, f); public override IO BindAsync(Func>> f) => new IOBindBindAsync2(Value, F, f); public override async ValueTask> Invoke(EnvIO envIO) => (await F(await Value)).As(); public override string ToString() => "IO bind bind async"; } record IOBindBindAsync2(ValueTask Value, Func>> F, Func>> G) : InvokeAsyncIO { public override IO Map(Func f) => new IOBindBindAsync2(Value, F, async x => (await G(x)).Map(f)); public override IO Bind(Func> f) => new IOBindBindAsync2(Value, F, async x => (await G(x)).Bind(f)); public override IO BindAsync(Func>> f) => new IOBindBindAsync2(Value, F, async x => (await G(x)).As().BindAsync(f)); public override async ValueTask> Invoke(EnvIO envIO) => (await F(await Value)).As().BindAsync(G); public override string ToString() => "IO bind bind async"; } record IOBindBindSync2(ValueTask Value, Func>> F, Func> G) : InvokeAsyncIO { public override IO Map(Func f) => new IOBindBindSync2(Value, F, x => G(x).Map(f)); public override IO Bind(Func> f) => new IOBindBindSync2(Value, F, x => G(x).Bind(f)); public override IO BindAsync(Func>> f) => new IOBindBindSync2(Value, F, x => G(x).As().BindAsync(f)); public override async ValueTask> Invoke(EnvIO envIO) => (await F(await Value)).As().Bind(G); public override string ToString() => "IO bind bind async"; } ================================================ FILE: LanguageExt.Core/Effects/IO/DSL/IOBindMap.cs ================================================ using System; using System.Threading.Tasks; using LanguageExt.Traits; namespace LanguageExt.DSL; record IOBindMap(A Value, Func> Ff, Func Fg) : InvokeSyncIO { public override IO Map(Func f) => new IOBindMap(Value, Ff, Fg, f); public override IO Bind(Func> f) => new IOBindMap2(Value, Ff, Fg, f); public override IO BindAsync(Func>> f) => new IOBindMap2(Value, Ff, Fg, x => IO.pureVAsync(f(x)).Bind(v => v)); public override IO Invoke(EnvIO envIO) => Ff(Value).As().Map(Fg); public override string ToString() => "IO bind map"; } record IOBindBind(A Value, Func> Ff, Func> Fg) : InvokeSyncIO { public override IO Map(Func f) => new IOBindBindMap(Value, Ff, Fg, f); public override IO Bind(Func> f) => new IOBindBind(Value, Ff, x => Fg(x).Bind(f)); public override IO BindAsync(Func>> f) => new IOBindBind(Value, Ff, x => Fg(x).As().BindAsync(f)); public override IO Invoke(EnvIO envIO) => Ff(Value).As().Bind(Fg); public override string ToString() => "IO bind bind"; } record IOBindBindMap(A Value, Func> Ff, Func> Fg, Func Fh) : InvokeSyncIO { public override IO Map(Func f) => new IOBindBindMap(Value, Ff, Fg, x => f(Fh(x))); public override IO Bind(Func> f) => new IOBindBindMapBind(Value, Ff, Fg, Fh, f); public override IO BindAsync(Func>> f) => new IOBindBindMapBind(Value, Ff, Fg, Fh, x => IO.pureVAsync(f(x)).Bind(v => v)); public override IO Invoke(EnvIO envIO) => Ff(Value).As().Bind(Fg).Map(Fh); public override string ToString() => "IO bind bind map"; } record IOBindBindMapBind(A Value, Func> Ff, Func> Fg, Func Fh, Func> Fi) : InvokeSyncIO { public override IO Map(Func f) => new IOBindBindMapBind(Value, Ff, Fg, Fh, x => Fi(x).Map(f)); public override IO Bind(Func> f) => new IOBindBindMapBind(Value, Ff, Fg, Fh, x => Fi(x).Bind(f)); public override IO BindAsync(Func>> f) => new IOBindBindMapBind(Value, Ff, Fg, Fh, x => Fi(x).As().BindAsync(f)); public override IO Invoke(EnvIO envIO) => Ff(Value).As().Bind(Fg).Map(Fh).Bind(Fi); public override string ToString() => "IO bind map bind"; } record IOBindMap(A Value, Func> Ff, Func Fg, Func Fh) : InvokeSyncIO { public override IO Map(Func f) => new IOBindMap(Value, Ff, Fg, x => f(Fh(x))); public override IO Bind(Func> f) => new IOBindMap2(Value, Ff, Fg, x => f(Fh(x))); public override IO BindAsync(Func>> f) => new IOBindMap2(Value, Ff, Fg, x => IO.pureVAsync(f(Fh(x))).Bind(v => v)); public override IO Invoke(EnvIO envIO) => Ff(Value).As().Map(Fg).Map(Fh); public override string ToString() => "IO bind map"; } record IOBindMap2(A Value, Func> Ff, Func Fg, Func> Fh) : InvokeSyncIO { public override IO Map(Func f) => new IOBindMap2(Value, Ff, Fg, x => Fh(x).Map(f)); public override IO Bind(Func> f) => new IOBindMap2(Value, Ff, Fg, x => Fh(x).As().Bind(f)); public override IO BindAsync(Func>> f) => new IOBindMap2(Value, Ff, Fg, x => Fh(x).As().BindAsync(f)); public override IO Invoke(EnvIO envIO) => Ff(Value).As().Map(Fg).Bind(Fh); public override string ToString() => "IO bind map"; } ================================================ FILE: LanguageExt.Core/Effects/IO/DSL/IOCatch.cs ================================================ using System; using System.Threading.Tasks; using LanguageExt.Traits; using LanguageExt.Common; namespace LanguageExt.DSL; /// /// Base-type of the IOCatch〈X, A〉 DSL-type used by the `IO` monad. /// /// Use this to extend the core `IO` exception catching functionality. /// /// Bound value type public abstract record IOCatch : IO { /// /// Provide the `IO` computation to run inside the `try` block /// /// `IO` computation to run inside the `try` block public abstract IO MakeOperation(); /// /// Provide the handler that will accept an `Exception` if one is thrown. /// /// Function to handle exceptions that returns an `IO` public abstract Func> MakeHandler(); public override string ToString() => "IO catch"; } record IOCatch( K Operation, Func Predicate, Func> Failure, K? Final, Func> Next) : IOCatch { public override IO Map(Func f) => new IOCatch(Operation, Predicate, Failure, Final, x => Next(x).Map(f)); public override IO Bind(Func> f) => new IOCatch(Operation, Predicate, Failure, Final, x => Next(x).Bind(f)); public override IO BindAsync(Func>> f) => new IOCatch(Operation, Predicate, Failure, Final, x => Next(x).As().BindAsync(f)); public override Func> MakeHandler() => e => { var err = Error.New(e); return Predicate(err) ? Failure(e).Bind(Next).As() : IO.fail(err); }; public override IO MakeOperation() => Operation.Bind(x => new IOCatchPop(IO.pure(x))).Bind(Next).As(); public override string ToString() => "IO catch"; } ================================================ FILE: LanguageExt.Core/Effects/IO/DSL/IOCatchPop.cs ================================================ using System; using System.Threading.Tasks; using LanguageExt.Traits; namespace LanguageExt.DSL; record IOCatchPop(IO Next) : IO { public override IO Map(Func f) => new IOCatchPop(Next.Map(f)); public override IO Bind(Func> f) => new IOCatchPop(Next.Bind(f)); public override IO BindAsync(Func>> f) => new IOCatchPop(Next.BindAsync(f)); public override string ToString() => "IO catch pop"; } ================================================ FILE: LanguageExt.Core/Effects/IO/DSL/IOEmpty.cs ================================================ using System; using System.Threading.Tasks; using LanguageExt.Common; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt.DSL; record IOEmpty : InvokeSync { internal static readonly IO Default = new IOEmpty(); public override A Invoke(EnvIO envIO) => throw Errors.None; public override IO Map(Func f) => IOEmpty.Default; public override IO Bind(Func> f) => IOEmpty.Default; public override IO BindAsync(Func>> f) => IOEmpty.Default; public override IO ApplyBack(K> f)=> new IOEmptyAsync, B>(f); public override IO Fold( Schedule schedule, S initialState, Func folder) => IOEmpty.Default; public override IO Fold( S initialState, Func folder) => IOEmpty.Default; public override IO FoldWhile( Schedule schedule, S initialState, Func folder, Func stateIs) => IOEmpty.Default; public override IO FoldWhile( S initialState, Func folder, Func stateIs) => IOEmpty.Default; public override IO FoldWhile( Schedule schedule, S initialState, Func folder, Func valueIs) => IOEmpty.Default; public override IO FoldWhile( S initialState, Func folder, Func valueIs) => IOEmpty.Default; public override IO FoldWhile( Schedule schedule, S initialState, Func folder, Func<(S State, A Value), bool> predicate) => IOEmpty.Default; public override IO FoldWhile( S initialState, Func folder, Func<(S State, A Value), bool> predicate) => IOEmpty.Default; public override IO FoldUntil( Schedule schedule, S initialState, Func folder, Func stateIs) => IOEmpty.Default; public override IO FoldUntil( S initialState, Func folder, Func stateIs) => IOEmpty.Default; public override IO FoldUntil( Schedule schedule, S initialState, Func folder, Func valueIs) => IOEmpty.Default; public override IO FoldUntil( S initialState, Func folder, Func valueIs) => IOEmpty.Default; public override IO FoldUntil( S initialState, Func folder, Func<(S State, A Value), bool> predicate) => IOEmpty.Default; public override IO FoldUntil( Schedule schedule, S initialState, Func folder, Func<(S State, A Value), bool> predicate) => IOEmpty.Default; public override IO Post() => Default; protected override IO WithEnv(Func f) => Default; protected override IO WithEnvFail(Func f) => Default; public override IO> Fork(Option timeout = default) => IOEmpty>.Default; public override IO RepeatUntil(Schedule schedule, Func predicate) => Default; public override IO RetryUntil(Schedule schedule, Func predicate) => Default; public override IO Finally(K @finally) => Default; public override IO Catch(Func Predicate, Func> Fail) => Default; } record IOEmptyAsync(K RunFirst) : InvokeAsync { public override async ValueTask Invoke(EnvIO envIO) { ignore(await RunFirst.RunAsync(envIO)); throw Errors.None; } public override IO Map(Func f) => IOEmpty.Default; public override IO Bind(Func> f) => IOEmpty.Default; public override IO BindAsync(Func>> f) => IOEmpty.Default; public override IO ApplyBack(K> f)=> new IOEmptyAsync, B>(f); public override IO Fold( Schedule schedule, S initialState, Func folder) => IOEmpty.Default; public override IO Fold( S initialState, Func folder) => IOEmpty.Default; public override IO FoldWhile( Schedule schedule, S initialState, Func folder, Func stateIs) => IOEmpty.Default; public override IO FoldWhile( S initialState, Func folder, Func stateIs) => IOEmpty.Default; public override IO FoldWhile( Schedule schedule, S initialState, Func folder, Func valueIs) => IOEmpty.Default; public override IO FoldWhile( S initialState, Func folder, Func valueIs) => IOEmpty.Default; public override IO FoldWhile( Schedule schedule, S initialState, Func folder, Func<(S State, A Value), bool> predicate) => IOEmpty.Default; public override IO FoldWhile( S initialState, Func folder, Func<(S State, A Value), bool> predicate) => IOEmpty.Default; public override IO FoldUntil( Schedule schedule, S initialState, Func folder, Func stateIs) => IOEmpty.Default; public override IO FoldUntil( S initialState, Func folder, Func stateIs) => IOEmpty.Default; public override IO FoldUntil( Schedule schedule, S initialState, Func folder, Func valueIs) => IOEmpty.Default; public override IO FoldUntil( S initialState, Func folder, Func valueIs) => IOEmpty.Default; public override IO FoldUntil( S initialState, Func folder, Func<(S State, A Value), bool> predicate) => IOEmpty.Default; public override IO FoldUntil( Schedule schedule, S initialState, Func folder, Func<(S State, A Value), bool> predicate) => IOEmpty.Default; public override IO Post() => IOEmpty.Default; protected override IO WithEnv(Func f) => IOEmpty.Default; protected override IO WithEnvFail(Func f) => IOEmpty.Default; public override IO> Fork(Option timeout = default) => IOEmpty>.Default; public override IO RepeatUntil(Schedule schedule, Func predicate) => IOEmpty.Default; public override IO RetryUntil(Schedule schedule, Func predicate) => IOEmpty.Default; public override IO Finally(K @finally) => IOEmpty.Default; public override IO Catch(Func Predicate, Func> Fail) => IOEmpty.Default; } ================================================ FILE: LanguageExt.Core/Effects/IO/DSL/IOFail.cs ================================================ using System; using System.Threading.Tasks; using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt.DSL; record IOFail(Error Value) : InvokeSync { public override A Invoke(EnvIO envIO) => Value.Throw(); public override IO Map(Func f) => new IOFail(Value); public override IO Bind(Func> f) => new IOFail(Value); public override IO BindAsync(Func>> f) => new IOFail(Value); public override IO Fold( Schedule schedule, S initialState, Func folder) => new IOFail(Value); public override IO Fold( S initialState, Func folder) => new IOFail(Value); public override IO FoldWhile( Schedule schedule, S initialState, Func folder, Func stateIs) => new IOFail(Value); public override IO FoldWhile( S initialState, Func folder, Func stateIs) => new IOFail(Value); public override IO FoldWhile( Schedule schedule, S initialState, Func folder, Func valueIs) => new IOFail(Value); public override IO FoldWhile( S initialState, Func folder, Func valueIs) => new IOFail(Value); public override IO FoldWhile( Schedule schedule, S initialState, Func folder, Func<(S State, A Value), bool> predicate) => new IOFail(Value); public override IO FoldWhile( S initialState, Func folder, Func<(S State, A Value), bool> predicate) => new IOFail(Value); public override IO FoldUntil( Schedule schedule, S initialState, Func folder, Func stateIs) => new IOFail(Value); public override IO FoldUntil( S initialState, Func folder, Func stateIs) => new IOFail(Value); public override IO FoldUntil( Schedule schedule, S initialState, Func folder, Func valueIs) => new IOFail(Value); public override IO FoldUntil( S initialState, Func folder, Func valueIs) => new IOFail(Value); public override IO FoldUntil( S initialState, Func folder, Func<(S State, A Value), bool> predicate) => new IOFail(Value); public override IO FoldUntil( Schedule schedule, S initialState, Func folder, Func<(S State, A Value), bool> predicate) => new IOFail(Value); public override IO Post() => this; protected override IO WithEnv(Func f) => this; protected override IO WithEnvFail(Func f) => this; public override IO> Fork(Option timeout = default) => new IOFail>(Value); public override IO RepeatUntil(Schedule schedule, Func predicate) => this; public override IO RetryUntil(Schedule schedule, Func predicate) => this; public override IO Finally(K @finally) => this; public override IO Catch(Func Predicate, Func> Fail) => Fail(Value).As(); public override string ToString() => $"fail({Value})"; } ================================================ FILE: LanguageExt.Core/Effects/IO/DSL/IOFinal.cs ================================================ using System; using System.Threading.Tasks; using LanguageExt.Traits; namespace LanguageExt.DSL; record IOFinal(K Fa, K Final, Func> Next) : InvokeSyncIO { public override IO Map(Func f) => new IOFinal(Fa, Final, x => Next(x).Map(f)); public override IO Bind(Func> f) => new IOFinal(Fa, Final, x => Next(x).Bind(f)); public override IO BindAsync(Func>> f) => new IOFinal(Fa, Final, x => Next(x).As().BindAsync(f)); public override IO Invoke(EnvIO envIO) { var finalShouldRun = true; try { var task = Fa.RunAsync(envIO); finalShouldRun = false; if (task.IsCompleted) { return Final.As().Bind(_ => Next(task.Result)); } else { return new IOFinalAsync(task, Final, Next); } } finally { if(finalShouldRun) Final.As().Run(envIO); } } public override string ToString() => "IO final"; } record IOFinalAsync(ValueTask Fa, K Final, Func> Next) : InvokeAsyncIO { public override IO Map(Func f) => new IOFinalAsync(Fa, Final, x => Next(x).Map(f)); public override IO Bind(Func> f) => new IOFinalAsync(Fa, Final, x => Next(x).Bind(f)); public override IO BindAsync(Func>> f) => new IOFinalAsync(Fa, Final, x => Next(x).As().BindAsync(f)); public override async ValueTask> Invoke(EnvIO envIO) { try { var value = await Fa; return Next(value).As(); } finally { // ReSharper disable once ReturnValueOfPureMethodIsNotUsed await Final.RunAsync(envIO); } } public override string ToString() => "IO final async"; } ================================================ FILE: LanguageExt.Core/Effects/IO/DSL/IOFold.cs ================================================ using System; using System.Collections.Generic; using System.Threading.Tasks; using LanguageExt.DSL; using LanguageExt.Traits; namespace LanguageExt.DSL; record IOFold( IO Operation, Schedule Schedule, S InitialState, Func Folder, Func> Next) : InvokeSyncIO { public override IO Map(Func f) => new IOFold(Operation, Schedule, InitialState, Folder, x => Next(x).Map(f)); public override IO Bind(Func> f) => new IOFold(Operation, Schedule, InitialState, Folder, x => Next(x).Bind(f)); public override IO BindAsync(Func>> f) => new IOFold(Operation, Schedule, InitialState, Folder, x => Next(x).As().BindAsync(f)); public override IO Invoke(EnvIO envIO) { var task = Operation.RunAsync(envIO); if (task.IsCompleted) { var value = task.Result; var state = Folder(InitialState, value); return new IOFoldingSync(Operation, Schedule.Run().GetEnumerator(), state, Folder, Next); } else { return new IOFoldingInitialAsync( task, Operation, Schedule.Run().GetEnumerator(), InitialState, Folder, Next); } } public override string ToString() => "IO fold"; } record IOFoldingInitialAsync( ValueTask First, IO Operation, IEnumerator Schedule, S State, Func Folder, Func> Next) : InvokeAsyncIO { public override IO Map(Func f) => new IOFoldingInitialAsync(First, Operation, Schedule, State, Folder, x => Next(x).Map(f)); public override IO Bind(Func> f) => new IOFoldingInitialAsync(First, Operation, Schedule, State, Folder, x => Next(x).Bind(f)); public override IO BindAsync(Func>> f) => new IOFoldingInitialAsync(First, Operation, Schedule, State, Folder, x => Next(x).As().BindAsync(f)); public override async ValueTask> Invoke(EnvIO envIO) { var value = await First; var state = Folder(State, value); if (Schedule.MoveNext()) { await Task.Delay((TimeSpan)Schedule.Current, envIO.Token); value = await Operation.RunAsync(envIO); state = Folder(State, value); return new IOFoldingAsync(Operation, Schedule, state, Folder, Next); } else { return Next(State).As(); } } public override string ToString() => "IO folding initial async"; } record IOFoldingAsync( IO Operation, IEnumerator Schedule, S State, Func Folder, Func> Next) : InvokeAsyncIO { public override IO Map(Func f) => new IOFoldingAsync(Operation, Schedule, State, Folder, x => Next(x).Map(f)); public override IO Bind(Func> f) => new IOFoldingAsync(Operation, Schedule, State, Folder, x => Next(x).Bind(f)); public override IO BindAsync(Func>> f) => new IOFoldingAsync(Operation, Schedule, State, Folder, x => Next(x).As().BindAsync(f)); public override async ValueTask> Invoke(EnvIO envIO) { if (Schedule.MoveNext()) { await Task.Delay((TimeSpan)Schedule.Current, envIO.Token); var value = await Operation.RunAsync(envIO); var state = Folder(State, value); return new IOFoldingAsync(Operation, Schedule, state, Folder, Next); } else { return Next(State).As(); } } public override string ToString() => "IO folding async"; } record IOFoldingSync( IO Operation, IEnumerator Schedule, S State, Func Folder, Func> Next) : InvokeSyncIO { public override IO Map(Func f) => new IOFoldingSync(Operation, Schedule, State, Folder, x => Next(x).Map(f)); public override IO Bind(Func> f) => new IOFoldingSync(Operation, Schedule, State, Folder, x => Next(x).Bind(f)); public override IO BindAsync(Func>> f) => new IOFoldingSync(Operation, Schedule, State, Folder, x => Next(x).As().BindAsync(f)); public override IO Invoke(EnvIO envIO) { if (Schedule.MoveNext()) { Task.Delay((TimeSpan)Schedule.Current, envIO.Token).GetAwaiter().GetResult(); var value = Operation.Run(envIO); var state = Folder(State, value); return new IOFoldingSync(Operation, Schedule, state, Folder, Next); } else { return Next(State).As(); } } public override string ToString() => "IO folding sync"; } ================================================ FILE: LanguageExt.Core/Effects/IO/DSL/IOFoldUntil.cs ================================================ using System; using System.Collections.Generic; using System.Threading.Tasks; using LanguageExt.DSL; using LanguageExt.Traits; namespace LanguageExt.DSL; record IOFoldUntil( IO Operation, Schedule Schedule, S InitialState, Func Folder, Func<(S State, A Value), bool> Predicate, Func> Next) : InvokeSyncIO { public override IO Map(Func f) => new IOFoldUntil(Operation, Schedule, InitialState, Folder, Predicate, x => Next(x).Map(f)); public override IO Bind(Func> f) => new IOFoldUntil(Operation, Schedule, InitialState, Folder, Predicate, x => Next(x).Bind(f)); public override IO BindAsync(Func>> f) => new IOFoldUntil(Operation, Schedule, InitialState, Folder, Predicate, x => Next(x).As().BindAsync(f)); public override IO Invoke(EnvIO envIO) { var task = Operation.RunAsync(envIO); if (task.IsCompleted) { var value = task.Result; var state = Folder(InitialState, value); return Predicate((state, value)) ? Next(state).As() : new IOFoldingUntilSync(Operation, Schedule.Run().GetEnumerator(), state, Folder, Predicate, Next); } else { return new IOFoldingUntilInitialAsync( task, Operation, Schedule.Run().GetEnumerator(), InitialState, Folder, Predicate, Next); } } public override string ToString() => "IO fold until"; } record IOFoldingUntilInitialAsync( ValueTask First, IO Operation, IEnumerator Schedule, S State, Func Folder, Func<(S State, A Value), bool> Predicate, Func> Next) : InvokeAsyncIO { public override IO Map(Func f) => new IOFoldingUntilInitialAsync(First, Operation, Schedule, State, Folder, Predicate, x => Next(x).Map(f)); public override IO Bind(Func> f) => new IOFoldingUntilInitialAsync(First, Operation, Schedule, State, Folder, Predicate, x => Next(x).Bind(f)); public override IO BindAsync(Func>> f) => new IOFoldingUntilInitialAsync(First, Operation, Schedule, State, Folder, Predicate, x => Next(x).As().BindAsync(f)); public override async ValueTask> Invoke(EnvIO envIO) { var value = await First; var state = Folder(State, value); if (Predicate((state, value))) { return Next(state).As(); } if (Schedule.MoveNext()) { await Task.Delay((TimeSpan)Schedule.Current, envIO.Token); value = await Operation.RunAsync(envIO); state = Folder(State, value); return Predicate((state, value)) ? Next(state).As() : new IOFoldingUntilAsync(Operation, Schedule, state, Folder, Predicate, Next); } else { return Next(State).As(); } } public override string ToString() => "IO fold until initial async"; } record IOFoldingUntilAsync( IO Operation, IEnumerator Schedule, S State, Func Folder, Func<(S State, A Value), bool> Predicate, Func> Next) : InvokeAsyncIO { public override IO Map(Func f) => new IOFoldingUntilAsync(Operation, Schedule, State, Folder, Predicate, x => Next(x).Map(f)); public override IO Bind(Func> f) => new IOFoldingUntilAsync(Operation, Schedule, State, Folder, Predicate, x => Next(x).Bind(f)); public override IO BindAsync(Func>> f) => new IOFoldingUntilAsync(Operation, Schedule, State, Folder, Predicate, x => Next(x).As().BindAsync(f)); public override async ValueTask> Invoke(EnvIO envIO) { if (Schedule.MoveNext()) { await Task.Delay((TimeSpan)Schedule.Current, envIO.Token); var value = await Operation.RunAsync(envIO); var state = Folder(State, value); return Predicate((state, value)) ? Next(state).As() : new IOFoldingUntilAsync(Operation, Schedule, state, Folder, Predicate, Next); } else { return Next(State).As(); } } public override string ToString() => "IO fold until async"; } record IOFoldingUntilSync( IO Operation, IEnumerator Schedule, S State, Func Folder, Func<(S State, A Value), bool> Predicate, Func> Next) : InvokeSyncIO { public override IO Map(Func f) => new IOFoldingUntilSync(Operation, Schedule, State, Folder, Predicate, x => Next(x).Map(f)); public override IO Bind(Func> f) => new IOFoldingUntilSync(Operation, Schedule, State, Folder, Predicate, x => Next(x).Bind(f)); public override IO BindAsync(Func>> f) => new IOFoldingUntilSync(Operation, Schedule, State, Folder, Predicate, x => Next(x).As().BindAsync(f)); public override IO Invoke(EnvIO envIO) { if (Schedule.MoveNext()) { Task.Delay((TimeSpan)Schedule.Current, envIO.Token).GetAwaiter().GetResult(); var value = Operation.Run(envIO); var state = Folder(State, value); return Predicate((state, value)) ? Next(state).As() : new IOFoldingUntilSync(Operation, Schedule, state, Folder, Predicate, Next); } else { return Next(State).As(); } } public override string ToString() => "IO fold until sync"; } ================================================ FILE: LanguageExt.Core/Effects/IO/DSL/IOFoldWhile.cs ================================================ using System; using System.Collections.Generic; using System.Threading.Tasks; using LanguageExt.DSL; using LanguageExt.Traits; namespace LanguageExt.DSL; record IOFoldWhile( IO Operation, Schedule Schedule, S InitialState, Func Folder, Func<(S State, A Value), bool> Predicate, Func> Next) : InvokeSyncIO { public override IO Map(Func f) => new IOFoldWhile(Operation, Schedule, InitialState, Folder, Predicate, x => Next(x).Map(f)); public override IO Bind(Func> f) => new IOFoldWhile(Operation, Schedule, InitialState, Folder, Predicate, x => Next(x).Bind(f)); public override IO BindAsync(Func>> f) => new IOFoldWhile(Operation, Schedule, InitialState, Folder, Predicate, x => Next(x).As().BindAsync(f)); public override IO Invoke(EnvIO envIO) { var task = Operation.RunAsync(envIO); if (task.IsCompleted) { var value = task.Result; if (!Predicate((InitialState, value))) { return Next(InitialState).As(); } var state = Folder(InitialState, value); return new IOFoldingWhileSync(Operation, Schedule.Run().GetEnumerator(), state, Folder, Predicate, Next); } else { return new IOFoldingWhileInitialAsync( task, Operation, Schedule.Run().GetEnumerator(), InitialState, Folder, Predicate, Next); } } public override string ToString() => "IO fold while"; } record IOFoldingWhileInitialAsync( ValueTask First, IO Operation, IEnumerator Schedule, S State, Func Folder, Func<(S State, A Value), bool> Predicate, Func> Next) : InvokeAsyncIO { public override IO Map(Func f) => new IOFoldingWhileInitialAsync(First, Operation, Schedule, State, Folder, Predicate, x => Next(x).Map(f)); public override IO Bind(Func> f) => new IOFoldingWhileInitialAsync(First, Operation, Schedule, State, Folder, Predicate, x => Next(x).Bind(f)); public override IO BindAsync(Func>> f) => new IOFoldingWhileInitialAsync(First, Operation, Schedule, State, Folder, Predicate, x => Next(x).As().BindAsync(f)); public override async ValueTask> Invoke(EnvIO envIO) { var value = await First; var state = State; if (!Predicate((state, value))) { return Next(state).As(); } state = Folder(State, value); if (Schedule.MoveNext()) { await Task.Delay((TimeSpan)Schedule.Current, envIO.Token); value = await Operation.RunAsync(envIO); if (!Predicate((state, value))) { return Next(State).As(); } state = Folder(State, value); return new IOFoldingWhileAsync(Operation, Schedule, state, Folder, Predicate, Next); } else { return Next(State).As(); } } public override string ToString() => "IO fold while initial async"; } record IOFoldingWhileAsync( IO Operation, IEnumerator Schedule, S State, Func Folder, Func<(S State, A Value), bool> Predicate, Func> Next) : InvokeAsyncIO { public override IO Map(Func f) => new IOFoldingWhileAsync(Operation, Schedule, State, Folder, Predicate, x => Next(x).Map(f)); public override IO Bind(Func> f) => new IOFoldingWhileAsync(Operation, Schedule, State, Folder, Predicate, x => Next(x).Bind(f)); public override IO BindAsync(Func>> f) => new IOFoldingWhileAsync(Operation, Schedule, State, Folder, Predicate, x => Next(x).As().BindAsync(f)); public override async ValueTask> Invoke(EnvIO envIO) { if (Schedule.MoveNext()) { await Task.Delay((TimeSpan)Schedule.Current, envIO.Token); var value = await Operation.RunAsync(envIO); var state = State; if (!Predicate((state, value))) { return Next(State).As(); } state = Folder(State, value); return new IOFoldingWhileAsync(Operation, Schedule, state, Folder, Predicate, Next); } else { return Next(State).As(); } } public override string ToString() => "IO fold while async"; } record IOFoldingWhileSync( IO Operation, IEnumerator Schedule, S State, Func Folder, Func<(S State, A Value), bool> Predicate, Func> Next) : InvokeSyncIO { public override IO Map(Func f) => new IOFoldingWhileSync(Operation, Schedule, State, Folder, Predicate, x => Next(x).Map(f)); public override IO Bind(Func> f) => new IOFoldingWhileSync(Operation, Schedule, State, Folder, Predicate, x => Next(x).Bind(f)); public override IO BindAsync(Func>> f) => new IOFoldingWhileSync(Operation, Schedule, State, Folder, Predicate, x => Next(x).As().BindAsync(f)); public override IO Invoke(EnvIO envIO) { if (Schedule.MoveNext()) { Task.Delay((TimeSpan)Schedule.Current, envIO.Token).GetAwaiter().GetResult(); var value = Operation.Run(envIO); var state = State; if (!Predicate((state, value))) { return Next(State).As(); } state = Folder(State, value); return new IOFoldingWhileSync(Operation, Schedule, state, Folder, Predicate, Next); } else { return Next(State).As(); } } public override string ToString() => "IO fold while sync"; } ================================================ FILE: LanguageExt.Core/Effects/IO/DSL/IOLiftAsync.cs ================================================ using System; using System.Threading.Tasks; using LanguageExt.Traits; namespace LanguageExt.DSL; record IOLiftAsync(Func> F, Func> Next) : InvokeAsyncIO { public override IO Map(Func f) => new IOLiftAsync(F, x => Next(x).Map(f)); public override IO Bind(Func> f) => new IOLiftAsync(F, x => Next(x).Bind(f)); public override IO BindAsync(Func>> f) => new IOLiftAsync(F, x => Next(x).As().BindAsync(f)); public override async ValueTask> Invoke(EnvIO envIO) => Next(await F(envIO)).As(); public override string ToString() => "IO lift async"; } record IOLiftVAsync(Func> F, Func> Next) : InvokeAsyncIO { public override IO Map(Func f) => new IOLiftVAsync(F, x => Next(x).Map(f)); public override IO Bind(Func> f) => new IOLiftVAsync(F, x => Next(x).Bind(f)); public override IO BindAsync(Func>> f) => new IOLiftVAsync(F, x => Next(x).As().BindAsync(f)); public override async ValueTask> Invoke(EnvIO envIO) => Next(await F(envIO)).As(); public override string ToString() => "IO lift vasync"; } ================================================ FILE: LanguageExt.Core/Effects/IO/DSL/IOLiftSync.cs ================================================ using System; using System.Threading.Tasks; using LanguageExt.Traits; namespace LanguageExt.DSL; record IOLiftSync(Func F, Func> Next) : InvokeSyncIO { public override IO Map(Func f) => new IOLiftSync(F, x => Next(x).Map(f)); public override IO Bind(Func> f) => new IOLiftSync(F, x => Next(x).Bind(f)); public override IO BindAsync(Func>> f) => new IOLiftSync(F, x => Next(x).As().BindAsync(f)); public override IO Invoke(EnvIO envIO) => Next(F(envIO)).As(); public override string ToString() => "IO lift sync"; } ================================================ FILE: LanguageExt.Core/Effects/IO/DSL/IOLocal2.cs ================================================ using System; using System.Threading.Tasks; using LanguageExt.Traits; namespace LanguageExt.DSL; record IOLocal(Func MapEnvIO, K Operation, Func> Next) : InvokeAsyncIO { public override IO Map(Func f) => new IOLocal(MapEnvIO, Operation, x => Next(x).Map(f)); public override IO Bind(Func> f) => new IOLocal(MapEnvIO, Operation, x => Next(x).Bind(f)); public override IO BindAsync(Func>> f) => new IOLocal(MapEnvIO, Operation, x => Next(x).As().BindAsync(f)); public override async ValueTask> Invoke(EnvIO envIO) { using var local = MapEnvIO(envIO); return +Next(await Operation.RunAsync(local)); } public override string ToString() => "IO local"; } record IOLocalOnFailOnly(Func MapEnvIO, K Operation, Func> Next) : InvokeAsyncIO { public override IO Map(Func f) => new IOLocalOnFailOnly(MapEnvIO, Operation, x => Next(x).Map(f)); public override IO Bind(Func> f) => new IOLocalOnFailOnly(MapEnvIO, Operation, x => Next(x).Bind(f)); public override IO BindAsync(Func>> f) => new IOLocalOnFailOnly(MapEnvIO, Operation, x => Next(x).As().BindAsync(f)); public override async ValueTask> Invoke(EnvIO envIO) { var local = MapEnvIO(envIO); try { return +Next(await Operation.RunAsync(local)); } catch { local.DisposeResources(); throw; } finally { local.DisposeNonResources(); } } public override string ToString() => "IO local"; } ================================================ FILE: LanguageExt.Core/Effects/IO/DSL/IOMap.cs ================================================ using System; using System.Threading.Tasks; using LanguageExt.Traits; namespace LanguageExt.DSL; record IOMap(Func Ff, IO Fa, Func> Next) : InvokeSyncIO { public override IO Map(Func f) => new IOMap(Ff, Fa, x => Next(x).Map(f)); public override IO Bind(Func> f) => new IOMap(Ff, Fa, x => Next(x).Bind(f)); public override IO BindAsync(Func>> f) => new IOMap(Ff, Fa, x => Next(x).As().BindAsync(f)); public override IO Invoke(EnvIO envIO) { var task = Fa.RunAsync(envIO); return task.IsCompleted ? Next(Ff(task.Result)).As() : new IOPureMapAsync(Ff, task, Next); } public override string ToString() => "IO map"; } record IOPureMap(Func Ff, A Fa, Func> Next) : InvokeSyncIO { public override IO Map(Func f) => new IOPureMap(Ff, Fa, x => Next(x).Map(f)); public override IO Bind(Func> f) => new IOPureMap(Ff, Fa, x => Next(x).Bind(f)); public override IO BindAsync(Func>> f) => new IOPureMap(Ff, Fa, x => Next(x).As().BindAsync(f)); public override IO Invoke(EnvIO envIO) => Next(Ff(Fa)).As(); public override string ToString() => "IO pure map"; } record IOPureMapAsync(Func Ff, ValueTask Fa, Func> Next) : InvokeAsyncIO { public override IO Map(Func f) => new IOPureMapAsync(Ff, Fa, x => Next(x).As().Map(f)); public override IO Bind(Func> f) => new IOPureMapAsync(Ff, Fa, x => Next(x).As().Bind(f)); public override IO BindAsync(Func>> f) => new IOPureMapAsync(Ff, Fa, x => Next(x).As().BindAsync(f)); public override async ValueTask> Invoke(EnvIO envIO) => Next(Ff(await Fa)).As(); public override string ToString() => "IO pure map async"; } ================================================ FILE: LanguageExt.Core/Effects/IO/DSL/IOPure.cs ================================================ using System; using System.Threading.Tasks; using LanguageExt.Traits; namespace LanguageExt.DSL; record IOPure(A Value) : InvokeSync { public override IO Map(Func f) => new IOPureMap(f, Value, IO.pure); public override IO Bind(Func> f) => new IOBind(Value, f); public override IO BindAsync(Func>> f) => new IOBindAsync2(new ValueTask(Value), f); public override A Invoke(EnvIO envIO) => Value; public override string ToString() => $"pure({Value})"; } ================================================ FILE: LanguageExt.Core/Effects/IO/DSL/IOPureAsync.cs ================================================ using System; using System.Threading.Tasks; using LanguageExt.Traits; namespace LanguageExt.DSL; record IOPureAsync(ValueTask Value) : InvokeAsync { public override IO Map(Func f) => new IOPureMapAsync(f, Value, IO.pure); public override IO Bind(Func> f) => new IOBindAsync(Value, f); public override IO BindAsync(Func>> f) => new IOBindAsync2(Value, f); public override async ValueTask Invoke(EnvIO envIO) => await Value; public override string ToString() => "IO pure async"; } ================================================ FILE: LanguageExt.Core/Effects/IO/DSL/IOTail.cs ================================================ using System; using System.Threading.Tasks; using LanguageExt.Traits; namespace LanguageExt.DSL; record IOTail(IO Tail) : IO { public override IO Map(Func f) => throw new NotSupportedException("You can't map a tail call"); public override IO Bind(Func> f) => throw new NotSupportedException("You can't chain a tail call"); public override IO BindAsync(Func>> f) => throw new NotSupportedException("You can't chain a tail call"); public override string ToString() => "IO tail"; public static IO resolve(A initialValue, IO bindResult, Func project) => bindResult switch { IOTail tail when typeof(B) == typeof(C) => (IO)(object)tail.Tail, IOTail => throw new NotSupportedException("Tail calls can't transform in the `select`"), var mb => mb.Map(y => project(initialValue, y)) }; } ================================================ FILE: LanguageExt.Core/Effects/IO/DSL/IOTimeout.cs ================================================ using System; using System.Threading; using System.Threading.Tasks; using LanguageExt.Traits; namespace LanguageExt.DSL; record IOTimeout(IO Fa, TimeSpan TimeLimit, Func> Next) : InvokeSyncIO { public override IO Map(Func f) => new IOTimeout(Fa, TimeLimit, x => Next(x).Map(f)); public override IO Bind(Func> f) => new IOTimeout(Fa, TimeLimit, x => Next(x).Bind(f)); public override IO BindAsync(Func>> f) => new IOTimeout(Fa, TimeLimit, x => Next(x).As().BindAsync(f)); public override IO Invoke(EnvIO envIO) { using var localEnv = envIO.LocalCancelWithTimeout(TimeLimit); var r = Fa.Run(localEnv); return +Next(r); } } ================================================ FILE: LanguageExt.Core/Effects/IO/DSL/IOToken.cs ================================================ using System; using System.Threading; using System.Threading.Tasks; using LanguageExt.Traits; namespace LanguageExt.DSL; record IOToken(Func> Next) : InvokeSyncIO { public override IO Map(Func f) => new IOTokenMap(Next, f); public override IO Bind(Func> f) => new IOTokenBind(Next, f); public override IO BindAsync(Func>> f) => new IOTokenBindAsync(Next, f); public override IO Invoke(EnvIO envIO) => Next(envIO.Token); } record IOTokenMap(Func> Next, Func F) : InvokeSyncIO { public override IO Map(Func f) => new IOTokenMap(Next, x => f(F(x))); public override IO Bind(Func> f) => new IOTokenBind(Next, x => f(F(x))); public override IO BindAsync(Func>> f) => new IOTokenBindAsync(Next, async x => await f(F(x))); public override IO Invoke(EnvIO envIO) => Next(envIO.Token).Map(F); } record IOTokenBind(Func> Next, Func> F) : InvokeSyncIO { public override IO Map(Func f) => new IOTokenBind(Next, x => F(x).Map(f)); public override IO Bind(Func> f) => new IOTokenBind(Next, x => F(x).Bind(f)); public override IO BindAsync(Func>> f) => new IOTokenBind(Next, x => F(x).As().BindAsync(f)); public override IO Invoke(EnvIO envIO) => Next(envIO.Token).Bind(F); } record IOTokenBindAsync(Func> Next, Func>> F) : InvokeSyncIO { public override IO Map(Func f) => new IOTokenBindAsync(Next, async x => (await F(x)).Map(f)); public override IO Bind(Func> f) => new IOTokenBindAsync(Next, async x => (await F(x)).Bind(f)); public override IO BindAsync(Func>> f) => new IOTokenBindAsync(Next, async x => (await F(x)).As().BindAsync(f)); public override IO Invoke(EnvIO envIO) => Next(envIO.Token).BindAsync(F); } ================================================ FILE: LanguageExt.Core/Effects/IO/DSL/IOUninterruptible.cs ================================================ using System; using System.Threading; using System.Threading.Tasks; using LanguageExt.Traits; namespace LanguageExt.DSL; record IOUninterruptible(IO Fa, Func> Next) : InvokeSyncIO { public override IO Map(Func f) => new IOUninterruptible(Fa, x => Next(x).Map(f)); public override IO Bind(Func> f) => new IOUninterruptible(Fa, x => Next(x).Bind(f)); public override IO BindAsync(Func>> f) => new IOUninterruptible(Fa, x => Next(x).As().BindAsync(f)); public override IO Invoke(EnvIO envIO) { var localEnv = EnvIO.New(envIO.Resources, CancellationToken.None, null, envIO.SyncContext); var tr = Fa.RunAsync(localEnv); return tr.IsCompleted ? +Next(tr.Result) : new IOUninterruptibleAsync(tr, localEnv, Next); } } record IOUninterruptibleAsync(ValueTask Fa, EnvIO LocalEnv, Func> Next) : InvokeAsyncIO { public override IO Map(Func f) => new IOUninterruptibleAsync(Fa, LocalEnv, x => Next(x).Map(f)); public override IO Bind(Func> f) => new IOUninterruptibleAsync(Fa, LocalEnv, x => Next(x).Bind(f)); public override IO BindAsync(Func>> f) => new IOUninterruptibleAsync(Fa, LocalEnv, x => Next(x).As().BindAsync(f)); public override async ValueTask> Invoke(EnvIO envIO) { var r = await Fa; LocalEnv.Dispose(); return +Next(r); } } ================================================ FILE: LanguageExt.Core/Effects/IO/DSL/IOUse.cs ================================================ using System; using System.Threading.Tasks; using LanguageExt.Traits; namespace LanguageExt.DSL; record IOUse(IO Acquire, Func> Release, Func> Next) : InvokeSyncIO { public override IO Map(Func f) => new IOUse(Acquire, Release, x => Next(x).Map(f)); public override IO Bind(Func> f) => new IOUse(Acquire, Release, x => Next(x).Bind(f)); public override IO BindAsync(Func>> f) => new IOUse(Acquire, Release, x => Next(x).BindAsync(f)); public override IO Invoke(EnvIO envIO) { var task = Acquire.RunAsync(envIO); if (task.IsCompleted) { envIO.Resources.Acquire(task.Result, Release); return Next(task.Result); } else { return new IOAcquireAsync(task, Release, Next); } } public override string ToString() => "IO use"; } record IOUseDisposable(IO Acquire, Func> Next) : InvokeSyncIO where X : IDisposable { public override IO Map(Func f) => new IOUseDisposable(Acquire, x => Next(x).Map(f)); public override IO Bind(Func> f) => new IOUseDisposable(Acquire, x => Next(x).Bind(f)); public override IO BindAsync(Func>> f) => new IOUseDisposable(Acquire, x => Next(x).BindAsync(f)); public override IO Invoke(EnvIO envIO) { var task = Acquire.RunAsync(envIO); if (task.IsCompleted) { envIO.Resources.Acquire(task.Result); return Next(task.Result); } else { return new IOAcquireDisposableAsync(task, Next); } } public override string ToString() => "IO use disposable"; } record IOUseAsyncDisposable(IO Acquire, Func> Next) : InvokeSyncIO where X : IAsyncDisposable { public override IO Map(Func f) => new IOUseAsyncDisposable(Acquire, x => Next(x).Map(f)); public override IO Bind(Func> f) => new IOUseAsyncDisposable(Acquire, x => Next(x).Bind(f)); public override IO BindAsync(Func>> f) => new IOUseAsyncDisposable(Acquire, x => Next(x).BindAsync(f)); public override IO Invoke(EnvIO envIO) { var task = Acquire.RunAsync(envIO); if (task.IsCompleted) { envIO.Resources.AcquireAsync(task.Result); return Next(task.Result); } else { return new IOAcquireAsyncDisposableAsync(task, Next); } } public override string ToString() => "IO pure async disposable"; } record IOAcquireAsync(ValueTask Value, Func> Release, Func> Next) : InvokeAsyncIO { public override IO Map(Func f) => new IOAcquireAsync(Value, Release, x => Next(x).Map(f)); public override IO Bind(Func> f) => new IOAcquireAsync(Value, Release, x => Next(x).Bind(f)); public override IO BindAsync(Func>> f) => new IOAcquireAsync(Value, Release, x => Next(x).BindAsync(f)); public override async ValueTask> Invoke(EnvIO envIO) { var value = await Value; envIO.Resources.Acquire(value, Release); return Next(value); } public override string ToString() => "IO acquire async"; } record IOAcquireDisposableAsync(ValueTask Value, Func> Next) : InvokeAsyncIO where X : IDisposable { public override IO Map(Func f) => new IOAcquireDisposableAsync(Value, x => Next(x).Map(f)); public override IO Bind(Func> f) => new IOAcquireDisposableAsync(Value, x => Next(x).Bind(f)); public override IO BindAsync(Func>> f) => new IOAcquireDisposableAsync(Value, x => Next(x).BindAsync(f)); public override async ValueTask> Invoke(EnvIO envIO) { var value = await Value; envIO.Resources.Acquire(value); return Next(value); } public override string ToString() => "IO acquire disposable async"; } record IOAcquireAsyncDisposableAsync(ValueTask Value, Func> Next) : InvokeAsyncIO where X : IAsyncDisposable { public override IO Map(Func f) => new IOAcquireAsyncDisposableAsync(Value, x => Next(x).Map(f)); public override IO Bind(Func> f) => new IOAcquireAsyncDisposableAsync(Value, x => Next(x).Bind(f)); public override IO BindAsync(Func>> f) => new IOAcquireAsyncDisposableAsync(Value, x => Next(x).BindAsync(f)); public override async ValueTask> Invoke(EnvIO envIO) { var value = await Value; envIO.Resources.AcquireAsync(value); return Next(value); } public override string ToString() => "IO acquire async disposable async"; } ================================================ FILE: LanguageExt.Core/Effects/IO/DSL/InvokeAsync.cs ================================================ using System.Threading.Tasks; namespace LanguageExt.DSL; /// /// Base-type of the DSL types used by the `IO` monad. Use this to extend the core `IO` functionality. /// /// Bound value type public abstract record InvokeAsync : IO { public abstract ValueTask Invoke(EnvIO envIO); public override string ToString() => "IO invoke async"; } ================================================ FILE: LanguageExt.Core/Effects/IO/DSL/InvokeAsyncIO.cs ================================================ using System.Threading.Tasks; namespace LanguageExt.DSL; /// /// Base-type of the DSL types used by the `IO` monad. Use this to extend the core `IO` functionality. /// /// Bound value type public abstract record InvokeAsyncIO : IO { public abstract ValueTask> Invoke(EnvIO envIO); public override string ToString() => "IO invoke async"; } ================================================ FILE: LanguageExt.Core/Effects/IO/DSL/InvokeSync.cs ================================================ namespace LanguageExt.DSL; /// /// Base-type of the DSL types used by the `IO` monad. Use this to extend the core `IO` functionality. /// /// Bound value type public abstract record InvokeSync : IO { public abstract A Invoke(EnvIO envIO); public override string ToString() => "IO invoke sync"; } ================================================ FILE: LanguageExt.Core/Effects/IO/DSL/InvokeSyncIO.cs ================================================ namespace LanguageExt.DSL; /// /// Base-type of the DSL types used by the `IO` monad. Use this to extend the core `IO` functionality. /// /// Bound value type public abstract record InvokeSyncIO : IO { public abstract IO Invoke(EnvIO envIO); public override string ToString() => "IO invoke sync"; } ================================================ FILE: LanguageExt.Core/Effects/IO/EnvIO.cs ================================================ using System; using System.Threading; namespace LanguageExt; /// /// Environment for the IO monad /// public class EnvIO : IDisposable { public readonly Resources Resources; public readonly CancellationToken Token; public readonly CancellationTokenSource Source; public readonly SynchronizationContext? SyncContext; readonly CancellationTokenRegistration? Registration; readonly int Own; int resourcesDisposed; int nonResourcesDisposed; static readonly EnvIO DisposeEnv = new( new Resources(null), CancellationToken.None, new CancellationTokenSource(), null, null, 0); internal EnvIO(Resources resources, CancellationToken token, CancellationTokenSource source, SynchronizationContext? syncContext, CancellationTokenRegistration? registration, int own) { Resources = resources; Token = token; Source = source; SyncContext = syncContext; Registration = registration; Own = own; resourcesDisposed = 0; nonResourcesDisposed = 0; } public static EnvIO New( Resources? resources = null, CancellationToken token = default, CancellationTokenSource? source = null, SynchronizationContext? syncContext = null, TimeSpan? timeout = null) { var own = 0; CancellationTokenRegistration? reg = null; if (source is null) { source = timeout is null ? new CancellationTokenSource() : new CancellationTokenSource(timeout.Value); own |= 1; } if (resources is null) { resources = new Resources(null); own |= 2; } if ((own & 1) == 1) { if (token.CanBeCanceled) { reg = token.Register(() => source.Cancel()); } token = source.Token; } syncContext ??= SynchronizationContext.Current; return new EnvIO(resources, token, source, syncContext, reg, own); } public EnvIO Local => New(null, Token, null, SynchronizationContext.Current); public EnvIO LocalWithTimeout(TimeSpan timeout) => New(null, Token, null, SynchronizationContext.Current, timeout); public EnvIO LocalResources => New(null, Token, Source, SyncContext); public EnvIO LocalCancel => New(Resources, Token, null, SyncContext); public EnvIO LocalSyncContext => New(Resources, Token, Source, SynchronizationContext.Current); public EnvIO LocalCancelWithTimeout(TimeSpan timeout) => New(Resources, Token, null, SyncContext, timeout); public void Dispose() { DisposeResources(); DisposeNonResources(); } public void DisposeResources() { if (Interlocked.CompareExchange(ref resourcesDisposed, 1, 0) == 0) { if ((Own & 2) == 2) { var env = New(); Resources.DisposeU(env); } } } public void DisposeNonResources() { if (Interlocked.CompareExchange(ref nonResourcesDisposed, 1, 0) == 0) { if ((Own & 1) == 1) Source.Dispose(); Registration?.Dispose(); } } public override string ToString() => "EnvIO"; } ================================================ FILE: LanguageExt.Core/Effects/IO/Extensions/IO.Extensions.MapApply.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public static partial class IOExtensions { /// /// Functor map operation /// /// /// Unwraps the value within the functor, passes it to the map function `f` provided, and /// then takes the mapped value and wraps it back up into a new functor. /// /// Functor to map /// Mapping function /// Mapped functor public static IO Map(this Func f, K ma) => Functor.map(f, ma).As(); /// /// Functor map operation /// /// /// Unwraps the value within the functor, passes it to the map function `f` provided, and /// then takes the mapped value and wraps it back up into a new functor. /// /// Functor to map /// Mapping function /// Mapped functor public static IO Map(this Func f, IO ma) => Functor.map(f, ma).As(); /// /// Applicative action: runs the first applicative, ignores the result, and returns the second applicative /// public static IO Action(this IO ma, IO mb) => Applicative.action(ma, mb).As(); /// /// Applicative functor apply operation /// /// /// Unwraps the value within the `ma` applicative-functor, passes it to the unwrapped function(s) within `mf`, and /// then takes the resulting value and wraps it back up into a new applicative-functor. /// /// Value(s) applicative functor /// Mapping function(s) /// Mapped applicative functor public static IO Apply(this IO> mf, K ma) => Applicative.apply(mf, ma).As(); /// /// Applicative functor apply operation /// /// /// Unwraps the value within the `ma` applicative-functor, passes it to the unwrapped function(s) within `mf`, and /// then takes the resulting value and wraps it back up into a new applicative-functor. /// /// Value(s) applicative functor /// Mapping function(s) /// Mapped applicative functor public static IO Apply(this K> mf, K ma) => Applicative.apply(mf, ma).As(); } ================================================ FILE: LanguageExt.Core/Effects/IO/Extensions/IO.Extensions.cs ================================================ using System; using System.Diagnostics.Contracts; using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; using LanguageExt.DSL; using LanguageExt.Traits; namespace LanguageExt; public static partial class IOExtensions { extension(K ma) { /// /// Convert the kind version of the `IO` monad to an `IO` monad. /// /// /// This is a simple cast operation which is just a bit more elegant /// than manually casting. /// /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public IO As() => (IO)ma; [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public A Run() => ma.As().Run(); [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public A Run(EnvIO envIO) => ma.As().Run(envIO); [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Fin RunSafe() => ma.As().Try().Run().Run(); [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Fin RunSafe(EnvIO envIO) => ma.As().Try().Run().Run(envIO); [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public ValueTask RunAsync() => ma.As().RunAsync(); [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public ValueTask RunAsync(EnvIO envIO) => ma.As().RunAsync(envIO); [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public ValueTask> RunSafeAsync() => ma.As().Try().Run().RunAsync(); [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public ValueTask> RunSafeAsync(EnvIO envIO) => ma.As().Try().Run().RunAsync(envIO); } extension(K ma) where M : MonadIO { public K Bind(Func> f) => M.LiftIO(ma).Bind(f); public K BindAsync(Func>> f) => ma.Bind(x => M.LiftIO(new IOPureAsync>(f(x)))).Flatten(); } /// /// Get the outer task and wrap it up in a new IO within the IO /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static IO Flatten(this Task> tma) => IO.liftAsync(async () => await tma.ConfigureAwait(false)) .Flatten(); /// /// Unwrap the inner IO to flatten the structure /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static IO Flatten(this IO> mma) => mma.Bind(x => x); [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static IO SelectMany(this K ma, Func> bind, Func project) => ma.As().SelectMany(bind, project); [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static OptionT SelectMany(this K ma, Func> bind, Func project) where M : MonadIO, Alternative => OptionT.liftIO(ma.As()).SelectMany(bind, project); [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TryT SelectMany(this K ma, Func> bind, Func project) where M : MonadIO, Alternative => TryT.liftIO(ma.As()).SelectMany(bind, project); [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static EitherT SelectMany(this K ma, Func> bind, Func project) where M : MonadIO, Alternative => EitherT.liftIO(ma.As()).SelectMany(bind, project); [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static FinT SelectMany(this K ma, Func> bind, Func project) where M : MonadIO, Alternative => FinT.liftIO(ma.As()).SelectMany(bind, project); [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ValidationT SelectMany(this K ma, Func> bind, Func project) where F : Monoid where M : MonadIO, Alternative => ValidationT.liftIO(ma.As()).SelectMany(bind, project); [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ReaderT SelectMany(this K ma, Func> bind, Func project) where M : MonadIO, Alternative => ReaderT.LiftIO(ma.As()).SelectMany(bind, project); [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static StateT SelectMany(this K ma, Func> bind, Func project) where M : MonadIO, Alternative => StateT.LiftIO(ma.As()).SelectMany(bind, project); /// /// Wait for a signal /// public static IO WaitOneIO(this AutoResetEvent wait) => IO.liftAsync(e => wait.WaitOneAsync(e.Token)); /// /// Wait for a signal /// public static K WaitOneIO(this AutoResetEvent wait) where M : Monad => M.LiftIOMaybe(wait.WaitOneIO()); } ================================================ FILE: LanguageExt.Core/Effects/IO/Extensions/IO.Guard.cs ================================================ using System; using LanguageExt.Common; using static LanguageExt.Prelude; namespace LanguageExt; public static class IOGuardExtensions { extension(Guard guard) { /// /// Monadic binding support for `IO` /// public IO Bind(Func> f) => guard.Flag ? f(unit).As() : Fail(guard.OnFalse()); /// /// Monadic binding support for `IO` /// public IO SelectMany(Func> bind, Func project) => guard.Flag ? bind(default).As().Map(b => project(default, b)) : Fail(guard.OnFalse()); /// /// Natural transformation to `IO` /// public IO ToIO() => IO.lift(() => guard.Flag ? unit : guard.OnFalse().Throw()); } } ================================================ FILE: LanguageExt.Core/Effects/IO/ForkIO.cs ================================================ using System.Threading.Tasks; namespace LanguageExt; /// /// Result of forking an `IO` monad /// /// An `IO` monad, which, if invoked, would cancel the forked IO operation /// An `IO` monad, which, if invoked, would await the result of the forked /// `IO` operation. Obviously, this mitigates the reasons for forking somewhat, but this struct /// could be passed to another process that does the awaiting - and so still has some value. /// Bound value type public readonly record struct ForkIO( IO Cancel, IO Await); ================================================ FILE: LanguageExt.Core/Effects/IO/IO.Module.cs ================================================ using System; using LanguageExt.DSL; using System.Threading; using LanguageExt.Common; using LanguageExt.Traits; using System.Threading.Tasks; using static LanguageExt.Prelude; using System.Diagnostics.Contracts; namespace LanguageExt; public partial class IO { /// /// Lift a pure value into an IO computation /// /// value /// Bound value type /// IO in a success state. Always yields the lifted value. [Pure] public static IO pure(A value) => new IOPure(value); /// /// Lift a pure value into an IO computation /// /// value /// Bound value type /// IO in a success state. Always yields the lifted value. [Pure] internal static IO pureAsync(Task value) => new IOPureAsync(new ValueTask(value)); /// /// Lift a pure value into an IO computation /// /// value /// Bound value type /// IO in a success state. Always yields the lifted value. [Pure] internal static IO pureVAsync(ValueTask value) => new IOPureAsync(value); /// /// Put the IO into a failure state /// /// Error value /// Bound value type /// IO in a failed state. Always yields an error. [Pure] public static IO fail(Error value) => new IOFail(value); /// /// Put the IO into a failure state /// /// Error value /// Bound value type /// IO in a failed state. Always yields an error. [Pure] public static IO fail(string value) => fail(Error.New(value)); /// /// Lift an action into the IO monad /// /// Action to lift [Pure] public static IO lift(Action f) => lift(() => { f(); return unit; }); [Pure] public static IO lift(Either ma) => ma switch { Either.Right (var r) => pure(r), Either.Left (var l) => fail(l), _ => fail(Errors.Bottom) }; [Pure] public static IO lift(Fin ma) => lift(ma.ToEither()); [Pure] public static IO lift(Func f) => new IOLiftSync(_ => f(), pure); [Pure] public static IO lift(Func f) => new IOLiftSync(f, pure); [Pure] public static IO lift(Func> f) => lift(() => f().ThrowIfFail()); [Pure] public static IO lift(Func> f) => lift(e => f(e).ThrowIfFail()); [Pure] public static IO lift(Func> f) => lift(() => f().ToFin().ThrowIfFail()); [Pure] public static IO lift(Func> f) => lift(e => f(e).ToFin().ThrowIfFail()); [Pure] public static IO liftAsync(Func> f) => new IOLiftAsync(_ => f(), pure); [Pure] public static IO liftAsync(Func> f) => new IOLiftAsync(f, pure); [Pure] public static IO liftVAsync(Func> f) => new IOLiftVAsync(_ => f(), pure); [Pure] public static IO liftVAsync(Func> f) => new IOLiftVAsync(f, pure); /// /// Wraps this computation in a local-environment that ignores any cancellation-token cancellation requests. /// /// An uninterruptible computation [Pure] public static IO uninterruptible(IO ma) => ma.Uninterruptible(); /// /// Creates a local cancellation environment /// /// /// A local cancellation environment stops other IO computations, that rely on the same /// environmental cancellation token, from being taken down by a regional cancellation. /// /// If an `IO.cancel` is invoked locally, then it will still create an exception that /// propagates upwards and so catching cancellations is still important. /// /// Computation to run within the local context /// Bound value /// Result of the computation [Pure] public static IO local(K ma) => ma.As().Local(); public static readonly IO env = lift(e => e); public static readonly IO token = new IOToken(pure); public static readonly IO source = lift(e => e.Source); public static readonly IO> syncContext = lift(e => Optional(e.SyncContext)); [Pure] public static IO empty() => IO.Empty; [Pure] public static IO combine(K ma, K mb) => ma.As() | mb.As(); /// /// Yield the thread for the specified duration or until cancelled. /// /// Amount of time to yield for /// Unit [Pure] public static IO yieldFor(Duration duration) => Math.Abs(duration.Milliseconds) < 0.00000001 ? unitIO : liftAsync(e => yieldFor(duration, e.Token)); /// /// Yield the thread for the specified duration or until cancelled. /// /// Amount of time to yield for /// Unit [Pure] public static IO yieldFor(TimeSpan timeSpan) => Math.Abs(timeSpan.TotalMilliseconds) < 0.00000001 ? unitIO : liftAsync(e => yieldFor(timeSpan, e.Token)); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Internal // /// /// Yields the thread for the `Duration` specified allowing for concurrency /// on the current thread /// internal static async Task yieldFor(Duration d, CancellationToken token) { await Task.Delay((TimeSpan)d, token); return unit; } } ================================================ FILE: LanguageExt.Core/Effects/IO/IO.Monad.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using LanguageExt.Common; using LanguageExt.DSL; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; public partial class IO : MonadUnliftIO, Final, Fallible, Alternative { static K Applicative.Apply(K> mf, K ma) => (mf, ma) switch { (_, IOEmpty) => IOEmpty.Default, (IOEmpty>, _) => IOEmpty.Default, _ => ma.As().ApplyBack(mf.As()), }; static K Applicative.Apply(K> mf, Memo ma) => mf switch { IOEmpty> => IOEmpty.Default, IOFail>(var e) => new IOFail(e), _ => ma.Value.As().ApplyBack(mf) }; static K Applicative.Action(K ma, K mb) => ma switch { IOEmpty => IOEmpty.Default, IOFail(var e) => new IOFail(e), _ => new IOAction(ma, mb, pure) }; static K Applicative.Action(K ma, Memo mb) => ma switch { IOEmpty => IOEmpty.Default, IOFail(var e) => new IOFail(e), _ => new IOActionLazy(ma, mb, pure) }; static K Applicative.Actions(IterableNE> fas) => new IOActions(fas, pure); static K Monad.Bind(K ma, Func> f) => ma switch { IOEmpty => IOEmpty.Default, IOFail(var e) => new IOFail(e), IO io => io.Bind(f), _ => throw new NotSupportedException() }; static K Monad.Recur(A value, Func>> f) => liftVAsync(async e => { while (true) { if(e.Token.IsCancellationRequested) throw new OperationCanceledException(); var next = await f(value).As().RunAsync(e); if (next.IsDone) return next.Done; value = next.Loop; } }); static K Functor.Map(Func f, K ma) => ma is IOEmpty ? IOEmpty.Default : ma.As().Map(f); static K Applicative.Pure(A value) => new IOPure(value); static K Fallible.Fail(Error error) => fail(error); static K Fallible.Catch( K fa, Func Predicate, Func> Fail) => fa switch { IOEmpty => Predicate(Errors.None) ? Fail(Errors.None) : fa, IOFail(var e) => Predicate(e) ? Fail(e) : fa, _ => new IOCatch(fa, Predicate, Fail, null, pure), }; static K Choice.Choose(K fa, K fb) => fa switch { IOEmpty => fb, IOFail => fb, _ => new IOCatch(fa, _ => true, _ => fb, null, pure) }; static K Choice.Choose(K fa, Memo fb) => fa switch { IOEmpty => fb.Value, IOFail => fb.Value, _ => new IOCatch(fa, _ => true, _ => fb.Value, null, pure) }; static K Alternative.Empty() => empty(); static K MonadIO.LiftIO(IO ma) => ma; static K MonadUnliftIO.MapIO(K ma, Func, IO> f) => ma is IOEmpty ? IOEmpty.Default : f(ma.As()); static K> MonadUnliftIO.ToIO(K ma) => pure(ma.As()); static K Final.Finally(K fa, K @finally) => fa is IOEmpty ? fa : new IOFinal(fa, @finally, pure); /// /// Creates a local cancellation environment /// /// /// A local cancellation environment stops other IO computations, that rely on the same /// environmental cancellation token, from being taken down by a regional cancellation. /// /// If an `IO.cancel` is invoked locally, then it will still create an exception that /// propagates upwards and so catching cancellations is still important. /// /// Computation to run within the local context /// Bound value /// Result of the computation static K MonadUnliftIO.LocalIO(K ma) => ma is IOEmpty ? ma : ma.As().Local(); /// /// Make this IO computation run on the `SynchronizationContext` that was captured at the start /// of the IO chain (i.e. the one embedded within the `EnvIO` environment that is passed through /// all IO computations) /// static K MonadUnliftIO.PostIO(K ma) => ma is IOEmpty ? ma : ma.As().Post(); /// /// Await a forked operation /// static K MonadUnliftIO.Await(K> ma) => ma is IOEmpty ? IOEmpty.Default : ma.As().Bind(f => f.Await); /// /// Queue this IO operation to run on the thread-pool. /// /// Maximum time that the forked IO operation can run for. `None` for no timeout. /// Returns a `ForkIO` data-structure that contains two IO effects that can be used to either cancel /// the forked IO operation or to await the result of it. /// static K> MonadUnliftIO.ForkIO(K ma, Option timeout) => ma is IOEmpty ? IOEmpty>.Default : ma.As().Fork(timeout); /// /// Timeout operation if it takes too long /// static K MonadUnliftIO.TimeoutIO(K ma, TimeSpan timeout) => ma is IOEmpty ? IOEmpty.Default : ma.As().Timeout(timeout); /// /// The IO monad tracks resources automatically, this creates a local resource environment /// to run this computation in. Once the computation has completed any resources acquired /// are automatically released. Imagine this as the ultimate `using` statement. /// static K MonadUnliftIO.BracketIO(K ma) => ma is IOEmpty ? IOEmpty.Default : ma.As().Bracket(); /// /// When acquiring, using, and releasing various resources, it can be quite convenient to write a function to manage /// the acquisition and releasing, taking a function of the acquired value that specifies an action to be performed /// in between. /// /// Resource acquisition /// Function to use the acquired resource /// Function to invoke to release the resource static K MonadUnliftIO.BracketIO( K Acq, Func> Use, Func> Fin) => Acq is IOEmpty ? IOEmpty.Default : Acq.As().Bracket(Use, Fin); /// /// When acquiring, using, and releasing various resources, it can be quite convenient to write a function to manage /// the acquisition and releasing, taking a function of the acquired value that specifies an action to be performed /// in between. /// /// Resource acquisition /// Function to use the acquired resource /// Function to run to handle any exceptions /// Function to invoke to release the resource static K MonadUnliftIO.BracketIO( K Acq, Func> Use, Func> Catch, Func> Fin) => Acq is IOEmpty ? IOEmpty.Default : Acq.As().Bracket(Use, Catch, Fin); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Repeating the effect // /// /// Keeps repeating the computation forever, or until an error occurs /// /// /// Any resources acquired within a repeated IO computation will automatically be released. This also means you can't /// acquire resources and return them from within a repeated computation. /// /// The result of the last invocation static K MonadUnliftIO.RepeatIO(K ma) => ma is IOEmpty ? IOEmpty.Default : ma.As().Repeat(); /// /// Keeps repeating the computation, until the scheduler expires, or an error occurs /// /// /// Any resources acquired within a repeated IO computation will automatically be released. This also means you can't /// acquire resources and return them from within a repeated computation. /// /// Scheduler strategy for repeating /// The result of the last invocation static K MonadUnliftIO.RepeatIO( K ma, Schedule schedule) => ma is IOEmpty ? IOEmpty.Default : ma.As().Repeat(schedule); /// /// Keeps repeating the computation until the predicate returns false, or an error occurs /// /// /// Any resources acquired within a repeated IO computation will automatically be released. This also means you can't /// acquire resources and return them from within a repeated computation. /// /// Keep repeating while this predicate returns `true` for each computed value /// The result of the last invocation static K MonadUnliftIO.RepeatWhileIO( K ma, Func predicate) => ma is IOEmpty ? IOEmpty.Default : ma.As().RepeatWhile(predicate); /// /// Keeps repeating the computation, until the scheduler expires, or the predicate returns false, or an error occurs /// /// /// Any resources acquired within a repeated IO computation will automatically be released. This also means you can't /// acquire resources and return them from within a repeated computation. /// /// Scheduler strategy for repeating /// Keep repeating while this predicate returns `true` for each computed value /// The result of the last invocation static K MonadUnliftIO.RepeatWhileIO( K ma, Schedule schedule, Func predicate) => ma is IOEmpty ? IOEmpty.Default : ma.As().RepeatWhile(schedule, predicate); /// /// Keeps repeating the computation until the predicate returns true, or an error occurs /// /// /// Any resources acquired within a repeated IO computation will automatically be released. This also means you can't /// acquire resources and return them from within a repeated computation. /// /// Keep repeating until this predicate returns `true` for each computed value /// The result of the last invocation static K MonadUnliftIO.RepeatUntilIO( K ma, Func predicate) => ma is IOEmpty ? IOEmpty.Default : ma.As().RepeatUntil(predicate); /// /// Keeps repeating the computation, until the scheduler expires, or the predicate returns true, or an error occurs /// /// /// Any resources acquired within a repeated IO computation will automatically be released. This also means you can't /// acquire resources and return them from within a repeated computation. /// /// Scheduler strategy for repeating /// Keep repeating until this predicate returns `true` for each computed value /// The result of the last invocation static K MonadUnliftIO.RepeatUntilIO( K ma, Schedule schedule, Func predicate) => ma is IOEmpty ? IOEmpty.Default : ma.As().RepeatUntil(schedule, predicate); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Retrying the effect when it fails // /// /// Retry if the IO computation fails /// /// /// This variant will retry forever /// /// /// Any resources acquired within a retrying IO computation will automatically be released *if* the operation fails. /// So, successive retries will not grow the acquired resources on each retry iteration. Any successful operation that /// acquires resources will have them tracked in the usual way. /// static K MonadUnliftIO.RetryIO(K ma) => ma is IOEmpty ? IOEmpty.Default : ma.As().Retry(); /// /// Retry if the IO computation fails /// /// /// This variant will retry until the schedule expires /// /// /// Any resources acquired within a retrying IO computation will automatically be released *if* the operation fails. /// So, successive retries will not grow the acquired resources on each retry iteration. Any successful operation that /// acquires resources will have them tracked in the usual way. /// static K MonadUnliftIO.RetryIO( K ma, Schedule schedule) => ma is IOEmpty ? IOEmpty.Default : ma.As().Retry(schedule); /// /// Retry if the IO computation fails /// /// /// This variant will keep retrying whilst the predicate returns `true` for the error generated at each iteration; /// at which point the last raised error will be thrown. /// /// /// Any resources acquired within a retrying IO computation will automatically be released *if* the operation fails. /// So, successive retries will not grow the acquired resources on each retry iteration. Any successful operation that /// acquires resources will have them tracked in the usual way. /// static K MonadUnliftIO.RetryWhileIO( K ma, Func predicate) => ma is IOEmpty ? IOEmpty.Default : ma.As().RetryWhile(predicate); /// /// Retry if the IO computation fails /// /// /// This variant will keep retrying whilst the predicate returns `true` for the error generated at each iteration; /// or, until the schedule expires; at which point the last raised error will be thrown. /// /// /// Any resources acquired within a retrying IO computation will automatically be released *if* the operation fails. /// So, successive retries will not grow the acquired resources on each retry iteration. Any successful operation that /// acquires resources will have them tracked in the usual way. /// static K MonadUnliftIO.RetryWhileIO( K ma, Schedule schedule, Func predicate) => ma is IOEmpty ? IOEmpty.Default : ma.As().RetryWhile(schedule, predicate); /// /// Retry if the IO computation fails /// /// /// This variant will keep retrying until the predicate returns `true` for the error generated at each iteration; /// at which point the last raised error will be thrown. /// /// /// Any resources acquired within a retrying IO computation will automatically be released *if* the operation fails. /// So, successive retries will not grow the acquired resources on each retry iteration. Any successful operation that /// acquires resources will have them tracked in the usual way. /// static K MonadUnliftIO.RetryUntilIO( K ma, Func predicate) => ma is IOEmpty ? IOEmpty.Default : ma.As().RetryUntil(predicate); /// /// Retry if the IO computation fails /// /// /// This variant will keep retrying until the predicate returns `true` for the error generated at each iteration; /// or, until the schedule expires; at which point the last raised error will be thrown. /// /// /// Any resources acquired within a retrying IO computation will automatically be released *if* the operation fails. /// So, successive retries will not grow the acquired resources on each retry iteration. Any successful operation that /// acquires resources will have them tracked in the usual way. /// static K MonadUnliftIO.RetryUntilIO( K ma, Schedule schedule, Func predicate) => ma is IOEmpty ? IOEmpty.Default : ma.As().RetryUntil(schedule, predicate); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Folding // static K MonadUnliftIO.FoldIO( K ma, Schedule schedule, S initialState, Func folder) => ma is IOEmpty ? IOEmpty.Default : ma.As().Fold(schedule, initialState, folder); static K MonadUnliftIO.FoldIO( K ma, S initialState, Func folder) => ma is IOEmpty ? IOEmpty.Default : ma.As().Fold(initialState, folder); static K MonadUnliftIO.FoldWhileIO( K ma, Schedule schedule, S initialState, Func folder, Func stateIs) => ma is IOEmpty ? IOEmpty.Default : ma.As().FoldWhile(schedule, initialState, folder, stateIs); static K MonadUnliftIO.FoldWhileIO( K ma, S initialState, Func folder, Func stateIs) => ma is IOEmpty ? IOEmpty.Default : ma.As().FoldWhile(initialState, folder, stateIs); static K MonadUnliftIO.FoldWhileIO( K ma, Schedule schedule, S initialState, Func folder, Func valueIs) => ma is IOEmpty ? IOEmpty.Default : ma.As().FoldWhile(schedule, initialState, folder, valueIs); static K MonadUnliftIO.FoldWhileIO( K ma, S initialState, Func folder, Func valueIs) => ma is IOEmpty ? IOEmpty.Default : ma.As().FoldWhile(initialState, folder, valueIs); static K MonadUnliftIO.FoldWhileIO( K ma, Schedule schedule, S initialState, Func folder, Func<(S State, A Value), bool> predicate) => ma is IOEmpty ? IOEmpty.Default : ma.As().FoldWhile(schedule, initialState, folder, predicate); static K MonadUnliftIO.FoldWhileIO( K ma, S initialState, Func folder, Func<(S State, A Value), bool> predicate) => ma is IOEmpty ? IOEmpty.Default : ma.As().FoldWhile(initialState, folder, predicate); static K MonadUnliftIO.FoldUntilIO( K ma, Schedule schedule, S initialState, Func folder, Func stateIs) => ma is IOEmpty ? IOEmpty.Default : ma.As().FoldUntil(schedule, initialState, folder, stateIs); static K MonadUnliftIO.FoldUntilIO( K ma, S initialState, Func folder, Func stateIs) => ma is IOEmpty ? IOEmpty.Default : ma.As().FoldUntil(initialState, folder, stateIs); static K MonadUnliftIO.FoldUntilIO( K ma, Schedule schedule, S initialState, Func folder, Func valueIs) => ma is IOEmpty ? IOEmpty.Default : ma.As().FoldUntil(schedule, initialState, folder, valueIs); static K MonadUnliftIO.FoldUntilIO( K ma, S initialState, Func folder, Func valueIs) => ma is IOEmpty ? IOEmpty.Default : ma.As().FoldUntil(initialState, folder, valueIs); static K MonadUnliftIO.FoldUntilIO( K ma, S initialState, Func folder, Func<(S State, A Value), bool> predicate) => ma is IOEmpty ? IOEmpty.Default : ma.As().FoldUntil(initialState, folder, predicate); static K MonadUnliftIO.FoldUntilIO( K ma, Schedule schedule, S initialState, Func folder, Func<(S State, A Value), bool> predicate) => ma is IOEmpty ? IOEmpty.Default : ma.As().FoldUntil(schedule, initialState, folder, predicate); } ================================================ FILE: LanguageExt.Core/Effects/IO/IO.cs ================================================ using System; using LanguageExt.DSL; using System.Threading; using LanguageExt.Common; using LanguageExt.Traits; using System.Threading.Tasks; using System.Diagnostics.Contracts; using System.Runtime.CompilerServices; using static LanguageExt.Prelude; namespace LanguageExt; /// /// A value of type `IO` is a computation which, when performed, does some I/O before returning /// a value of type `A`. /// /// There is really only one way you should _"perform"_ an I/O action: bind it to `Main` in your /// program: When your program is run, the I/O will be performed. It shouldn't be possible to /// perform I/O from an arbitrary function, unless that function is itself in the `IO` monad and /// called at some point, directly or indirectly, from `Main`. /// /// As this is C#, the above restrictions are for you to enforce. It would be reasonable /// to relax that approach and have I/O invoked from, say, web-request handlers - or any other 'edges' /// of your application. /// /// `IO` is a monad, so `IO` actions can be combined using either the LINQ-notation or the `bind` /// operations from the `Monad` class. /// /// The lifted thunk that is the IO operation /// Bound value public abstract record IO : K, Monoid> { //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // General // public static IO Empty => IOEmpty.Default; /// /// Wraps this IO computation in a local-environment that ignores any cancellation-token cancellation requests. /// /// An uninterruptible IO computation public IO Uninterruptible() => new IOUninterruptible(this, IO.pure); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Functor // public abstract IO Map(Func f); public virtual IO ApplyBack(K> f) => new IOApply(f, this, IO.pure); public IO Map(B value) => Map(_ => value); public IO MapFail(Func f) => this.Catch(f).As(); public IO BiMap(Func Succ, Func Fail) => Map(Succ).Catch(Fail).As(); public IO Match(Func Succ, Func Fail) => Map(Succ).Catch(Fail).As(); public IO IfFail(Func Fail) => this.Catch(Fail).As(); public IO IfFail(A Fail) => this.Catch(Fail).As(); public IO IfFail(Func> Fail) => this.CatchIO(Fail).As(); public IO IfFail(IO Fail) => this.Catch(Fail).As(); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Folding // public virtual IO Fold( Schedule schedule, S initialState, Func folder) => new IOFold(this, schedule, initialState, folder, IO.pure); public virtual IO Fold( S initialState, Func folder) => new IOFold(this, Schedule.Forever, initialState, folder, IO.pure); public virtual IO FoldWhile( Schedule schedule, S initialState, Func folder, Func stateIs) => new IOFoldWhile(this, schedule, initialState, folder, s => stateIs(s.State), IO.pure); public virtual IO FoldWhile( S initialState, Func folder, Func stateIs) => new IOFoldWhile(this, Schedule.Forever, initialState, folder, s => stateIs(s.State), IO.pure); public virtual IO FoldWhile( Schedule schedule, S initialState, Func folder, Func valueIs) => new IOFoldWhile(this, schedule, initialState, folder, s => valueIs(s.Value), IO.pure); public virtual IO FoldWhile( S initialState, Func folder, Func valueIs) => new IOFoldWhile(this, Schedule.Forever, initialState, folder, s => valueIs(s.Value), IO.pure); public virtual IO FoldWhile( Schedule schedule, S initialState, Func folder, Func<(S State, A Value), bool> predicate) => new IOFoldWhile(this, schedule, initialState, folder, predicate, IO.pure); public virtual IO FoldWhile( S initialState, Func folder, Func<(S State, A Value), bool> predicate) => new IOFoldWhile(this, Schedule.Forever, initialState, folder, predicate, IO.pure); public virtual IO FoldUntil( Schedule schedule, S initialState, Func folder, Func stateIs) => new IOFoldUntil(this, schedule, initialState, folder, p => stateIs(p.State), IO.pure); public virtual IO FoldUntil( S initialState, Func folder, Func stateIs) => new IOFoldUntil(this, Schedule.Forever, initialState, folder, p => stateIs(p.State), IO.pure); public virtual IO FoldUntil( Schedule schedule, S initialState, Func folder, Func valueIs) => new IOFoldUntil(this, schedule, initialState, folder, p => valueIs(p.Value), IO.pure); public virtual IO FoldUntil( S initialState, Func folder, Func valueIs) => new IOFoldUntil(this, Schedule.Forever, initialState, folder, p => valueIs(p.Value), IO.pure); public virtual IO FoldUntil( S initialState, Func folder, Func<(S State, A Value), bool> predicate) => new IOFoldUntil(this, Schedule.Forever, initialState, folder, predicate, IO.pure); public virtual IO FoldUntil( Schedule schedule, S initialState, Func folder, Func<(S State, A Value), bool> predicate) => new IOFoldUntil(this, schedule, initialState, folder, predicate, IO.pure); ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Cross thread-context posting // /// /// Make this IO computation run on the `SynchronizationContext` that was captured at the start /// of the IO chain (i.e. the one embedded within the `EnvIO` environment that is passed through /// all IO computations) /// public virtual IO Post() => IO.liftAsync(async env => { if (env.Token.IsCancellationRequested) throw new OperationCanceledException(); if (env.SyncContext is null) return await RunAsync(env); A? value = default; Exception? error = default; using var wait = new AutoResetEvent(false); env.SyncContext.Post(_ => { try { value = Run(env); } catch (Exception e) { error = e; } finally { wait.Set(); } }, null); await wait.WaitOneAsync(env.Token); if (env.Token.IsCancellationRequested) throw new OperationCanceledException(); if (error is not null) error.Rethrow(); return value!; }); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Monad // public abstract IO Bind(Func> f); public abstract IO BindAsync(Func>> f); public IO Bind(Func> f) => Bind(x => f(x).Kind()); public IO Bind(Func> f) => Map(x => f(x).Value); public IO BindAsync(Func>> f) => BindAsync(async x => (await f(x)).Kind()); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // LINQ // public IO Select(Func f) => Map(f); public IO SelectMany(Func> bind, Func project) => Bind(x => IOTail.resolve(x, bind(x), project)); public IO SelectMany(Func> bind, Func project) => Bind(x => IOTail.resolve(x, bind(x).As(), project)); public IO SelectMany(Func> bind, Func project) => Bind(x => bind(x).Map(y => project(x, y))); public Eff SelectMany(Func> bind, Func project) => Eff.LiftIO(this).SelectMany(bind, project); public Eff SelectMany(Func> bind, Func project) => Eff.LiftIO(this).SelectMany(bind, project); public IO SelectMany(Func> bind, Func project) => SelectMany(a => bind(a).ToIO(), project); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Brackets // /// /// The IO monad tracks resources automatically; this method creates a local resource environment /// to run this computation in. Once the computation is complete, any resources acquired are automatically /// released. Imagine this as the ultimate `using` statement. /// [Pure] [MethodImpl(Opt.Default)] public IO Bracket() => WithEnv(env => env.LocalResources); /// /// The IO monad tracks resources automatically; this method creates a local resource environment /// to run the computation in. If the computation errors, then the resources are automatically /// released. /// /// /// This differs from `Bracket` in that `Bracket` will also free resources once the computation /// is successfully complete. `BracketFail` only frees resources on failure. /// [Pure] [MethodImpl(Opt.Default)] public IO BracketFail() => WithEnvFail(env => env.LocalResources); /// /// When acquiring, using, and releasing various resources, it can be quite convenient to write a function to manage /// the acquisition and releasing, taking a function of the acquired value that specifies an action to be performed /// in between. /// /// Function to use the acquired resource /// Function to invoke to release the resource public IO Bracket(Func> Use, Func> Fin) => Bind(x => Use(x).Finally(Fin(x))); /// /// When acquiring, using, and releasing various resources, it can be quite convenient to write a function to manage /// the acquisition and releasing, taking a function of the acquired value that specifies an action to be performed /// in between. /// /// Function to use the acquired resource /// Function to run to handle any exceptions /// Function to invoke to release the resource public IO Bracket(Func> Use, Func> Catch, Func> Fin) => Bind(x => Use(x).Catch(Catch).Finally(Fin(x))); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Conversion // public static implicit operator IO(Pure ma) => IO.pure(ma.Value); public static implicit operator IO(Error error) => IO.lift(_ => error.Throw()); public static implicit operator IO(Fail ma) => IO.lift(() => ma.Value.Throw()); public static implicit operator IO(Fail ma) => IO.lift(() => ma.Value.Rethrow()); public static implicit operator IO(Lift ma) => IO.lift(ma.Function); public static implicit operator IO(Lift ma) => IO.lift(ma.Function); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Parallel // /// /// Applies a time limit to the IO computation. If exceeded, an exception is thrown. /// /// Timeout /// Result of the operation or throws if the time limit exceeded. public IO Timeout(TimeSpan timeout) => new IOTimeout(this, timeout, IO.pure); /// /// Map the `EnvIO` value threaded through the computation. Upon completion, any resources acquired during /// the processing of this computation (in the newly mapped local context) will automatically be released. /// protected virtual IO WithEnv(Func f) => new IOLocal(f, this, IO.pure); /// /// Map the `EnvIO` value threaded through the computation. Upon completion, any resources acquired during /// the processing of this computation (in the newly mapped local context) will automatically be released. /// /// /// This variant will only release resources on failure. Successful computations will allow the return of /// acquired resources so they can be used outside this computation. /// protected virtual IO WithEnvFail(Func f) => new IOLocalOnFailOnly(f, this, IO.pure); /// /// Create a local cancellation environment /// public IO Local() => WithEnv(env => EnvIO.New(env.Resources, env.Token, null, env.SyncContext)); /// /// Queues the specified work to run on the thread pool /// /// /// Any resources acquired within a forked IO computation will automatically be released upon the forked /// computation's completion (successful or otherwise). Resources acquired in the parent thread will be /// available to the forked thread, and can be released from there, but they are shared resources at that /// point and should be treated with care. /// /// Optional timeout /// `Fork` record that contains members for cancellation and optional awaiting public virtual IO> Fork(Option timeout = default) => IO.lift( env => { if (env.Token.IsCancellationRequested) throw new OperationCanceledException(); // Create a new local token-source with its own cancellation token var tsrc = timeout.Match(Some: to => new CancellationTokenSource(to), None: () => new CancellationTokenSource()); var token = tsrc.Token; // If the parent cancels, we should too var reg = env.Token.Register(() => tsrc.Cancel()); // Gather our resources for clean-up var cleanup = new CleanUp(tsrc, reg); var parentResources = env.Resources; var task = Task.Factory.StartNew( () => { var forkedResources = new Resources(parentResources); try { var t = RunAsync(EnvIO.New(forkedResources, token, tsrc, env.SyncContext)); return t.GetAwaiter().GetResult(); } finally { forkedResources.Dispose(); cleanup.Dispose(); } }, TaskCreationOptions.LongRunning); return new ForkIO( IO.lift(_ => { try { tsrc.Cancel(); } catch(OperationCanceledException) { // ignore if already cancelled } catch(ObjectDisposedException) { // ignore if already cancelled } return default; }), IO.liftAsync(e => AwaitAsync(task, e, token, tsrc))); }); /// /// Run the `IO` monad to get its result. Differs from `Run` in that it catches any exceptions and turns /// them into a `Fin〈A〉` result. /// public FinT Try() => new (Map(Fin.Succ).Catch(Fin.Fail)); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Repeating the effect // /// /// Keeps repeating the computation forever, or until an error occurs /// /// /// Any resources acquired within a repeated IO computation will automatically be released. This also means you can't /// acquire resources and return them from within a repeated computation. /// /// The result of the last invocation public IO Repeat() => RepeatUntil(_ => false); /// /// Keeps repeating the computation, until the scheduler expires, or an error occurs /// /// /// Any resources acquired within a repeated IO computation will automatically be released. This also means you can't /// acquire resources and return them from within a repeated computation. /// /// Scheduler strategy for repeating /// The result of the last invocation public IO Repeat(Schedule schedule) => RepeatUntil(schedule, _ => false); /// /// Keeps repeating the computation until the predicate returns false, or an error occurs /// /// /// Any resources acquired within a repeated IO computation will automatically be released. This also means you can't /// acquire resources and return them from within a repeated computation. /// /// Keep repeating while this predicate returns `true` for each computed value /// The result of the last invocation public IO RepeatWhile(Func predicate) => RepeatUntil(not(predicate)); /// /// Keeps repeating the computation, until the scheduler expires, or the predicate returns false, or an error occurs /// /// /// Any resources acquired within a repeated IO computation will automatically be released. This also means you can't /// acquire resources and return them from within a repeated computation. /// /// Scheduler strategy for repeating /// Keep repeating while this predicate returns `true` for each computed value /// The result of the last invocation public IO RepeatWhile( Schedule schedule, Func predicate) => RepeatUntil(schedule, not(predicate)); /// /// Keeps repeating the computation until the predicate returns true, or an error occurs /// /// /// Any resources acquired within a repeated IO computation will automatically be released. This also means you can't /// acquire resources and return them from within a repeated computation. /// /// Keep repeating until this predicate returns `true` for each computed value /// The result of the last invocation public IO RepeatUntil(Func predicate) => Bracket().Bind(v => predicate(v) ? IO.pure(v) : RepeatUntil(predicate)); /// /// Keeps repeating the computation, until the scheduler expires, or the predicate returns true, or an error occurs /// /// /// Any resources acquired within a repeated IO computation will automatically be released. This also means you can't /// acquire resources and return them from within a repeated computation. /// /// Scheduler strategy for repeating /// Keep repeating until this predicate returns `true` for each computed value /// The result of the last invocation public virtual IO RepeatUntil( Schedule schedule, Func predicate) { return go(schedule.PrependZero.Run().GetIterator(), default); IO go(Iterator iter, A? value) => iter switch { Iterator.Nil => IO.pure(value!), Iterator.Cons(var head, var tail) => IO.yieldFor(head) .Bind(_ => Bracket() .Bind(v => predicate(v) ? IO.pure(v) : go(tail, v))), _ => throw new InvalidOperationException("Invalid iterator") }; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Retrying the effect when it fails // /// /// Retry if the IO computation fails /// /// /// This variant will retry forever /// /// /// Any resources acquired within a retrying IO computation will automatically be released *if* the operation fails. /// So, successive retries will not grow the acquired resources on each retry iteration. Any successful operation that /// acquires resources will have them tracked in the usual way. /// public IO Retry() => RetryUntil(_ => false); /// /// Retry if the IO computation fails /// /// /// This variant will retry until the schedule expires /// /// /// Any resources acquired within a retrying IO computation will automatically be released *if* the operation fails. /// So, successive retries will not grow the acquired resources on each retry iteration. Any successful operation that /// acquires resources will have them tracked in the usual way. /// public IO Retry(Schedule schedule) => RetryUntil(schedule, _ => false); /// /// Retry if the IO computation fails /// /// /// This variant will keep retrying whilst the predicate returns `true` for the error generated at each iteration; /// at which point the last raised error will be thrown. /// /// /// Any resources acquired within a retrying IO computation will automatically be released *if* the operation fails. /// So, successive retries will not grow the acquired resources on each retry iteration. Any successful operation that /// acquires resources will have them tracked in the usual way. /// public IO RetryWhile(Func predicate) => RetryUntil(not(predicate)); /// /// Retry if the IO computation fails /// /// /// This variant will keep retrying whilst the predicate returns `true` for the error generated at each iteration; /// or, until the schedule expires; at which point the last raised error will be thrown. /// /// /// Any resources acquired within a retrying IO computation will automatically be released *if* the operation fails. /// So, successive retries will not grow the acquired resources on each retry iteration. Any successful operation that /// acquires resources will have them tracked in the usual way. /// public IO RetryWhile( Schedule schedule, Func predicate) => RetryUntil(schedule, not(predicate)); /// /// Retry if the IO computation fails /// /// /// This variant will keep retrying until the predicate returns `true` for the error generated at each iteration; /// at which point the last raised error will be thrown. /// /// /// Any resources acquired within a retrying IO computation will automatically be released *if* the operation fails. /// So, successive retries will not grow the acquired resources on each retry iteration. Any successful operation that /// acquires resources will have them tracked in the usual way. /// public IO RetryUntil(Func predicate) => BracketFail() .Catch(e => predicate(e) ? IO.fail(e) : RetryUntil(predicate)).As(); /// /// Retry if the IO computation fails /// /// /// This variant will keep retrying until the predicate returns `true` for the error generated at each iteration; /// or, until the schedule expires; at which point the last raised error will be thrown. /// /// /// Any resources acquired within a retrying IO computation will automatically be released *if* the operation fails. /// So, successive retries will not grow the acquired resources on each retry iteration. Any successful operation that /// acquires resources will have them tracked in the usual way. /// public virtual IO RetryUntil(Schedule schedule, Func predicate) { return go(schedule.PrependZero.Run().GetIterator(), Errors.None); IO go(Iterator iter, Error error) => iter switch { Iterator.Nil => IO.fail(error), Iterator.Cons(var head, var tail) => IO.yieldFor(head) .Bind(_ => BracketFail() .Catch(e => predicate(e) ? IO.fail(e) : go(tail, e))), _ => throw new InvalidOperationException("Invalid iterator") }; } /// /// Catches any error thrown by invoking this IO computation, passes it through a predicate, /// and if that returns true, returns the result of invoking the Fail function, otherwise /// this is returned. /// /// Predicate /// Fail functions public virtual IO Catch(Func Predicate, Func> Fail) => new IOCatch(this, Predicate, Fail, null, IO.pure); /// /// Run a `finally` operation after the `this` operation regardless of whether `this` succeeds or not. /// /// Finally operation /// Result of primary operation public virtual IO Finally(K @finally) => new IOFinal(this, @finally, IO.pure); /// /// Monoid combine /// /// Alternative /// This if computation runs without error. `rhs` otherwise public IO Combine(IO rhs) => Catch(_ => true, _ => rhs); /// /// Run the `IO` monad to get its result /// /// /// Any lifted asynchronous operations will yield to the thread-scheduler, allowing other queued /// operations to run concurrently. So, even though this call isn't awaitable it still plays /// nicely and doesn't block the thread. /// /// /// NOTE: An exception will always be thrown if the IO operation fails. Lift this monad into /// other error handling monads to leverage more declarative error handling. /// /// IO environment /// Result of the IO operation /// Throws if the operation is cancelled public A Run() { // RunAsync can run completely synchronously and without the creation of an async/await state-machine, so calling // it for operations that are completely synchronous has no additional overhead for us. Therefore, calling it // directly here and then unpacking the `ValueTask` makes sense to reduce code duplication. var task = RunAsync(); if (task.IsCompleted) return task.Result; // If RunAsync really had to do some asynchronous work, then make sure we use the awaiter and get its result return task.GetAwaiter().GetResult(); } /// /// Run the `IO` monad to get its result /// /// /// Any lifted asynchronous operations will yield to the thread-scheduler, allowing other queued /// operations to run concurrently. So, even though this call isn't awaitable it still plays /// nicely and doesn't block the thread. /// /// /// NOTE: An exception will always be thrown if the IO operation fails. Lift this monad into /// other error handling monads to leverage more declarative error handling. /// /// IO environment /// Result of the IO operation /// Throws if the operation is cancelled public A Run(EnvIO envIO) { // RunAsync can run completely synchronously and without the creation of an async/await state-machine, so calling // it for operations that are completely synchronous has no additional overhead for us. Therefore, calling it // directly here and then unpacking the `ValueTask` makes sense to reduce code duplication. var task = RunAsync(envIO); if (task.IsCompleted) return task.Result; // If RunAsync really had to do some asynchronous work, then make sure we use the awaiter and get its result return task.GetAwaiter().GetResult(); } /// /// Run the `IO` monad to get its result /// /// /// This forks the operation to run on a new task that is then awaited. /// /// Any lifted asynchronous operations will yield to the thread-scheduler, allowing other queued /// operations to run concurrently. So, even though this call isn't awaitable it still plays /// nicely and doesn't block the thread. /// /// /// NOTE: An exception will always be thrown if the IO operation fails. Lift this monad into /// other error handling monads to leverage more declarative error handling. /// /// Result of the IO operation /// Throws if the operation is cancelled public ValueTask RunAsync() { var envIO = EnvIO.New(); try { var va = RunAsync(envIO); if (va.IsCompleted) { envIO.Dispose(); return va; } else { return SafeRunAsync(va, envIO); } } catch (Exception) { envIO.Dispose(); throw; } } /// /// Run the `IO` monad to get its result /// /// /// Any lifted asynchronous operations will yield to the thread-scheduler, allowing other queued /// operations to run concurrently. So, even though this call isn't awaitable it still plays /// nicely and doesn't block the thread. /// /// /// NOTE: An exception will always be thrown if the IO operation fails. Lift this monad into /// other error handling monads to leverage more declarative error handling. /// /// Cancellation toke /// Result of the IO operation /// Throws if the operation is cancelled public async ValueTask RunAsync(CancellationToken token) { using var env = EnvIO.New(token: token); return await RunAsync(env); } static async ValueTask SafeRunAsync(ValueTask va, EnvIO envIO) { try { return await va; } finally { envIO.Dispose(); } } /// /// Run the `IO` monad to get its result /// /// /// This forks the operation to run on a new task that is then awaited. /// /// Any lifted asynchronous operations will yield to the thread-scheduler, allowing other queued /// operations to run concurrently. So, even though this call isn't awaitable it still plays /// nicely and doesn't block the thread. /// /// /// NOTE: An exception will always be thrown if the IO operation fails. Lift this monad into /// other error handling monads to leverage more declarative error handling. /// /// Result of the IO operation /// Throws if the operation is cancelled public ValueTask RunAsync(EnvIO envIO) { if (envIO.Token.IsCancellationRequested) return ValueTask.FromCanceled(envIO.Token); var ma = this; var stack = Seq(new StackItem(ex => ex.Rethrow>())); // we want at least one item in the stack // so that we benefit from the auto-clean-up // of resources. while (!envIO.Token.IsCancellationRequested) { switch (ma) { case InvokeAsync: return ma.RunAsyncInternal(stack, envIO); case InvokeAsyncIO: return ma.RunAsyncInternal(stack, envIO); case InvokeSync op: try { return new ValueTask(op.Invoke(envIO)); } catch (Exception e) { if (stack.IsEmpty) throw; ma = RunCatchHandler(e); break; } case InvokeSyncIO op: try { ma = op.Invoke(envIO); } catch (Exception e) { if (stack.IsEmpty) throw; ma = RunCatchHandler(e); } break; case IOCatch c: var @catch = c.MakeHandler(); stack = new StackItem(@catch).Cons(stack); ma = c.MakeOperation(); break; case IOCatchPop pop: stack = stack.Tail; ma = pop.Next; break; case IOTail tail: ma = tail.Tail; break; default: return ValueTask.FromException(Errors.IODSLExtension); } } return ValueTask.FromCanceled(envIO.Token); IO RunCatchHandler(Exception e) { var top = stack[0]; var @catch = top.Catch; stack = stack.Tail; // This must be the last thing because the `@catch` may rethrow, // so we have to have cleaned everything up. return @catch(e); } } /// /// This version of `RunAsync` uses the async/await machinery. It is kept separate from the `RunAsync` version /// so that we can avoid creating the async/await state-machine if all operations are synchronous. /// async ValueTask RunAsyncInternal(Seq stack, EnvIO envIO) { var ma = this; while (!envIO.Token.IsCancellationRequested) { switch (ma) { case InvokeAsync op: try { return await op.Invoke(envIO); } catch (Exception e) { if (stack.IsEmpty) throw; ma = RunCatchHandler(e); break; } case InvokeSync op: try { return op.Invoke(envIO); } catch (Exception e) { if (stack.IsEmpty) throw; ma = RunCatchHandler(e); break; } case InvokeSyncIO op: try { ma = op.Invoke(envIO); } catch (Exception e) { if (stack.IsEmpty) throw; ma = RunCatchHandler(e); } break; case InvokeAsyncIO op: try { ma = await op.Invoke(envIO); } catch (Exception e) { if (stack.IsEmpty) throw; ma = RunCatchHandler(e); } break; case IOCatch c: var @catch = c.MakeHandler(); stack = new StackItem(@catch).Cons(stack); ma = c.MakeOperation(); break; case IOCatchPop pop: stack = stack.Tail; ma = pop.Next; break; case IOTail tail: ma = tail.Tail; break; default: return Errors.IODSLExtension.Throw(); } } throw new OperationCanceledException(); IO RunCatchHandler(Exception e) { var top = stack[0]; var @catch = top.Catch; stack = stack.Tail; // This must be the last thing because the `@catch` may rethrow, // so we have to have cleaned everything up. return @catch(e); } } public override string ToString() => "IO"; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Internal // async Task AwaitAsync(Task t, EnvIO envIO, CancellationToken token, CancellationTokenSource source) { if (envIO.Token.IsCancellationRequested) throw new OperationCanceledException(); if (token.IsCancellationRequested) throw new OperationCanceledException(); await using var reg = envIO.Token.Register(source.Cancel); return await t; } record CleanUp(CancellationTokenSource Src, CancellationTokenRegistration Reg) : IDisposable { volatile int disposed; public void Dispose() { if (Interlocked.Exchange(ref disposed, 1) == 0) { try{Src.Dispose();} catch { /* not important */ } try{Reg.Dispose();} catch { /* not important */ } } } } readonly record struct StackItem(Func> Catch); } ================================================ FILE: LanguageExt.Core/Effects/IO/Operators/IO.Operators.Applicative.cs ================================================ using System; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; public static partial class IOExtensions { extension(K self) { /// /// Applicative sequence operator /// public static IO operator >>> (K ma, K mb) => +ma.Action(mb); /// /// Applicative apply operator /// public static IO operator * (K> mf, K ma) => mf.Apply(ma); /// /// Applicative apply operator /// public static IO operator * (K ma, K> mf) => mf.Apply(ma); } extension(K self) { /// /// Applicative apply operator /// public static IO> operator * ( K> mf, K ma) => curry * mf * ma; /// /// Applicative apply operator /// public static IO> operator * ( K ma, K> mf) => curry * mf * ma; } extension(K self) { /// /// Applicative apply operator /// public static IO>> operator * ( K> mf, K ma) => curry * mf * ma; /// /// Applicative apply operator /// public static IO>> operator * ( K ma, K> mf) => curry * mf * ma; } extension(K self) { /// /// Applicative apply operator /// public static IO>>> operator * ( K> mf, K ma) => curry * mf * ma; /// /// Applicative apply operator /// public static IO>>> operator * ( K ma, K> mf) => curry * mf * ma; } extension(K self) { /// /// Applicative apply operator /// public static IO>>>> operator * ( K> mf, K ma) => curry * mf * ma; /// /// Applicative apply operator /// public static IO>>>> operator * ( K ma, K> mf) => curry * mf * ma; } extension(K self) { /// /// Applicative apply operator /// public static IO>>>>> operator * ( K> mf, K ma) => curry * mf * ma; /// /// Applicative apply operator /// public static IO>>>>> operator * ( K ma, K> mf) => curry * mf * ma; } extension(K self) { /// /// Applicative apply operator /// public static IO>>>>>> operator * ( K> mf, K ma) => curry * mf * ma; /// /// Applicative apply operator /// public static IO>>>>>> operator * ( K ma, K> mf) => curry * mf * ma; } extension(K self) { /// /// Applicative apply operator /// public static IO>>>>>>> operator * ( K> mf, K ma) => curry * mf * ma; /// /// Applicative apply operator /// public static IO>>>>>>> operator * ( K ma, K> mf) => curry * mf * ma; } extension(K self) { /// /// Applicative apply operator /// public static IO>>>>>>>> operator * ( K> mf, K ma) => curry * mf * ma; /// /// Applicative apply operator /// public static IO>>>>>>>> operator * ( K ma, K> mf) => curry * mf * ma; } extension(K self) { /// /// Applicative apply operator /// public static IO>>>>>>>>> operator * ( K> mf, K ma) => curry * mf * ma; /// /// Applicative apply operator /// public static IO>>>>>>>>> operator *( K ma, K> mf) => curry * mf * ma; } } ================================================ FILE: LanguageExt.Core/Effects/IO/Operators/IO.Operators.Choice.cs ================================================ using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt; public static partial class IOExtensions { extension(K self) { /// /// Choice operator. Usually means if the first argument succeeds, return it, otherwise return the second /// argument. /// /// Left hand side operand /// Right hand side operand /// public static IO operator |(K lhs, K rhs) => +lhs.Choose(rhs); /// /// Choice operator. Usually means if the first argument succeeds, return it, otherwise return the second /// argument. /// /// Left hand side operand /// Right hand side operand /// public static IO operator |(K lhs, Pure rhs) => +lhs.Choose(rhs.ToIO()); } } ================================================ FILE: LanguageExt.Core/Effects/IO/Operators/IO.Operators.Fallible.cs ================================================ using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt; public static partial class IOExtensions { extension(K self) { public static IO operator |(K lhs, CatchM rhs) => +lhs.Catch(rhs); public static IO operator |(K lhs, Fail rhs) => +lhs.Catch(rhs); public static IO operator |(K lhs, Error rhs) => +lhs.Catch(rhs); } } ================================================ FILE: LanguageExt.Core/Effects/IO/Operators/IO.Operators.Final.cs ================================================ namespace LanguageExt.Traits; public static class IOExtensions { extension(K _) { /// /// Run a `finally` operation after the main operation regardless of whether it succeeds or not. /// /// Primary operation /// Finally operation /// Result of primary operation public static IO operator |(K lhs, Finally rhs) => +lhs.Finally(rhs.Operation); } } ================================================ FILE: LanguageExt.Core/Effects/IO/Operators/IO.Operators.Functor.cs ================================================ using System; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; public static partial class IOExtensions { extension(K _) { /// /// Functor map operator /// public static IO operator *(Func f, K ma) => +ma.Map(f); /// /// Functor map operator /// public static IO operator *(K ma, Func f) => +ma.Map(f); } extension(K _) { /// /// Functor map operator /// public static IO> operator * ( Func f, K ma) => curry(f) * ma; /// /// Functor map operator /// public static IO> operator * ( K ma, Func f) => curry(f) * ma; } extension(K _) { /// /// Functor map operator /// public static IO>> operator * ( Func f, K ma) => curry(f) * ma; /// /// Functor map operator /// public static IO>> operator * ( K ma, Func f) => curry(f) * ma; } extension(K _) { /// /// Functor map operator /// public static IO>>> operator * ( Func f, K ma) => curry(f) * ma; /// /// Functor map operator /// public static IO>>> operator * ( K ma, Func f) => curry(f) * ma; } extension(K _) { /// /// Functor map operator /// public static IO>>>> operator * ( Func f, K ma) => curry(f) * ma; /// /// Functor map operator /// public static IO>>>> operator * ( K ma, Func f) => curry(f) * ma; } extension(K _) { /// /// Functor map operator /// public static IO>>>>> operator * ( Func f, K ma) => curry(f) * ma; /// /// Functor map operator /// public static IO>>>>> operator * ( K ma, Func f) => curry(f) * ma; } extension(K _) { /// /// Functor map operator /// public static IO>>>>>> operator * ( Func f, K ma) => curry(f) * ma; /// /// Functor map operator /// public static IO>>>>>> operator * ( K ma, Func f) => curry(f) * ma; } extension(K _) { /// /// Functor map operator /// public static IO>>>>>>> operator * ( Func f, K ma) => curry(f) * ma; /// /// Functor map operator /// public static IO>>>>>>> operator * ( K ma, Func f) => curry(f) * ma; } extension(K _) { /// /// Functor map operator /// public static IO>>>>>>>> operator * ( Func f, K ma) => curry(f) * ma; /// /// Functor map operator /// public static IO>>>>>>>> operator * ( K ma, Func f) => curry(f) * ma; } extension(K _) { /// /// Functor map operator /// public static IO>>>>>>>>> operator * ( Func f, K ma) => curry(f) * ma; /// /// Functor map operator /// public static IO>>>>>>>>> operator * ( K ma, Func f) => curry(f) * ma; } } ================================================ FILE: LanguageExt.Core/Effects/IO/Operators/IO.Operators.Monad.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public static partial class IOExtensions { extension(K self) { /// /// Monad bind operator /// /// Monad to bind /// Binding function /// Mapped monad public static IO operator >> (K ma, Func> f) => +ma.Bind(f); /// /// Sequentially compose two actions, discarding any value produced by the first, like sequencing operators (such /// as the semicolon) in C#. /// /// First action to run /// Second action to run /// Result of the second action public static IO operator >> (K lhs, K rhs) => lhs >> (_ => rhs); } extension(K self) { /// /// Sequentially compose two actions. The second action is a unit-returning action, so the result of the /// first action is propagated. /// /// First action to run /// Second action to run /// Result of the first action public static IO operator >> (K lhs, K rhs) => lhs >> (x => (_ => x) * rhs); } } ================================================ FILE: LanguageExt.Core/Effects/IO/Operators/IO.Operators.cs ================================================ using LanguageExt.Traits; namespace LanguageExt; public static partial class IOExtensions { extension(K _) { /// /// Downcast operator /// public static IO operator +(K ma) => (IO)ma; public static IO operator >> (K ma, Lower lower) => (IO)ma; } } ================================================ FILE: LanguageExt.Core/Effects/IO/Prelude/IO.Prelude.Concurreny.cs ================================================ using System; using System.Runtime.CompilerServices; using LanguageExt.Common; namespace LanguageExt; public static partial class Prelude { /// /// Generates a new reference that can be used within a `sync` transaction /// /// `Refs` ensure safe shared use of mutable storage locations via a software transactional /// memory (STM) system. `Refs` are bound to a single storage location for their lifetime, /// and only allow mutation of that location to occur within a transaction. /// /// /// /// Transactions (within a `sync(() => ...)`) should be easy to understand if you’ve ever used database /// transactions - they ensure that all actions on Refs are atomic, consistent, and isolated. /// /// * **Atomic** - means that every change to Refs made within a transaction occurs or none do. /// * **Consistent** - means that each new value can be checked with a validator function before allowing /// the transaction to commit. /// * **Isolated** - means that no transaction sees the effects of any other transaction while it is /// running. /// /// Another feature common to STMs is that, should a transaction have a conflict while running, /// it is automatically retried. The language-ext STM uses multi-version concurrency control for /// snapshot and serialisable isolation. /// /// In practice, this means: /// /// All reads of Refs will see a consistent snapshot of the _Ref world_ as of the starting point /// of the transaction (its 'read point'). The transaction will see any changes it has made. /// This is called the in-transaction-value. /// /// All changes made to Refs during a transaction will appear to occur at a single point in the /// _Ref world_ timeline (its 'write point'). /// /// No changes will have been made by any other transactions to any Refs that have been modified /// by this transaction. /// /// * Readers will never block writers, or other readers. /// /// * Writers will never block readers. /// /// I/O and other activities with side effects should be avoided in transactions, since transactions /// will be retried. /// /// If a constraint on the validity of a value of a Ref that is being changed depends upon the /// simultaneous value of a Ref that is not being changed, that second Ref can be protected from /// modification by running the `sync` transaction with `Isolation.Serialisable`. /// /// The language-ext STM is designed to work with the persistent collections (`Map`, `HashMap`, /// `Seq`, `Lst`, `Set, `HashSet` etc.), and it is strongly recommended that you use the language-ext /// collections as the values of your Refs. Since all work done in an STM transaction is speculative, /// it is imperative that there be a low cost to making copies and modifications. Persistent collections /// have free copies (just use the original, it can’t be changed), and 'modifications' share structure /// efficiently. In any case: /// /// The values placed in Refs must be, or be considered, **immutable**. Otherwise, this library can’t help you. /// /// /// See the [concurrency section](https://github.com/louthy/language-ext/wiki/Concurrency) of the wiki for more info. /// /// Initial value of the ref /// Validator that is called on the ref value just /// before any transaction is committed (within a `sync`) [MethodImpl(MethodImplOptions.AggressiveInlining)] public static IO> refIO(A value, Func? validator = null) => lift(() => Ref(value, validator)); /// /// Snapshot isolation requires that nothing outside the transaction has written to any of the values that are /// *written-to within the transaction*. If anything does write to the values used within the transaction, then /// the transaction is rolled back and retried (using the latest 'world' state). /// /// /// Serialisable isolation requires that nothing outside the transaction has written to any of the values that /// are *read-from or written-to within the transaction*. If anything does read from or written to the values used /// within the transaction, then it is rolled back and retried (using the latest 'world' state). /// /// It is the strictest form of isolation, and the most likely to conflict; but protects against cross read/write /// inconsistencies. For example, if you have: /// /// var x = Ref(1); /// var y = Ref(2); /// /// snapshot(() => x.Value = y.Value + 1); /// /// Then something writing to `y` mid-way through the transaction would *not* cause the transaction to fail. /// Because `y` was only read-from, not written to. However, this: /// /// var x = Ref(1); /// var y = Ref(2); /// /// serial(() => x.Value = y.Value + 1); /// /// ... would fail if something wrote to `y`. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static IO atomicIO(Func op, Isolation isolation = Isolation.Snapshot) => lift(() => atomic(op, isolation)); /// /// Run the op within a new transaction /// If a transaction is already running, then this becomes part of the parent transaction /// /// /// Snapshot isolation requires that nothing outside the transaction has written to any of the values that are /// *written-to within the transaction*. If anything does write to the values used within the transaction, then /// the transaction is rolled back and retried (using the latest 'world' state). /// /// /// Serialisable isolation requires that nothing outside the transaction has written to any of the values that /// are *read-from or written-to within the transaction*. If anything does read from or written to the values used /// within the transaction, then it is rolled back and retried (using the latest 'world' state). /// /// It is the strictest form of isolation, and the most likely to conflict; but protects against cross read/write /// inconsistencies. For example, if you have: /// /// var x = Ref(1); /// var y = Ref(2); /// /// snapshot(() => x.Value = y.Value + 1); /// /// Then something writing to `y` mid-way through the transaction would *not* cause the transaction to fail. /// Because `y` was only read-from, not written to. However, this: /// /// var x = Ref(1); /// var y = Ref(2); /// /// serial(() => x.Value = y.Value + 1); /// /// ... would fail if something wrote to `y`. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static IO atomicIO(Action op, Isolation isolation = Isolation.Snapshot) => lift(() => atomic(op, isolation)); /// /// Run the op within a new transaction /// If a transaction is already running, then this becomes part of the parent transaction /// /// /// Snapshot isolation requires that nothing outside the transaction has written to any of the values that are /// *written-to within the transaction*. If anything does write to the values used within the transaction, then /// the transaction is rolled back and retried (using the latest 'world' state). /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static IO snapshotIO(Func op) => lift(() => snapshot(op)); /// /// Run the op within a new transaction /// If a transaction is already running, then this becomes part of the parent transaction /// /// /// Snapshot isolation requires that nothing outside the transaction has written to any of the values that are /// *written-to within the transaction*. If anything does write to the values used within the transaction, then /// the transaction is rolled back and retried (using the latest 'world' state). /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static IO snapshotIO(Action op) => lift(() => snapshot(op)); /// /// Run the op within a new transaction /// If a transaction is already running, then this becomes part of the parent transaction /// /// /// Serialisable isolation requires that nothing outside the transaction has written to any of the values that /// are *read-from or written-to within the transaction*. If anything does read from or written to the values used /// within the transaction, then it is rolled back and retried (using the latest 'world' state). /// /// It is the strictest form of isolation, and the most likely to conflict; but protects against cross read/write /// inconsistencies. For example, if you have: /// /// var x = Ref(1); /// var y = Ref(2); /// /// snapshot(() => x.Value = y.Value + 1); /// /// Then something writing to `y` mid-way through the transaction would *not* cause the transaction to fail. /// Because `y` was only read-from, not written to. However, this: /// /// var x = Ref(1); /// var y = Ref(2); /// /// serial(() => x.Value = y.Value + 1); /// /// ... would fail if something wrote to `y`. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static IO serialIO(Func op) => lift(() => serial(op)); /// /// Run the op within a new transaction /// If a transaction is already running, then this becomes part of the parent transaction /// /// /// Serialisable isolation requires that nothing outside the transaction has written to any of the values that /// are *read-from or written-to within the transaction*. If anything does read from or written to the values used /// within the transaction, then it is rolled back and retried (using the latest 'world' state). /// /// It is the strictest form of isolation, and the most likely to conflict; but protects against cross read/write /// inconsistencies. For example, if you have: /// /// var x = Ref(1); /// var y = Ref(2); /// /// snapshot(() => x.Value = y.Value + 1); /// /// Then something writing to `y` mid-way through the transaction would *not* cause the transaction to fail. /// Because `y` was only read-from, not written to. However, this: /// /// var x = Ref(1); /// var y = Ref(2); /// /// serial(() => x.Value = y.Value + 1); /// /// ... would fail if something wrote to `y`. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static IO serialIO(Action op) => lift(() => serial(op)); /// /// Swap the old value for the new returned by `f` /// Must be run within a `sync` transaction /// /// `Ref` to process /// Function to update the `Ref` /// The value returned from `f` [MethodImpl(MethodImplOptions.AggressiveInlining)] public static IO swapIO(Ref r, Func f) => r.SwapIO(f); /// /// Must be called in a transaction. Sets the in-transaction-value of /// ref to: /// /// `f(in-transaction-value-of-ref)` /// /// and returns the in-transaction-value when complete. /// /// At the commit point of the transaction, `f` is run *AGAIN* with the /// most recently committed value: /// /// `f(most-recently-committed-value-of-ref)` /// /// Thus `f` should be commutative, or, failing that, you must accept /// last-one-in-wins behavior. /// /// Commute allows for more concurrency than just setting the Ref's value /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static IO commuteIO(Ref r, Func f) => lift(() => commute(r, f)); /// /// Atoms provide a way to manage shared, synchronous, independent state without /// locks. /// /// Initial value of the atom /// The constructed Atom /// /// The intended use of atom is to hold one an immutable data structure. You change /// the value by applying a function to the old value. This is done in an atomic /// manner by `Swap`. /// /// Internally, `Swap` reads the current value, applies the function to it, and /// attempts to `CompareExchange` it in. Since another thread may have changed the /// value in the intervening time, it may have to retry, and does so in a spin loop. /// /// The net effect is that the value will always be the result of the application /// of the supplied function to a current value, atomically. However, because the /// function might be called multiple times, it must be free of side effects. /// /// Atoms are an efficient way to represent some state that will never need to be /// coordinated with any other, and for which you wish to make synchronous changes. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static IO> atomIO(A value) => lift(() => Atom(value)); /// /// Atoms provide a way to manage shared, synchronous, independent state without /// locks. /// /// Initial value of the atom /// Function to run on the value after each state change. /// /// If the function returns false for any proposed new state, then the `swap` /// function will return `false`, else it will return `true` on successful setting /// of the atom's state /// /// The constructed Atom or None if the validation faled for the initial /// `value` /// /// The intended use of atom is to hold one an immutable data structure. You change /// the value by applying a function to the old value. This is done in an atomic /// manner by `Swap`. /// /// Internally, `Swap` reads the current value, applies the function to it, and /// attempts to `CompareExchange` it in. Since another thread may have changed the /// value in the intervening time, it may have to retry, and does so in a spin loop. /// /// The net effect is that the value will always be the result of the application /// of the supplied function to a current value, atomically. However, because the /// function might be called multiple times, it must be free of side effects. /// /// Atoms are an efficient way to represent some state that will never need to be /// coordinated with any other, and for which you wish to make synchronous changes. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static IO> atomIO(A value, Func validator) => lift(() => Atom(value, validator) switch { { IsSome: true } atom => (Atom)atom, _ => throw Exceptions.ValidationFailed }); /// /// Atoms provide a way to manage shared, synchronous, independent state without /// locks. /// /// Metadata to be passed to the validation function /// Initial value of the atom /// The constructed Atom /// /// The intended use of atom is to hold one an immutable data structure. You change /// the value by applying a function to the old value. This is done in an atomic /// manner by `Swap`. /// /// Internally, `Swap` reads the current value, applies the function to it, and /// attempts to `CompareExchange` it in. Since another thread may have changed the /// value in the intervening time, it may have to retry, and does so in a spin loop. /// /// The net effect is that the value will always be the result of the application /// of the supplied function to a current value, atomically. However, because the /// function might be called multiple times, it must be free of side effects. /// /// Atoms are an efficient way to represent some state that will never need to be /// coordinated with any other, and for which you wish to make synchronous changes. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static IO> atomIO(M metadata, A value) => lift(() => Atom(metadata, value)); /// /// Atoms provide a way to manage shared, synchronous, independent state without /// locks. /// /// Metadata to be passed to the validation function /// Initial value of the atom /// Function to run on the value after each state change. /// /// If the function returns false for any proposed new state, then the `swap` /// function will return `false`, else it will return `true` on successful setting /// of the atom's state /// /// The constructed Atom or None if the validation faled for the initial /// `value` /// /// The intended use of atom is to hold one an immutable data structure. You change /// the value by applying a function to the old value. This is done in an atomic /// manner by `Swap`. /// /// Internally, `Swap` reads the current value, applies the function to it, and /// attempts to `CompareExchange` it in. Since another thread may have changed the /// value in the intervening time, it may have to retry, and does so in a spin loop. /// /// The net effect is that the value will always be the result of the application /// of the supplied function to a current value, atomically. However, because the /// function might be called multiple times, it must be free of side effects. /// /// Atoms are an efficient way to represent some state that will never need to be /// coordinated with any other, and for which you wish to make synchronous changes. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static IO> atomIO(M metadata, A value, Func validator) => lift(() => Atom(metadata, value, validator) switch { { IsSome: true } atom => (Atom)atom, _ => throw Exceptions.ValidationFailed }); /// /// Atomically updates the value by passing the old value to `f` and updating /// the atom with the result. Note: `f` may be called multiple times, so it /// should be free of side effects. /// /// Function to update the atom /// /// If the swap operation succeeded then a snapshot of the value that was set is returned. /// If the swap operation fails (which can only happen due to its validator returning false), /// then a snapshot of the current value within the Atom is returned. /// If there is no validator for the Atom then the return value is always the snapshot of /// the successful `f` function. /// public static IO swapIO(Atom ma, Func f) => ma.SwapIO(f); /// /// Atomically updates the value by passing the old value to `f` and updating /// the atom with the result. Note: `f` may be called multiple times, so it /// should be free of side effects. /// /// Function to update the atom /// /// If the swap operation succeeded then a snapshot of the value that was set is returned. /// If the swap operation fails (which can only happen due to its validator returning false), /// then a snapshot of the current value within the Atom is returned. /// If there is no validator for the Atom then the return value is always the snapshot of /// the successful `f` function. /// public static IO swapIO(Atom ma, Func f) => ma.SwapIO(f); /// /// Atomically updates the value by passing the old value to `f` and updating /// the atom with the result. Note: `f` may be called multiple times, so it /// should be free of side effects. /// /// Function to update the atom /// /// * If `f` returns `None` then no update occurs and the result of the call /// to `Swap` will be the latest (unchanged) value of `A`. /// * If the swap operation fails, due to its validator returning false, then a snapshot of /// the current value within the Atom is returned. /// * If the swap operation succeeded then a snapshot of the value that was set is returned. /// * If there is no validator for the Atom then the return value is always the snapshot of /// the successful `f` function. /// public static IO swapIO(Atom ma, Func> f) => ma.SwapMaybeIO(f); /// /// Atomically updates the value by passing the old value to `f` and updating /// the atom with the result. Note: `f` may be called multiple times, so it /// should be free of side effects. /// /// Function to update the atom /// /// * If `f` returns `None` then no update occurs and the result of the call /// to `Swap` will be the latest (unchanged) value of `A`. /// * If the swap operation fails, due to its validator returning false, then a snapshot of /// the current value within the Atom is returned. /// * If the swap operation succeeded then a snapshot of the value that was set is returned. /// * If there is no validator for the Atom then the return value is always the snapshot of /// the successful `f` function. /// public static IO swapIO(Atom ma, Func> f) => ma.SwapIO(f); /// /// Retrieve an IO computation that on each invocation will snapshot of the /// value in an atom /// /// Atom to snapshot /// Value type /// IO representation of the snapshot public static IO valueIO(Atom ma) => ma.ValueIO; /// /// Retrieve an IO computation that on each invocation will snapshot of the /// value in an atom /// /// Atom to snapshot /// Value type /// IO representation of the snapshot public static IO valueIO(Atom ma) => ma.ValueIO; /// /// Write a value to an atom. /// /// /// Note, this can be dangerous and is usually better to use `swapIO` if the /// `value` is derived from a snapshot of the atom's value (via `valueIO`). /// /// The computation is better run inside the swap operation for atomic consistency. /// /// However, using `writeIO` is reasonable if this is simply a forceful overwrite /// of the atomic value without any dependency on the previous state. /// /// Atom to write /// Value type /// IO representation of the write operation public static IO writeIO(Atom ma, A value) => ma.SwapIO(_ => value); /// /// Write a value to an atom. /// /// /// Note, this can be dangerous and is usually better to use `swapIO` if the /// `value` is derived from a snapshot of the atom's value (via `valueIO`). /// /// The computation is better run inside the swap operation for atomic consistency. /// /// However, using `writeIO` is reasonable if this is simply a forceful overwrite /// of the atomic value without any dependency on the previous state. /// /// Atom to write /// Value type /// IO representation of the write operation public static IO writeIO(Atom ma, A value) => ma.SwapIO((_, _) => value); } ================================================ FILE: LanguageExt.Core/Effects/IO/Prelude/IO.Prelude.cs ================================================ using System; using System.Diagnostics.Contracts; using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; using LanguageExt.Common; using LanguageExt.DSL; using LanguageExt.Traits; using LanguageExt.UnsafeValueAccess; namespace LanguageExt; public static partial class Prelude { /// /// Access the cancellation-token from the IO environment /// /// CancellationToken public static readonly IO cancelToken = IO.lift(e => e.Token); /// /// Request a cancellation of the IO expression /// public static readonly IO cancel = IO.lift( e => { e.Source.Cancel(); throw new OperationCanceledException(); }); /// /// Always yields a `Unit` value /// public static readonly IO unitIO = IO.pure(default); /// /// Yields the IO environment /// public static readonly IO envIO = IO.lift(e => e); /// /// Tail call /// /// /// /// public static IO tail(IO tailIO) => new IOTail(tailIO); /// /// Wraps this computation in a local-environment that ignores any cancellation-token cancellation requests. /// /// An uninterruptible computation public static IO uninterruptible(IO ma) => ma.Uninterruptible(); /// /// Yield the thread for the specified duration or until cancelled. /// /// Amount of time to yield for /// Unit [Pure] [MethodImpl(Opt.Default)] public static IO yieldFor(Duration duration) => IO.yieldFor(duration); /// /// Yield the thread for the specified duration or until cancelled. /// /// Amount of time to yield for /// Unit [Pure] [MethodImpl(Opt.Default)] public static IO yieldFor(TimeSpan timeSpan) => IO.yieldFor(timeSpan); /// /// Awaits all /// /// IO operations to await /// Sequence of results public static IO> awaitAll(params ForkIO[] forks) => awaitAll(forks.ToSeqUnsafe()); /// /// Awaits all operations /// /// Operations to await /// Sequence of results public static IO> awaitAll(Seq> ms) => IO.liftAsync(async eio => { var result = await Task.WhenAll(ms.Map(io => io.RunAsync(eio).AsTask())); return result.ToSeqUnsafe(); }); /// /// Awaits all /// /// IO operations to await /// Sequence of results public static IO> awaitAll(Seq>> forks) => IO.liftAsync(async eio => { var forks1 = forks.Map(mf => mf.Run(eio)); var result = await Task.WhenAll(forks1.Map(f => f.Await.RunAsync(eio).AsTask())); return result.ToSeqUnsafe(); }); /// /// Awaits all /// /// IO operations to await /// Sequence of results public static IO> awaitAll(Seq> forks) => IO.liftAsync(async eio => { var result = await Task.WhenAll(forks.Map(f => f.Await.RunAsync(eio).AsTask())); return result.ToSeqUnsafe(); }); /// /// Awaits for any IO to complete /// /// IO operations to await /// /// If we get one success, then we'll return straight away and cancel the others. /// If we get any errors, we'll collect them in the hope that at least one works. /// If we have collected as many errors as we have forks, then we'll return them all. /// public static IO awaitAny(params IO[] ms) => awaitAny(ms.ToSeqUnsafe()); /// /// Awaits for any IO to complete /// /// IO operations to await /// /// If we get one success, then we'll return straight away and cancel the others. /// If we get any errors, we'll collect them in the hope that at least one works. /// If we have collected as many errors as we have forks, then we'll return them all. /// public static IO awaitAny(params K[] ms) => awaitAny(ms.ToSeqUnsafe()); /// /// Awaits for any forks to complete /// /// Forks to await /// /// If we get one success, then we'll return straight away and cancel the others. /// If we get any errors, we'll collect them in the hope that at least one works. /// If we have collected as many errors as we have forks, then we'll return them all. /// public static IO awaitAny(params IO>[] forks) => awaitAny(forks.ToSeqUnsafe()); /// /// Awaits for any forks to complete /// /// Forks to await /// /// If we get one success, then we'll return straight away and cancel the others. /// If we get any errors, we'll collect them in the hope that at least one works. /// If we have collected as many errors as we have forks, then we'll return them all. /// public static IO awaitAny(params ForkIO[] forks) => awaitAny(forks.ToSeqUnsafe()); /// /// Awaits for any IO to complete /// /// IO operations to await /// /// If we get one success, then we'll return straight away and cancel the others. /// If we get any errors, we'll collect them in the hope that at least one works. /// If we have collected as many errors as we have forks, then we'll return them all. /// public static IO awaitAny(Seq> ms) => awaitAny(ms.Map(m => m.As())); /// /// Awaits for any IO to complete /// /// IO operations to await /// /// If we get one success, then we'll return straight away and cancel the others. /// If we get any errors, we'll collect them in the hope that at least one works. /// If we have collected as many errors as we have forks, then we'll return them all. /// public static IO awaitAny(Seq> ms) => IO.liftAsync(async eio => { if(eio.Token.IsCancellationRequested) throw new OperationCanceledException(); using var lenv = eio.LocalCancel; var result = await await Task.WhenAny(ms.Map(io => io.RunAsync(lenv).AsTask())); await lenv.Source.CancelAsync(); return result; }); /// /// Awaits for any forks to complete /// /// Forks to await /// /// If we get one success, then we'll return straight away and cancel the others. /// If we get any errors, we'll collect them in the hope that at least one works. /// If we have collected as many errors as we have forks, then we'll return them all. /// public static IO awaitAny(Seq> forks) => IO.liftAsync(async eio => { if(eio.Token.IsCancellationRequested) throw new OperationCanceledException(); using var lenv = eio.LocalCancel; var result = await await Task.WhenAny(forks.Map(f => f.Await.RunAsync(eio).AsTask())); await lenv.Source.CancelAsync(); return result; }); /// /// Awaits for any forks to complete /// /// Forks to await /// /// If we get one success, then we'll return straight away and cancel the others. /// If we get any errors, we'll collect them in the hope that at least one works. /// If we have collected as many errors as we have forks, then we'll return them all. /// public static IO awaitAny(Seq>> forks) => IO.liftAsync(async eio => { if(eio.Token.IsCancellationRequested) throw new OperationCanceledException(); using var lenv = eio.LocalCancel; var forks1 = forks.Map(mf => mf.Run(lenv)); var result = await await Task.WhenAny(forks1.Map(f => f.Await.RunAsync(eio).AsTask())); await lenv.Source.CancelAsync(); return result; }); /// /// Timeout operation if it takes too long /// [Pure] [MethodImpl(Opt.Default)] public static IO timeout(TimeSpan timeout, K ma) => ma.As().Timeout(timeout); /// /// Keeps repeating the computation /// /// Computation to repeat /// Computation bound value type /// The result of the last invocation of `ma` public static IO repeat(IO ma) => ma.Repeat(); /// /// Keeps repeating the computation until the scheduler expires /// /// Scheduler strategy for repeating /// Computation to repeat /// Computation bound value type /// The result of the last invocation of `ma` public static IO repeat(Schedule schedule, K ma) => ma.As().Repeat(schedule); /// /// Keeps repeating the computation until the predicate returns false /// /// Computation to repeat /// Computation bound value type /// The result of the last invocation of `ma` public static IO repeatWhile(IO ma, Func predicate) => ma.As().RepeatWhile(predicate); /// /// Keeps repeating the computation until the scheduler expires, or the predicate returns false /// /// Scheduler strategy for repeating /// Computation to repeat /// Computation bound value type /// The result of the last invocation of `ma` public static IO repeatWhile( Schedule schedule, K ma, Func predicate) => ma.As().RepeatWhile(schedule, predicate); /// /// Keeps repeating the computation until the predicate returns true /// /// Computation to repeat /// Computation bound value type /// The result of the last invocation of `ma` public static IO repeatUntil( K ma, Func predicate) => ma.As().RepeatUntil(predicate); /// /// Keeps repeating the computation until the scheduler expires, or the predicate returns true /// /// Scheduler strategy for repeating /// Computation to repeat /// Computation bound value type /// The result of the last invocation of `ma` public static IO repeatUntil( Schedule schedule, K ma, Func predicate) => ma.As().RepeatUntil(schedule, predicate); /// /// Keeps retrying the computation /// /// Computation to retry /// Computation bound value type /// The result of the last invocation of ma public static IO retry(K ma) => ma.As().Retry(); /// /// Keeps retrying the computation until the scheduler expires /// /// Scheduler strategy for retrying /// Computation to retry /// Computation bound value type /// The result of the last invocation of ma public static IO retry(Schedule schedule, K ma) => ma.As().Retry(schedule); /// /// Keeps retrying the computation until the predicate returns false /// /// Computation to retry /// Computation bound value type /// The result of the last invocation of ma public static IO retryWhile( K ma, Func predicate) => ma.As().RetryWhile(predicate); /// /// Keeps retrying the computation until the scheduler expires, or the predicate returns false /// /// Scheduler strategy for retrying /// Computation to retry /// Computation bound value type /// The result of the last invocation of ma public static IO retryWhile( Schedule schedule, K ma, Func predicate) => ma.As().RetryWhile(schedule, predicate); /// /// Keeps retrying the computation until the predicate returns true /// /// Computation to retry /// Computation bound value type /// The result of the last invocation of ma public static IO retryUntil( K ma, Func predicate) => ma.As().RetryUntil(predicate); /// /// Keeps retrying the computation until the scheduler expires, or the predicate returns true /// /// Scheduler strategy for retrying /// Computation to retry /// Computation bound value type /// The result of the last invocation of ma public static IO retryUntil( Schedule schedule, K ma, Func predicate) => ma.As().RetryUntil(schedule, predicate); } ================================================ FILE: LanguageExt.Core/Effects/IO/Prelude/IO.Prelude.mapapply.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public static partial class Prelude { /// /// Functor map operation /// /// /// Unwraps the value within the functor, passes it to the map function `f` provided, and /// then takes the mapped value and wraps it back up into a new functor. /// /// Functor to map /// Mapping function /// Mapped functor public static IO map(Func f, K ma) => Functor.map(f, ma).As(); /// /// Applicative action: runs the first applicative, ignores the result, and returns the second applicative /// public static IO action(K ma, K mb) => Applicative.action(ma, mb).As(); /// /// Applicative functor apply operation /// /// /// Unwraps the value within the `ma` applicative-functor, passes it to the unwrapped function(s) within `mf`, and /// then takes the resulting value and wraps it back up into a new applicative-functor. /// /// Value(s) applicative functor /// Mapping function(s) /// Mapped applicative functor public static IO apply(K> mf, K ma) => Applicative.apply(mf, ma).As(); } ================================================ FILE: LanguageExt.Core/Effects/IO/Resources.cs ================================================ using System; using System.Threading; using static LanguageExt.Prelude; namespace LanguageExt; /// /// Holds the acquired resources for the `ResourceT` monad transformer /// public class Resources : IDisposable { readonly AtomHashMap resources = AtomHashMap(); readonly Resources? parent; public Resources(Resources? parent) => this.parent = parent; public static IO NewIO(Resources? parent) => IO.lift(_ => new Resources(parent)); public void Dispose() { var s = new CancellationTokenSource(); var e = EnvIO.New(this, default, s, SynchronizationContext.Current); DisposeU(e); } public Unit DisposeU(EnvIO envIO) { if(resources.IsEmpty) return unit; using var source = new CancellationTokenSource(); var disposeEnv = new EnvIO(envIO.Resources, CancellationToken.None, source, envIO.SyncContext, null, 0); foreach (var item in resources) { item.Value.Release().Run(disposeEnv); } return default; } public Unit DisposeU() { Dispose(); return default; } public IO DisposeIO() => IO.lift(_ => DisposeU()); public Unit Acquire(A value) where A : IDisposable { var obj = (object?)value; if (obj is null) throw new InvalidCastException(); return resources.TryAdd(obj, new TrackedResourceDisposable(value)); } public Unit AcquireAsync(A value) where A : IAsyncDisposable { var obj = (object?)value; if (obj is null) throw new InvalidCastException(); return resources.TryAdd(obj, new TrackedResourceAsyncDisposable(value)); } public Unit Acquire(A value, Func> release) { var obj = (object?)value; if (obj is null) throw new InvalidCastException(); return resources.TryAdd(obj, new TrackedResourceWithFree(value, release)); } public IO Release(A value) { var obj = (object?)value; if (obj is null) throw new InvalidCastException(); return resources.Find(obj) .Match(Some: f => { resources.Remove(obj); return f.Release(); }, None: () => parent is null ? unitIO : parent.Release(value)); } public IO ReleaseAll() => IO.lift(envIO => { resources.Swap( r => { foreach (var kv in r) { kv.Value.Release().Run(envIO); } return []; }); return unit; }); internal Unit Merge(Resources rhs) => resources.Swap(r => r.AddRange(rhs.resources.AsIterable())); } abstract record TrackedResource { public abstract IO Release(); } /// /// Holds a resource with its disposal function /// record TrackedResourceWithFree(A Value, Func> Dispose) : TrackedResource { public override IO Release() => Dispose(Value); } /// /// Holds a resource with its disposal function /// record TrackedResourceDisposable(A Value) : TrackedResource where A : IDisposable { public override IO Release() => Value switch { IAsyncDisposable disposable => IO.liftAsync(async () => { await disposable.DisposeAsync().ConfigureAwait(false); return unit; }), _ => IO.lift(() => { Value.Dispose(); return unit; }) }; } /// /// Holds a resource with its disposal function /// record TrackedResourceAsyncDisposable(A Value) : TrackedResource where A : IAsyncDisposable { public override IO Release() => IO.liftAsync(async () => { await Value.DisposeAsync().ConfigureAwait(false); return unit; }); } ================================================ FILE: LanguageExt.Core/Effects/MinRT.cs ================================================ namespace LanguageExt.Effects; /// /// Minimal runtime for running the non-runtime based IO monads /// public readonly struct MinRT; ================================================ FILE: LanguageExt.Core/Effects/Prelude.cs ================================================ using System; using LanguageExt.Common; using LanguageExt.Traits; using System.Diagnostics.Contracts; using LanguageExt.UnsafeValueAccess; namespace LanguageExt; public static partial class Prelude { /// /// Tail call /// /// /// /// [Pure] public static K tailIO(K ma) where M : MonadUnliftIO => ma.MapIO(tail); /// /// Make this computation run on the `SynchronizationContext` that was captured at the start /// of the IO chain (i.e. the one embedded within the `EnvIO` environment that is passed through /// all IO computations) /// [Pure] public static K postIO(K ma) where M : MonadUnliftIO => M.PostIO(ma); /// /// Queue this IO operation to run on the thread-pool. /// /// Maximum time that the forked IO operation can run for. `None` for no timeout. /// Returns a `ForkIO` data-structure that contains two IO effects that can be used to either cancel /// the forked IO operation or to await the result of it. /// [Pure] public static K mapIO(K ma, Func, IO> f) where M : MonadUnliftIO, Monad => M.MapIO(ma, f); /// /// Wraps this computation in a local-environment that ignores any cancellation-token cancellation requests. /// /// An uninterruptible computation [Pure] public static K uninterruptible(K ma) where M : MonadUnliftIO => M.UninterruptibleIO(ma); /// /// Creates a local cancellation environment /// /// /// A local cancellation environment stops other IO computations, that rely on the same /// environmental cancellation token, from being taken down by a regional cancellation. /// /// If an `IO.cancel` is invoked locally, then it will still create an exception that /// propagates upwards and so catching cancellations is still important. /// /// Computation to run within the local context /// Bound value /// Result of the computation [Pure] public static K localIO(K ma) where M : MonadUnliftIO => M.LocalIO(ma); /// /// Queue this IO operation to run on the thread-pool. /// /// Maximum time that the forked IO operation can run for. `None` for no timeout. /// Returns a `ForkIO` data-structure that contains two IO effects that can be used to either cancel /// the forked IO operation or to await the result of it. /// [Pure] public static K> fork(K ma, Option timeout = default) where M : MonadUnliftIO, Monad => M.ForkIO(ma, timeout); /// /// Queue this IO operation to run on the thread-pool. /// /// Maximum time that the forked IO operation can run for. `None` for no timeout. /// Returns a `ForkIO` data-structure that contains two IO effects that can be used to either cancel /// the forked IO operation or to await the result of it. /// [Pure] public static K awaitIO(K> ma) where M : MonadUnliftIO => M.Await(ma); /// /// Awaits all operations /// /// Operations to await /// Sequence of results [Pure] public static K> awaitAll(params K[] ms) where M : MonadUnliftIO => awaitAll(ms.ToSeqUnsafe()); /// /// Awaits all forks /// /// Forks to await /// Sequence of results [Pure] public static K> awaitAll(params K>[] forks) where M : MonadUnliftIO => awaitAll(forks.ToSeqUnsafe()); /// /// Awaits all operations /// /// Operations to await /// Sequence of results [Pure] public static K> awaitAll(Seq> ms) where M : MonadUnliftIO => ms.Traverse(f => f.ToIO()) .Bind(awaitAll); /// /// Awaits all forks /// /// Forks to await /// Sequence of results [Pure] public static K> awaitAll(Seq>> forks) where M : MonadUnliftIO => forks.TraverseM(f => f.Await()); /// /// Awaits for any operation to complete /// /// Operations to await /// /// If we get one success, then we'll return straight away and cancel the others. /// If we get any errors, we'll collect them in the hope that at least one works. /// If we have collected as many errors as we have forks, then we'll return them all. /// [Pure] public static K awaitAny(params K[] ms) where M : MonadUnliftIO => awaitAny(ms.ToSeqUnsafe()); /// /// Awaits for any forks to complete /// /// Forks to await /// /// If we get one success, then we'll return straight away and cancel the others. /// If we get any errors, we'll collect them in the hope that at least one works. /// If we have collected as many errors as we have forks, then we'll return them all. /// [Pure] public static K awaitAny(params K>[] forks) where M : MonadUnliftIO => awaitAny(forks.ToSeqUnsafe()); /// /// Awaits for any forks to complete /// /// Forks to await /// /// If we get one success, then we'll return straight away and cancel the others. /// If we get any errors, we'll collect them in the hope that at least one works. /// If we have collected as many errors as we have forks, then we'll return them all. /// [Pure] public static K awaitAny(Seq>> forks) where M : MonadUnliftIO => forks.Traverse(f => f.ToIO()) .Bind(awaitAny); /// /// Awaits for operations to complete /// /// Operations to await /// /// If we get one success, then we'll return straight away and cancel the others. /// If we get any errors, we'll collect them in the hope that at least one works. /// If we have collected as many errors as we have forks, then we'll return them all. /// [Pure] public static K awaitAny(Seq> forks) where M : MonadUnliftIO => forks.Traverse(f => f.ToIO()) .Bind(awaitAny); /// /// Timeout operation if it takes too long /// [Pure] public static K timeout(TimeSpan timeout, K ma) where M : MonadUnliftIO => ma.TimeoutIO(timeout); /// /// Keeps repeating the computation /// /// Computation to repeat /// Computation bound value type /// The result of the last invocation of `ma` [Pure] public static K repeat(K ma) where M : MonadUnliftIO => ma.RepeatIO(); /// /// Keeps repeating the computation, until the scheduler expires /// /// Scheduler strategy for repeating /// Computation to repeat /// Computation bound value type /// The result of the last invocation of `ma` [Pure] public static K repeat(Schedule schedule, K ma) where M : MonadUnliftIO => ma.RepeatIO(schedule); /// /// Keeps repeating the computation until the predicate returns false /// /// Computation to repeat /// Computation bound value type /// The result of the last invocation of `ma` [Pure] public static K repeatWhile(K ma, Func predicate) where M : MonadUnliftIO => ma.RepeatWhileIO(predicate); /// /// Keeps repeating the computation until the scheduler expires, or the predicate returns false /// /// Scheduler strategy for repeating /// Computation to repeat /// Computation bound value type /// The result of the last invocation of `ma` [Pure] public static K repeatWhile( Schedule schedule, K ma, Func predicate) where M : MonadUnliftIO => ma.RepeatWhileIO(schedule, predicate); /// /// Keeps repeating the computation until the predicate returns true /// /// Computation to repeat /// Computation bound value type /// The result of the last invocation of `ma` [Pure] public static K repeatUntil( K ma, Func predicate) where M : MonadUnliftIO => ma.RepeatUntilIO(predicate); /// /// Keeps repeating the computation until the scheduler expires, or the predicate returns true /// /// Scheduler strategy for repeating /// Computation to repeat /// Computation bound value type /// The result of the last invocation of `ma` [Pure] public static K repeatUntil( Schedule schedule, K ma, Func predicate) where M : MonadUnliftIO => ma.RepeatUntilIO(schedule, predicate); /// /// Keeps retrying the computation /// /// Computation to retry /// Computation bound value type /// The result of the last invocation of ma [Pure] public static K retry(K ma) where M : MonadUnliftIO => ma.RetryIO(); /// /// Keeps retrying the computation until the scheduler expires /// /// Scheduler strategy for retrying /// Computation to retry /// Computation bound value type /// The result of the last invocation of ma [Pure] public static K retry(Schedule schedule, K ma) where M : MonadUnliftIO => ma.RetryIO(schedule); /// /// Keeps retrying the computation until the predicate returns false /// /// Computation to retry /// Computation bound value type /// The result of the last invocation of ma [Pure] public static K retryWhile( K ma, Func predicate) where M : MonadUnliftIO => ma.RetryWhileIO(predicate); /// /// Keeps retrying the computation until the scheduler expires, or the predicate returns false /// /// Scheduler strategy for retrying /// Computation to retry /// Computation bound value type /// The result of the last invocation of ma [Pure] public static K retryWhile( Schedule schedule, K ma, Func predicate) where M : MonadUnliftIO => ma.RetryWhileIO(schedule, predicate); /// /// Keeps retrying the computation until the predicate returns true /// /// Computation to retry /// Computation bound value type /// The result of the last invocation of ma [Pure] public static K retryUntil( K ma, Func predicate) where M : MonadUnliftIO => ma.RetryUntilIO(predicate); /// /// Keeps retrying the computation until the scheduler expires, or the predicate returns true /// /// Scheduler strategy for retrying /// Computation to retry /// Computation bound value type /// The result of the last invocation of ma [Pure] public static K retryUntil( Schedule schedule, K ma, Func predicate) where M : MonadUnliftIO => ma.RetryUntilIO(schedule, predicate); } ================================================ FILE: LanguageExt.Core/Effects/README.md ================================================ Effects are functorial, monadic, and applicative types that are designed to capture IO based side-effects. | Section | Type | Description | |----------------------|-----------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | [`IO`](IO) | [`IO`](IO) | Asynchronous and synchronous IO. Captures side-effects, manages resources, but throws exceptions. The `IO` monad is the base of all IO based operations and should be used in your monad-transformer stacks when you need IO. | | [`Eff`](Eff) | [`Eff`](Eff) | Asynchronous and synchronous IO. Captures side-effects, manages resources, handles exceptions elegantly. | | [`Eff`](Eff) | [`Eff`](Eff) | Asynchronous and synchronous IO. Captures side-effects, manages resources, handles exceptions elegantly, and has an injectable runtime (`RT`) which can provide configuration and dependency-injection | ================================================ FILE: LanguageExt.Core/Effects/Schedule/Duration.cs ================================================ using System; using System.Diagnostics.Contracts; using System.Runtime.CompilerServices; namespace LanguageExt; /// /// Period of time in milliseconds. /// Can be used to convert between other duration like types, such as TimeSpan and Time. /// public readonly struct Duration : IEquatable, IComparable { public readonly double Milliseconds; /// /// Duration constructor /// /// Magnitude of the duration. Must be zero or a positive value /// Throws if `milliseconds` is less than `0` public Duration(double milliseconds) { if (milliseconds < 0) throw new ArgumentException($"{nameof(milliseconds)} must be a positive number."); Milliseconds = milliseconds; } /// /// Zero magnitude duration (instant) /// public static Duration Zero = new(0); /// /// Return true if the time duration is zero /// public bool IsZero => Milliseconds < 0.00000000001; /// /// Random duration between the provided min and max durations. /// /// /// This can be used to seed a schedule in parallel. /// Providing another method of de-correlation. /// /// For example, this is a linear schedule that, /// /// - starts with a seed duration between 10 and 50 milliseconds /// - includes a 10% jitter, added and removed in sequence from each duration /// - recurring 5 times /// /// Schedule.linear(Duration.Random(10*ms, 50*ms)) | Schedule.decorrelate() | Schedule.recurs(5) /// /// Three runs result in, /// /// (25ms, 23ms, 50ms, 47ms, 72ms) /// (13ms, 11ms, 25ms, 23ms, 40ms) /// (28ms, 25ms, 56ms, 53ms, 87ms) /// /// /// min duration /// max duration /// optional seed /// random duration between min and max duration [Pure] public static Duration random(Duration min, Duration max, Option seed = default) => new(SingletonRandom.Uniform(min, max, seed)); [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator Duration(double milliseconds) => new(milliseconds); [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator Duration(TimeSpan timeSpan) => new(timeSpan.TotalMilliseconds); [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator Duration(Time time) => new(time.Milliseconds); [Pure] public static implicit operator double(Duration duration) => duration.Milliseconds; [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static explicit operator TimeSpan(Duration duration) => TimeSpan.FromMilliseconds(duration.Milliseconds); [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static explicit operator Time(Duration duration) => duration.Milliseconds.Milliseconds(); [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(Duration a, Duration b) => a.Milliseconds.Equals(b.Milliseconds); [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(Duration a, Duration b) => !(a == b); [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(Duration other) => Milliseconds.Equals(other); [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public override bool Equals(object? obj) => obj is Duration other && Equals(other); [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public override int GetHashCode() => Milliseconds.GetHashCode(); [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public int CompareTo(Duration other) => Milliseconds.CompareTo(other); [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator >(Duration a, Duration b) => a.CompareTo(b) > 0; [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator >=(Duration a, Duration b) => a.CompareTo(b) >= 0; [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator <(Duration a, Duration b) => a.CompareTo(b) < 0; [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator <=(Duration a, Duration b) => a.CompareTo(b) <= 0; [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public override string ToString() => $"{nameof(Duration)}({(TimeSpan)this})"; } ================================================ FILE: LanguageExt.Core/Effects/Schedule/Schedule.Constructors.cs ================================================ using System; using System.Diagnostics.Contracts; using static LanguageExt.Prelude; namespace LanguageExt; public abstract partial record Schedule { /// /// Identity or no-op schedule result transformer /// public static readonly ScheduleTransformer Identity = Transform(identity); /// /// `Schedule` constructor that recurs for the specified durations /// /// durations to apply [Pure] public static Schedule TimeSeries(params Duration[] durations) => new SchItems(durations.AsIterable()); /// /// `Schedule` constructor that recurs for the specified durations /// /// durations to apply [Pure] public static Schedule TimeSeries(Arr durations) => new SchItems(durations.AsIterable()); /// /// `Schedule` constructor that recurs for the specified durations /// /// durations to apply [Pure] public static Schedule TimeSeries(Seq durations) => new SchItems(durations.AsIterable()); /// /// `Schedule` constructor that recurs for the specified durations /// /// durations to apply [Pure] public static Schedule TimeSeries(Lst durations) => new SchItems(durations.AsIterable()); /// /// `Schedule` constructor that recurs for the specified durations /// /// durations to apply [Pure] public static Schedule TimeSeries(Set durations) => new SchItems(IterableExtensions.AsIterable(durations.Value)); /// /// `Schedule` constructor that recurs for the specified durations /// /// durations to apply [Pure] public static Schedule TimeSeries(HashSet durations) => new SchItems(durations.AsIterable()); /// /// `ScheduleTransformer` constructor which provides mapping capabilities for `Schedule` instances /// /// Transformation function /// `ScheduleTransformer` [Pure] public static ScheduleTransformer Transform(Func transform) => new(transform); /// /// Schedule that runs forever /// public static readonly Schedule Forever = SchForever.Default; /// /// Schedule that never runs /// public static readonly Schedule Never = SchNever.Default; /// /// Schedule that runs once /// public static readonly Schedule Once = Forever.Take(1); /// /// A schedule transformer that will enforce the first retry has no delay /// public static readonly ScheduleTransformer NoDelayOnFirst = Transform(s => s.Tail.Prepend(Duration.Zero)); /// /// Repeats the schedule forever /// public static readonly ScheduleTransformer RepeatForever = Transform(s => new SchRepeatForever(s)); /// /// Schedule transformer that limits the schedule to run the specified number of times /// /// number of times [Pure] public static ScheduleTransformer recurs(int times) => Transform(s => s.Take(times)); /// /// Schedule that recurs continuously with the given spacing /// /// space [Pure] public static Schedule spaced(Duration space) => Forever.Map(_ => space); /// /// Schedule that recurs continuously using a linear backoff /// /// seed /// optional factor to apply, default 1 [Pure] public static Schedule linear(Duration seed, double factor = 1) => new SchLinear(seed, factor); /// /// Schedule that recurs continuously using a exponential backoff /// /// seed /// optional factor to apply, default 2 [Pure] public static Schedule exponential(Duration seed, double factor = 2) => Forever.Map((_, i) => seed * Math.Pow(factor, i)); /// /// Schedule that recurs continuously using a fibonacci based backoff /// /// seed [Pure] public static Schedule fibonacci(Duration seed) => new SchFibonacci(seed); internal static readonly Func LiveNowFn = () => DateTime.Now; /// /// Schedule that runs for a given duration /// /// max duration to run the schedule for /// current time function [Pure] public static Schedule upto(Duration max, Func? currentTimeFn = null) => new SchUpTo(max, currentTimeFn); [Pure] internal static Duration secondsToIntervalStart(DateTime startTime, DateTime currentTime, Duration interval) => interval - (currentTime - startTime).TotalMilliseconds % interval; /// /// Schedule that recurs on a fixed interval. /// /// If the action run between updates takes longer than the interval, then the /// action will be run immediately, but re-runs will not "pile up". /// /// /// |-----interval-----|-----interval-----|-----interval-----| /// |---------action--------||action|-----|action|-----------| /// /// /// schedule interval /// current time function [Pure] public static Schedule fixedInterval(Duration interval, Func? currentTimeFn = null) => new SchFixed(interval, currentTimeFn); /// /// A schedule that divides the timeline into `interval`-long windows, and sleeps /// until the nearest window boundary every time it recurs. /// /// For example, `Windowed(10 * seconds)` would produce a schedule as follows: /// /// 10s 10s 10s 10s /// |----------|----------|----------|----------| /// |action------|sleep---|act|-sleep|action----| /// /// /// schedule interval /// current time function [Pure] public static Schedule windowed(Duration interval, Func? currentTimeFn = null) => new SchWindowed(interval, currentTimeFn); [Pure] internal static int durationToIntervalStart(int intervalStart, int currentIntervalPosition, int intervalWidth) { var steps = intervalStart - currentIntervalPosition; return steps > 0 ? steps : steps + intervalWidth; } [Pure] internal static int roundBetween(int value, int min, int max) => value > max ? max : value < min ? min : value; /// /// Cron-like schedule that recurs every specified `second` of each minute /// /// second of the minute, will be rounded to fit between 0 and 59 /// current time function [Pure] public static Schedule secondOfMinute(int second, Func? currentTimeFn = null) => new SchSecondOfMinute(second, currentTimeFn); /// /// Cron-like schedule that recurs every specified `minute` of each hour /// /// minute of the hour, will be rounded to fit between 0 and 59 /// current time function [Pure] public static Schedule minuteOfHour(int minute, Func? currentTimeFn = null) => new SchMinuteOfHour(minute, currentTimeFn); /// /// Cron-like schedule that recurs every specified `hour` of each day /// /// hour of the day, will be rounded to fit between 0 and 23 /// current time function [Pure] public static Schedule hourOfDay(int hour, Func? currentTimeFn = null) => new SchHourOfDay(hour, currentTimeFn); /// /// Cron-like schedule that recurs every specified `day` of each week /// /// day of the week /// current time function [Pure] public static Schedule dayOfWeek(DayOfWeek day, Func? currentTimeFn = null) => new SchDayOfWeek(day, currentTimeFn); /// /// A schedule transformer that limits the returned delays to max delay /// /// max delay to return [Pure] public static ScheduleTransformer maxDelay(Duration max) => Transform(s => new SchMaxDelay(s, max)); /// /// Limits the schedule to the max cumulative delay /// /// max delay to stop schedule at [Pure] public static ScheduleTransformer maxCumulativeDelay(Duration max) => Transform(s => new SchMaxCumulativeDelay(s, max)); /// /// A schedule transformer that adds a random jitter to any returned delay /// /// min random milliseconds /// max random milliseconds /// optional seed [Pure] public static ScheduleTransformer jitter(Duration minRandom, Duration maxRandom, Option seed = default) => Transform(s => new SchJitter1(s, minRandom, maxRandom, seed)); /// /// A schedule transformer that adds a random jitter to any returned delay /// /// jitter factor based on the returned delay /// optional seed [Pure] public static ScheduleTransformer jitter(double factor = 0.5, Option seed = default) => Transform(s => new SchJitter2(s, factor, seed)); /// /// Transforms the schedule by de-correlating each of the durations both up and down in a jittered way /// /// /// Given a linear schedule starting at 100. (100, 200, 300...) /// Adding de-correlation to it might produce a result like this, (103.2342, 97.123, 202.3213, 197.321...) /// The overall schedule runs twice as long but should be less correlated when used in parallel. /// /// jitter factor based on the returned delay /// optional seed [Pure] public static ScheduleTransformer decorrelate(double factor = 0.1, Option seed = default) => Transform(s => new SchDecorrelate(s, factor, seed)); /// /// Resets the schedule after a provided cumulative max duration /// /// max delay to reset the schedule at [Pure] public static ScheduleTransformer resetAfter(Duration max) => Transform(s => new SchResetAfter(s, max)); /// /// Repeats the schedule n number of times /// /// number of times to repeat the schedule /// /// NOTE: This repeats the entire schedule! Use `Schedule.recurs(n)` for 'number of attempts'. /// [Pure] public static ScheduleTransformer repeat(int times) => Transform(s => new SchRepeat(s, times)); /// /// Intersperse the provided duration(s) between each duration in the schedule /// /// schedule to intersperse [Pure] public static ScheduleTransformer intersperse(Schedule schedule) => Transform(s => s.Bind(schedule.Prepend)); /// /// Intersperse the provided duration(s) between each duration in the schedule /// /// 1 or more durations to intersperse [Pure] public static ScheduleTransformer intersperse(params Duration[] durations) => intersperse(TimeSeries(durations)); [Obsolete("`Spaced` has been renamed to `spaced`")] public static Schedule Spaced(Duration space) => spaced(space); [Obsolete("`Recurs` has been renamed to `recurs`")] public static ScheduleTransformer Recurs(int times) => recurs(times); [Obsolete("`Exponential` has been renamed to `exponential`")] public static Schedule Exponential(Duration seed, double factor = 2) => exponential(seed, factor); [Obsolete("`Fibonacci` has been renamed to `fibonacci`")] public static Schedule Fibonacci(Duration seed) => fibonacci(seed); } ================================================ FILE: LanguageExt.Core/Effects/Schedule/Schedule.DSL.cs ================================================ using System; using System.Linq; using System.Collections.Generic; using static LanguageExt.UnitsOfMeasure; namespace LanguageExt; /// /// Time series of durations /// internal record SchItems(Iterable Items) : Schedule { public override Iterable Run() => Items; } /// /// Functor map /// internal record SchMap(Schedule Schedule, Func F) : Schedule { public override Iterable Run() => Schedule.Run().Map(F); } /// /// Functor map /// internal record SchMapIndex(Schedule Schedule, Func F) : Schedule { public override Iterable Run() => Schedule.Run().Select(F); } /// /// Filter /// internal record SchFilter(Schedule Schedule, Func Pred) : Schedule { public override Iterable Run() => Schedule.Run().Filter(Pred); } /// /// Functor bind /// internal record SchBind(Schedule Schedule, Func BindF) : Schedule { public override Iterable Run() => Schedule.Run().Bind(x => BindF(x).Run()); } /// /// Functor bind and project /// internal record SchBind2(Schedule Schedule, Func BindF, Func Project) : Schedule { public override Iterable Run() => Schedule.Run().Bind(x => BindF(x).Run().Map(y => Project(x, y))); } /// /// Tail of sequence /// internal record SchTail(Schedule Schedule) : Schedule { public override Iterable Run() => Schedule.Run().Tail(); } /// /// Skip items in sequence /// internal record SchSkip(Schedule Schedule, int Count) : Schedule { public override Iterable Run() => Schedule.Run().Skip(Count); } /// /// Take items in sequence /// internal record SchTake(Schedule Schedule, int Count) : Schedule { public override Iterable Run() => Schedule.Run().Take(Count); } /// /// Append in sequence /// internal record SchCombine(Schedule Left, Schedule Right) : Schedule { public override Iterable Run() => Left.Run().Combine(Right.Run()); } /// /// Interleave items in sequence /// internal record SchInterleave(Schedule Left, Schedule Right) : Schedule { public override Iterable Run() => Left.Run() .Zip(Right.Run(), static (d1, d2) => new[] {d1, d2}) .SelectMany(x => x); } /// /// Union sequence /// internal record SchUnion(Schedule Left, Schedule Right) : Schedule { public override Iterable Run() { return Go().AsIterable(); IEnumerable Go() { using var aEnumerator = Left.Run().GetEnumerator(); using var bEnumerator = Right.Run().GetEnumerator(); var hasA = aEnumerator.MoveNext(); var hasB = bEnumerator.MoveNext(); while (hasA || hasB) { yield return hasA switch { true when hasB => Math.Min(aEnumerator.Current, bEnumerator.Current), true => aEnumerator.Current, _ => bEnumerator.Current }; hasA = hasA && aEnumerator.MoveNext(); hasB = hasB && bEnumerator.MoveNext(); } } } } /// /// Intersect sequence /// internal record SchIntersect(Schedule Left, Schedule Right) : Schedule { public override Iterable Run() => Left.Run() .Zip(Right.Run()) .Map(static t => (Duration)Math.Max(t.Item1, t.Item2)); } /// /// Cons an item onto sequence /// internal record SchCons(Duration Left, Schedule Right) : Schedule { public override Iterable Run() { return Go().AsIterable(); IEnumerable Go() { yield return Left; foreach (var r in Right.Run()) { yield return r; } } } } internal record SchRepeatForever(Schedule Schedule) : Schedule { public override Iterable Run() { return Go().AsIterable(); IEnumerable Go() { while (true) foreach (var x in Schedule.Run()) yield return x; } } } internal record SchLinear(Duration Seed, double Factor) : Schedule { public override Iterable Run() { return Go().AsIterable(); IEnumerable Go() { Duration delayToAdd = Seed * Factor; var accumulator = Seed; yield return accumulator; while (true) { accumulator += delayToAdd; yield return accumulator; } } } } internal record SchFibonacci(Duration Seed) : Schedule { public override Iterable Run() { return Go().AsIterable(); IEnumerable Go() { var last = Duration.Zero; var accumulator = Seed; yield return accumulator; while (true) { var current = accumulator; accumulator += last; last = current; yield return accumulator; } } } } internal record SchForever : Schedule { public static readonly Schedule Default = new SchForever(); public override Iterable Run() { return Go().AsIterable(); IEnumerable Go() { while(true) yield return Duration.Zero; } } } internal record SchNever : Schedule { public static readonly Schedule Default = new SchNever(); public override Iterable Run() => Iterable.empty(); } internal record SchUpTo(Duration Max, Func? CurrentTimeFn = null) : Schedule { public override Iterable Run() { return Go().AsIterable(); IEnumerable Go() { var now = CurrentTimeFn ?? LiveNowFn; var startTime = now(); while (now() - startTime < Max) yield return Duration.Zero; } } } internal record SchFixed(Duration Interval, Func? CurrentTimeFn = null) : Schedule { public override Iterable Run() { return Go().AsIterable(); IEnumerable Go() { var now = CurrentTimeFn ?? LiveNowFn; var startTime = now(); var lastRunTime = startTime; while (true) { var currentTime = now(); var runningBehind = currentTime > lastRunTime + (TimeSpan)Interval; var boundary = Interval == Duration.Zero ? Interval : secondsToIntervalStart(startTime, currentTime, Interval); var sleepTime = boundary == Duration.Zero ? Interval : boundary; lastRunTime = runningBehind ? currentTime : currentTime + (TimeSpan)sleepTime; yield return runningBehind ? Duration.Zero : sleepTime; } } } } internal record SchWindowed(Duration Interval, Func? CurrentTimeFn = null) : Schedule { public override Iterable Run() { return Go().AsIterable(); IEnumerable Go() { var now = CurrentTimeFn ?? LiveNowFn; var startTime = now(); while (true) { var currentTime = now(); yield return secondsToIntervalStart(startTime, currentTime, Interval); } } } } internal record SchSecondOfMinute(int Second, Func? CurrentTimeFn = null) : Schedule { public override Iterable Run() { return Go().AsIterable(); IEnumerable Go() { var now = CurrentTimeFn ?? LiveNowFn; while (true) yield return durationToIntervalStart(roundBetween(Second, 0, 59), now().Second, 60) * seconds; } } } internal record SchMinuteOfHour(int Minute, Func? CurrentTimeFn = null) : Schedule { public override Iterable Run() { return Go().AsIterable(); IEnumerable Go() { var now = CurrentTimeFn ?? LiveNowFn; while (true) yield return durationToIntervalStart(roundBetween(Minute, 0, 59), now().Minute, 60) * minutes; } } } internal record SchHourOfDay(int Hour, Func? CurrentTimeFn = null) : Schedule { public override Iterable Run() { return Go().AsIterable(); IEnumerable Go() { var now = CurrentTimeFn ?? LiveNowFn; while (true) yield return durationToIntervalStart(roundBetween(Hour, 0, 23), now().Hour, 24) * hours; } } } internal record SchDayOfWeek(DayOfWeek Day, Func? CurrentTimeFn = null) : Schedule { public override Iterable Run() { return Go().AsIterable(); IEnumerable Go() { var now = CurrentTimeFn ?? LiveNowFn; while (true) yield return durationToIntervalStart((int)Day + 1, (int)now().DayOfWeek + 1, 7) * days; } } } internal record SchMaxDelay(Schedule Schedule, Duration Max) : Schedule { public override Iterable Run() => Schedule.Run().Map(x => x > Max ? Max : x); } internal record SchMaxCumulativeDelay(Schedule Schedule, Duration Max) : Schedule { public override Iterable Run() { return Go().AsIterable(); IEnumerable Go() { var totalAppliedDelay = Duration.Zero; foreach (var duration in Schedule.Run()) { if (totalAppliedDelay >= Max) yield break; totalAppliedDelay += duration; yield return duration; } } } } internal record SchJitter1(Schedule Schedule, Duration MinRandom, Duration MaxRandom, Option Seed) : Schedule { public override Iterable Run() => Schedule.Run().Map(x => (Duration)(x + SingletonRandom.Uniform(MinRandom, MaxRandom, Seed))); } internal record SchJitter2(Schedule Schedule, double Factor, Option Seed) : Schedule { public override Iterable Run() => Schedule.Run().Map(x => (Duration)(x + SingletonRandom.Uniform(0, x * Factor, Seed))); } internal record SchDecorrelate(Schedule Schedule, double Factor, Option Seed) : Schedule { public override Iterable Run() { return Go().AsIterable(); IEnumerable Go() { foreach(var currentMilliseconds in Schedule.Run()) { var rand1 = SingletonRandom.Uniform(0, currentMilliseconds * Factor, Seed); var rand2 = SingletonRandom.Uniform(0, currentMilliseconds * Factor, Seed); yield return currentMilliseconds + rand1; yield return currentMilliseconds - rand2; } } } } internal record SchResetAfter(Schedule Schedule, Duration Max) : Schedule { public override Iterable Run() { return Go().AsIterable(); IEnumerable Go() { while (true) foreach (var duration in (Schedule | maxCumulativeDelay(Max)).Run()) yield return duration; } } } internal record SchRepeat(Schedule Schedule, int Times) : Schedule { public override Iterable Run() { return Go().AsIterable(); IEnumerable Go() { for (var i = 0; i < Times; i++) foreach (var duration in Schedule.Run()) yield return duration; } } } ================================================ FILE: LanguageExt.Core/Effects/Schedule/Schedule.Extensions.cs ================================================ using System.Diagnostics.Contracts; namespace LanguageExt; public static class ScheduleExtensions { /// /// Converts a `Seq` of positive durations to a schedule /// /// Seq of positive durations /// schedule [Pure] public static Schedule ToSchedule(this Seq seq) => Schedule.TimeSeries(seq); /// /// Converts a `Arr` of positive durations to a schedule /// /// array of positive durations /// schedule [Pure] public static Schedule ToSchedule(this Arr array) => Schedule.TimeSeries(array); /// /// Converts a `Lst` of positive durations to a schedule /// /// list of positive durations /// schedule [Pure] public static Schedule ToSchedule(this Lst list) => Schedule.TimeSeries(list); /// /// Converts a `Set` of positive durations to a schedule /// /// set of positive durations /// schedule [Pure] public static Schedule ToSchedule(this Set set) => Schedule.TimeSeries(set); /// /// Converts a `HashSet` of positive durations to a schedule /// /// hashset of positive durations /// schedule [Pure] public static Schedule ToSchedule(this HashSet hashSet) => Schedule.TimeSeries(hashSet); /// /// Prepend a duration to the schedule /// /// Duration to prepend /// Schedule /// Schedule with the duration prepended [Pure] public static Schedule Cons(this Duration value, Schedule s) => s.Prepend(value); } ================================================ FILE: LanguageExt.Core/Effects/Schedule/Schedule.Prelude.cs ================================================ using System; using System.Diagnostics.Contracts; namespace LanguageExt; public static partial class Prelude { /// /// Intersection of two schedules. As long as they are both running it returns the max duration /// /// Schedule `a` /// Schedule `b` /// Max of schedule `a` and `b` to the length of the shortest schedule [Pure] public static Schedule intersect(Schedule a, Schedule b) => a.Intersect(b); /// /// Union of two schedules. As long as any are running it returns the min duration of both or a or b /// /// Schedule `a` /// Schedule `b` /// Min of schedule `a` and `b` or `a` or `b` to the length of the longest schedule [Pure] public static Schedule union(Schedule a, Schedule b) => a.Union(b); /// /// Interleave two schedules together /// /// Schedule `a` /// Schedule `b` /// Returns the two schedules interleaved together [Pure] public static Schedule interleave(Schedule a, Schedule b) => a.Interleave(b); /// /// Append two schedules together /// /// Schedule `a` /// Schedule `b` /// Returns the two schedules appended [Pure] public static Schedule append(Schedule a, Schedule b) => a.Combine(b); /// /// Take `amount` durations from the `Schedule` /// /// Schedule to take from /// Amount ot take /// Schedule with `amount` or less durations [Pure] public static Schedule take(Schedule s, int amount) => s.Take(amount); /// /// Skip `amount` durations from the `Schedule` /// /// Schedule to skip durations from /// Amount ot skip /// Schedule with `amount` durations skipped [Pure] public static Schedule skip(Schedule s, int amount) => s.Skip(amount); /// /// Take all but the first duration from the schedule /// [Pure] public static Schedule tail(Schedule s) => s.Tail; /// /// Functor map operation for Schedule /// /// Schedule /// Mapping function /// Mapped schedule [Pure] public static Schedule map(Schedule s, Func f) => s.Map(f); /// /// Filter operation for Schedule /// /// Schedule /// predicate /// Filtered schedule [Pure] public static Schedule filter(Schedule s, Func pred) => s.Filter(pred); /// /// Monad bind operation for Schedule /// /// Schedule /// Bind function /// Chained schedule [Pure] public static Schedule bind(Schedule s, Func f) => s.Bind(f); } ================================================ FILE: LanguageExt.Core/Effects/Schedule/Schedule.cs ================================================ using System; using System.Diagnostics.Contracts; using LanguageExt.Traits; namespace LanguageExt; /// /// A schedule is defined as a potentially infinite stream of durations, combined with mechanisms for composing them. /// /// /// Used heavily by `repeat`, `retry`, and `fold` with the effect types. Use the static methods to create parts /// of schedulers and then union them using `|` or intersect them using `&`. Union will take the minimum of the two /// schedules to the length of the longest, intersect will take the maximum of the two schedules to the length of the /// shortest. /// /// /// This example creates a schedule that repeats 5 times, with an exponential delay between each stage, starting /// at 10 milliseconds: /// /// var s = Schedule.recurs(5) | Schedule.exponential(10*ms) /// /// /// /// This example creates a schedule that repeats 5 times, with an exponential delay between each stage, starting /// at 10 milliseconds and with a maximum delay of 2000 milliseconds: /// /// var s = Schedule.recurs(5) | Schedule.exponential(10*ms) | Schedule.spaced(2000*ms) /// /// /// This example creates a schedule that repeats 5 times, with an exponential delay between each stage, starting /// at 10 milliseconds and with a minimum delay of 300 milliseconds: /// /// var s = Schedule.recurs(5) | Schedule.exponential(10*ms) & Schedule.spaced(300*ms) /// public abstract partial record Schedule : Semigroup { [Pure] public static Schedule operator |(Schedule a, Schedule b) => a.Union(b); [Pure] public static Schedule operator |(Schedule a, ScheduleTransformer b) => b.Apply(a); [Pure] public static Schedule operator |(ScheduleTransformer a, Schedule b) => a.Apply(b); [Pure] public static Schedule operator &(Schedule a, Schedule b) => a.Intersect(b); [Pure] public static Schedule operator &(Schedule a, ScheduleTransformer b) => b.Apply(a); [Pure] public static Schedule operator &(ScheduleTransformer a, Schedule b) => a.Apply(b); [Pure] public static Schedule operator +(Schedule a, Schedule b) => a.Combine(b); /// /// Realise the underlying time-series of durations /// /// The underlying time-series of durations [Pure] public abstract Iterable Run(); /// /// Intersection of two schedules. As long as they are both running it returns the max duration /// /// Schedule `b` /// Max of schedule `this` and `b` to the length of the shortest schedule [Pure] public Schedule Intersect(Schedule b) => new SchIntersect(this, b); /// /// Union of two schedules. As long as any are running it returns the min duration of both or a or b /// /// Schedule `b` /// Min of schedule `this` and `b` or `this` or `b` to the length of the longest schedule [Pure] public Schedule Union(Schedule b) => new SchUnion(this, b); /// /// Interleave two schedules together /// /// Schedule `b` /// Returns the two schedules interleaved together [Pure] public Schedule Interleave(Schedule b) => new SchInterleave(this, b); /// /// Append two schedules together /// /// Schedule `b` /// Returns the two schedules appended [Pure] public Schedule Combine(Schedule b) => new SchCombine(this, b); /// /// Take `amount` durations from the `Schedule` /// /// Schedule to take from /// Amount ot take /// Schedule with `amount` or less durations [Pure] public Schedule Take(int amount) => new SchTake(this, amount); /// /// Skip `amount` durations from the `Schedule` /// /// Schedule to skip durations from /// Amount ot skip /// Schedule with `amount` durations skipped [Pure] public Schedule Skip(int amount) => new SchSkip(this, amount); /// /// Take all but the first duration from the schedule /// [Pure] public Schedule Tail => new SchTail(this); /// /// Prepend a duration in-front of the rest of the scheduled durations /// /// Duration to prepend /// Schedule with the duration prepended [Pure] public Schedule Prepend(Duration value) => new SchCons(value, this); /// /// Prepend a zero duration in-front of the rest of the scheduled durations /// /// Duration to prepend /// Schedule with the duration prepended [Pure] public Schedule PrependZero => Prepend(Duration.Zero); /// /// Functor map operation for Schedule /// /// Mapping function /// Mapped schedule [Pure] public Schedule Map(Func f) => new SchMap(this, f); /// /// Functor map operation for Schedule /// /// Mapping function /// Mapped schedule [Pure] public Schedule Map(Func f) => new SchMapIndex(this, f); /// /// Filter operation for Schedule /// /// predicate /// Filtered schedule [Pure] public Schedule Filter(Func pred) => new SchFilter(this, pred); /// /// Filter operation for Schedule /// /// predicate /// Filtered schedule [Pure] public Schedule Where(Func pred) => new SchFilter(this, pred); /// /// Functor map operation for Schedule /// /// Mapping function /// Mapped schedule [Pure] public Schedule Select(Func f) => new SchMap(this, f); /// /// Functor map operation for Schedule /// /// Mapping function /// Mapped schedule [Pure] public Schedule Select(Func f) => new SchMapIndex(this, f); /// /// Monad bind operation for Schedule /// /// Bind function /// Chained schedule [Pure] public Schedule Bind(Func f) => new SchBind(this, f); /// /// Monad bind operation for Schedule /// /// Bind function /// Chained schedule [Pure] public Schedule SelectMany(Func f) => new SchBind(this, f); /// /// Monad bind and project operation for Schedule /// /// Schedule /// Bind function /// Project function /// Chained schedule [Pure] public Schedule SelectMany(Func bind, Func project) => new SchBind2(this, bind, project); } ================================================ FILE: LanguageExt.Core/Effects/Schedule/ScheduleTransformer.cs ================================================ using System; namespace LanguageExt; /// /// Transforms a schedule into another schedule /// public readonly struct ScheduleTransformer { readonly Func Map; /// /// Ctor /// /// Mapping functor internal ScheduleTransformer(Func map) => Map = map; /// /// Apply a schedule to the transformer /// /// `Schedule` to run through the transformer /// `Schedule` that has been run through the transformer public Schedule Apply(Schedule schedule) => Map?.Invoke(schedule) ?? schedule; /// /// Compose the two transformers into one /// /// First transformer to run in the composition /// Second transformer to run in the composition /// composition of the 2 transformers public static ScheduleTransformer operator +(ScheduleTransformer f, ScheduleTransformer g) => new(x => g.Apply(f.Apply(x))); public static implicit operator Schedule(ScheduleTransformer t) => Schedule.Forever | t; } ================================================ FILE: LanguageExt.Core/Effects/Schedule/SingletonRandom.cs ================================================ using System; using static LanguageExt.Prelude; namespace LanguageExt; /// /// Singleton source of randomness /// internal static class SingletonRandom { static readonly Random Random = new(); static readonly Func Provider = memoUnsafe((int seed) => new Random(seed)); /// /// Returns a random floating-point number that is greater than or equal to `0.0`, and less than `1.0` /// internal static double NextDouble(Option seed = default) => seed.IsSome ? Provider(seed.Value).NextDouble() : Random.NextDouble(); /// /// Returns a random floating-point number that is greater than or equal to `a` and less than `b` /// internal static double Uniform(double a, double b, Option seed = default) { if (a.Equals(b)) return a; return a + (b - a) * NextDouble(seed); } } ================================================ FILE: LanguageExt.Core/Exceptions/Exceptions.cs ================================================ using System; namespace LanguageExt; /// /// Some T not initialised /// [Serializable] public class SomeNotInitialisedException : Exception { /// /// Ctor /// public SomeNotInitialisedException(Type type) : base($"Unitialised Some<{type.Name}>.") { } } /// /// Value is none /// [Serializable] public class ValueIsNoneException : Exception { public static readonly ValueIsNoneException Default = new ValueIsNoneException(); /// /// Ctor /// public ValueIsNoneException() : base("Value is none.") { } /// /// Ctor /// public ValueIsNoneException(string message) : base(message) { } /// /// Ctor /// public ValueIsNoneException(string message, Exception innerException) : base(message, innerException) { } } /// /// Value is null /// [Serializable] public class ValueIsNullException : Exception { /// /// Ctor /// public ValueIsNullException() : base("Value is null.") { } /// /// Ctor /// public ValueIsNullException(string message) : base(message) { } /// /// Ctor /// public ValueIsNullException(string message, Exception innerException) : base(message, innerException) { } } /// /// Result is null /// [Serializable] public class ResultIsNullException : Exception { /// /// Ctor /// public ResultIsNullException() : base("Result is null.") { } /// /// Ctor /// public ResultIsNullException(string message) : base(message) { } /// /// Ctor /// public ResultIsNullException(string message, Exception innerException) : base(message, innerException) { } } /// /// Option T is none /// [Serializable] public class OptionIsNoneException : Exception { /// /// Ctor /// public OptionIsNoneException() : base("Option isn't set.") { } /// /// Ctor /// public OptionIsNoneException(string message) : base(message) { } /// /// Ctor /// public OptionIsNoneException(string message, Exception innerException) : base(message, innerException) { } } /// /// Either is not right /// [Serializable] public class EitherIsNotRightException : Exception { /// /// Ctor /// public EitherIsNotRightException() : base("Either is not right.") { } /// /// Ctor /// public EitherIsNotRightException(string message) : base(message) { } /// /// Ctor /// public EitherIsNotRightException(string message, Exception innerException) : base(message, innerException) { } } /// /// Either is not left /// [Serializable] public class EitherIsNotLeftException : Exception { /// /// Ctor /// public EitherIsNotLeftException() : base("Either is not left.") { } /// /// Ctor /// public EitherIsNotLeftException(string message) : base(message) { } /// /// Ctor /// public EitherIsNotLeftException(string message, Exception innerException) : base(message, innerException) { } } [Serializable] public class NotAppendableException : Exception { public NotAppendableException(Type t) : base($"Type '{t.Name}' not appendable: It's neither a CLR numeric-type, a string nor derived from IAppendable") { } } [Serializable] public class NotSubtractableException : Exception { public NotSubtractableException(Type t) : base($"Type '{t.Name}' not subtractable: It's neither a CLR numeric-type, nor derived from ISubtractable") { } } [Serializable] public class NotMultiplicableException : Exception { public NotMultiplicableException(Type t) : base($"Type '{t.Name}' not multiplicable: It's neither a CLR numeric-type, nor derived from IMultiplicable") { } } [Serializable] public class NotDivisibleException : Exception { public NotDivisibleException(Type t) : base($"Type '{t.Name}' not divisible: It's neither a CLR numeric-type, nor derived from IDivisible") { } } [Serializable] public class RefValidationFailedException : Exception { public RefValidationFailedException() : base("Ref validation failed") { } } [Serializable] public class DeadlockException : Exception { public DeadlockException() : base("Deadlock occured during atomic update") { } } ================================================ FILE: LanguageExt.Core/Extensions/ActionObservable.cs ================================================ using System; namespace LanguageExt; /// /// Executes an action post-subscription. This is useful when the action is /// going to publish to the observable. A kind of request/response. /// Use the IObservable extension method: PostSubscribe(() => ...) /// public class ActionObservable( Action PostSubscribeAction, IObservable SwitchTo) : IObservable { public IDisposable Subscribe(IObserver observer) { var subs = SwitchTo.Subscribe(observer); PostSubscribeAction(); return subs; } } ================================================ FILE: LanguageExt.Core/Extensions/Compose.cs ================================================ using System; using System.Diagnostics.Contracts; namespace LanguageExt; public static class ComposeExtensions { /// /// Function back composition /// /// v => g(f(v)) [Pure] public static Func BackCompose(this Func g, Func f) => v => g(f(v)); /// /// Function back composition /// /// () => g(f()) [Pure] public static Func BackCompose(this Func g, Func f) => () => g(f()); /// /// Action back composition /// /// v => g(f(v)) [Pure] public static Action BackCompose(this Action g, Func f) => v => g(f(v)); /// /// Action back composition /// /// () => g(f()) [Pure] public static Action BackCompose(this Action g, Func f) => () => g(f()); /// /// Function composition /// /// v => g(f(v)) [Pure] public static Func Compose(this Func f, Func g) => v => g(f(v)); /// /// Function composition /// /// () => g(f()) [Pure] public static Func Compose(this Func f, Func g) => () => g(f()); /// /// Action composition /// /// v => g(f(v)) [Pure] public static Action Compose(this Func f, Action g) => v => g(f(v)); /// /// Action composition /// /// () => g(f()) [Pure] public static Action Compose(this Func f, Action g) => () => g(f()); } ================================================ FILE: LanguageExt.Core/Extensions/FuncExtensions.cs ================================================ using System; using System.Diagnostics.Contracts; namespace LanguageExt; public static class FuncExtensions { /// /// Flip the arguments in a two argument function /// [Pure] public static Func Flip(this Func f) => (b, a) => f(a, b); /// /// Flip the arguments in a two argument function /// [Pure] public static Func> Flip(this Func> f) => b => a => f(a)(b); extension(Func lhs) { /// /// Function composition /// /// First function /// Second function /// Composed function public static Func operator >>(Func f, Func g) => x => g(f(x)); } extension(Func lhs) { /// /// Function composition /// /// First function /// Second function /// Composed function public static Func operator <<(Func g, Func f) => x => g(f(x)); } extension(Func lhs) { /// /// Function composition /// /// Function /// Input /// Result of invoking the function public static Pure operator >> (Func f, Pure x) => new(f(x.Value)); } } ================================================ FILE: LanguageExt.Core/Extensions/ObjectExt.cs ================================================ using System; using System.Diagnostics.Contracts; using System.Reflection; using System.Runtime.CompilerServices; using LanguageExt.ClassInstances; namespace LanguageExt; #nullable disable //TODO: THIS IS TO RETAIN THE ORIGINAL CAPABILITY --- LEAVE IT DISABLED UNTIL YOU CAN PROVE IT WORKS OTHERWISE public static class ObjectExt { /// /// Returns true if the value is null, and does so without /// boxing of any value-types. Value-types will always /// return false. /// /// /// int x = 0; /// string y = null; /// /// x.IsNull() // false /// y.IsNull() // true /// /// True if the value is null, and does so without /// boxing of any value-types. Value-types will always /// return false. [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsNull(this A value) => Check.IsNull(value); } internal static class Check { static readonly bool IsReferenceType; static readonly bool IsNullable; static Check() { IsNullable = Nullable.GetUnderlyingType(typeof(A)) != null; IsReferenceType = !typeof(A).GetTypeInfo().IsValueType; } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static bool IsDefault(A value) => EqDefault.Equals(value, default); [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static bool IsNull(A value) => (IsReferenceType && ReferenceEquals(value, null)) || (IsNullable && value.Equals(default)); } ================================================ FILE: LanguageExt.Core/Extensions/ObservableExt.cs ================================================ using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics.Contracts; using System.Runtime.CompilerServices; using System.Threading; using LanguageExt.Common; namespace LanguageExt; /// /// Observable extensions /// public static class ObservableExt { /// /// Executes an action post-subscription. This is useful when the action is /// going to publish to the observable. A kind of request/response. /// [Pure] public static IObservable PostSubscribe( this IObservable self, Action action) => new ActionObservable(action, self); /// /// Executes an action post-subscription. This is useful when the action is /// going to publish to the observable. A kind of request/response. /// [Pure] public static IObservable PostSubscribe( this IObservable self, Func action) => new ActionObservable(() => action(), self); /// /// Convert an `IObservable` to an `IAsyncEnumerable` /// public static IAsyncEnumerable ToAsyncEnumerable( this IObservable observable, CancellationToken token) => Observe.Run(observable, token); class Observe : IObserver { readonly AutoResetEvent wait; readonly ConcurrentQueue> queue; Observe(AutoResetEvent wait, ConcurrentQueue> queue) { this.wait = wait; this.queue = queue; } public static async IAsyncEnumerable Run( IObservable observable, [EnumeratorCancellation] CancellationToken token) { using var wait = new AutoResetEvent(false); var queue = new ConcurrentQueue>(); observable.Subscribe(new Observe(wait, queue)); while (true) { await wait.WaitOneAsync(token).ConfigureAwait(false); while (queue.TryDequeue(out var item)) { if (item.IsFail) { if (item.FailValue == Errors.None) yield break; if (item.FailValue == Errors.Cancelled) throw new OperationCanceledException(); item.FailValue.Throw(); yield break; } else { yield return item.SuccValue; } } } } public void OnCompleted() { queue.Enqueue(Errors.None); wait.Set(); } public void OnError(Exception error) { queue.Enqueue(Error.New(error)); wait.Set(); } public void OnNext(A value) { queue.Enqueue(value); wait.Set(); } } } ================================================ FILE: LanguageExt.Core/Extensions/Query.cs ================================================ using System; using System.Linq; using System.Collections.Generic; using System.Linq.Expressions; using static LanguageExt.Prelude; namespace LanguageExt; public static class Query { public static T head(IQueryable list) => list.First(); public static Option headOrNone(IQueryable list) => list.FirstOrDefault() ?? Option.None; public static Either headOrLeft(IQueryable list, L left) => list.FirstOrDefault() ?? Either.Left(left); public static IQueryable tail(IQueryable list) => list.Skip(1); public static IQueryable map(IQueryable list, Expression> map) => list.Select(map); public static IQueryable map(IQueryable list, Expression> map) { var paramT = Expression.Parameter(typeof(T), "t"); var paramI = Expression.Parameter(typeof(int), "i"); return zip(list, Range(0, Int32.MaxValue), Expression.Lambda>( Expression.Invoke(map, paramI, paramT), paramT, paramI )); } public static IQueryable filter(IQueryable list, Expression> predicate) => list.Where(predicate); public static IQueryable choose(IQueryable list, Expression>> selector) => map(filter(map(list, selector), t => t.IsSome), t => t.Value!); public static IQueryable choose(IQueryable list, Expression>> selector) => map(filter(map(list, selector), t => t.IsSome), t => t.Value!); public static IQueryable collect(IQueryable list, Expression>> map) { var paramT = Expression.Parameter(typeof(T), "t"); return list.SelectMany( Expression.Lambda>>( Expression.Invoke(map, paramT), paramT )); } public static int sum(IQueryable list) => fold(list, 0, (x, s) => s + x); public static float sum(IQueryable list) => fold(list, 0.0f, (x, s) => s + x); public static double sum(IQueryable list) => fold(list, 0.0, (x, s) => s + x); public static decimal sum(IQueryable list) => fold(list, (decimal)0, (x, s) => s + x); public static IQueryable rev(IQueryable list) => list.Reverse(); public static IQueryable append(IQueryable lhs, IQueryable rhs) => lhs.Concat(rhs); public static S fold(IQueryable list, S state, Expression> folder) => list.Aggregate(state, folder); public static S foldBack(IQueryable list, S state, Expression> folder) => fold(rev(list), state, folder); public static T reduce(IQueryable list, Expression> reducer) => match(headOrNone(list), Some: x => fold(tail(list), x, reducer), None: () => failwith("Input list was empty") ); public static T reduceBack(IQueryable list, Expression> reducer) => reduce(rev(list), reducer); public static Option find(IQueryable list, Expression> pred) => headOrNone(filter(list, pred)); public static Lst freeze(IQueryable list) => toList(list); public static IQueryable zip(IQueryable list, IEnumerable other, Expression> zipper) => Queryable.Zip(list, other, zipper); public static int length(IQueryable list) => list.Count(); public static bool forall(IQueryable list, Expression> pred) => list.All(pred); public static IQueryable distinct(IQueryable list) => Queryable.Distinct(list); public static IQueryable take(IQueryable list, int count) => list.Take(count); public static IQueryable takeWhile(IQueryable list, Expression> pred) => list.TakeWhile(pred); public static IQueryable takeWhile(IQueryable list, Expression> pred) => list.TakeWhile(pred); public static bool exists(IQueryable list, Expression> pred) => list.Any(pred); } public static class QueryExtensions { public static T Head(this IQueryable list) => Query.head(list); public static Option HeadOrNone(this IQueryable list) => Query.headOrNone(list); public static Either HeadOrLeft(this IQueryable list, S left) => Query.headOrLeft(list, left); public static IQueryable Tail(this IQueryable list) => Query.tail(list); public static IQueryable Map(this IQueryable list, Expression> map) => Query.map(list, map); public static IQueryable Map(this IQueryable list, Expression> map) => Query.map(list, map); public static IQueryable Filter(this IQueryable list, Expression> predicate) => Query.filter(list, predicate); public static IQueryable Choose(this IQueryable list, Expression>> selector) => Query.choose(list, selector); public static IQueryable Choose(this IQueryable list, Expression>> selector) => Query.choose(list, selector); public static IQueryable Collect(this IQueryable list, Expression>> map) => Query.collect(list, map); public static IQueryable Rev(this IQueryable list) => Query.rev(list); public static IQueryable Append(this IQueryable lhs, IQueryable rhs) => Query.append(lhs, rhs); public static S Fold(this IQueryable list, S state, Expression> folder) => Query.fold(list, state, folder); public static S FoldBack(this IQueryable list, S state, Expression> folder) => Query.foldBack(list, state, folder); public static T Reduce(this IQueryable list, Expression> reducer) => Query.reduce(list, reducer); public static T ReduceBack(this IQueryable list, Expression> reducer) => Query.reduceBack(list, reducer); public static Lst Freeze(this IQueryable list) => Query.freeze(list); public static IQueryable Zip(this IQueryable list, IEnumerable other, Expression> zipper) => Query.zip(list, other, zipper); public static int Length(this IQueryable list) => Query.length(list); public static bool ForAll(this IQueryable list, Expression> pred) => Query.forall(list, pred); public static IQueryable Distinct(this IQueryable list) => Query.distinct(list); public static bool Exists(this IQueryable list, Expression> pred) => Query.exists(list, pred); } ================================================ FILE: LanguageExt.Core/Extensions/TryOutExt.cs ================================================ using System.Collections.Generic; using static LanguageExt.Prelude; using System.Diagnostics.Contracts; namespace LanguageExt; public static class OutExtensions { /// /// Get a value out of a dictionary as Some, otherwise None. /// /// Key type /// Value type /// Dictionary /// Key /// OptionT filled Some(value) or None [Pure] public static Option TryGetValue(this IDictionary self, K Key) => self.TryGetValue(Key, out var value) ? Optional(value) : None; /// /// Get a value out of a dictionary as Some, otherwise None. /// /// Key type /// Value type /// Dictionary /// Key /// OptionT filled Some(value) or None [Pure] public static Option TryGetValue(this IReadOnlyDictionary self, K ReadOnlyKey) => self.TryGetValue(ReadOnlyKey, out var value) ? Optional(value) : None; } ================================================ FILE: LanguageExt.Core/Extensions/UnsafeValueAccess.cs ================================================ using System.Buffers; using System.Runtime.CompilerServices; namespace LanguageExt.UnsafeValueAccess; public static class UnsafeValueAccessExtensions { public static A? ValueUnsafe(this Option option) => option.IsSome ? option.Value : default; public static A Value(this Option option) where A : struct => option.IsSome ? option.Value : default; public static R Value(this Either either) where R : struct => either.IsRight ? either.RightValue : default; public static R? ValueUnsafe(this Either either) => either.IsRight ? either.RightValue : default; /// /// This creates a Seq from an Array without any copying of data, so it's super fast /// However because the input array is mutable it weakens the guarantees of the immutable Seq, so this is not /// advised unless you know what you're doing. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Seq ToSeqUnsafe(this A[] value) => Seq.FromArray(value); /// /// This creates a Seq from an Array without any copying of data, so it's super fast /// However because the input array is mutable it weakens the guarantees of the immutable Seq, so this is not /// advised unless you know what you're doing. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Seq ToSeqUnsafe(this A[] value, int length) => Seq.FromArray(value, length); /// /// This creates a Seq from an Array without any copying of data, so it's super fast /// However because the input array is mutable it weakens the guarantees of the immutable Seq, so this is not /// advised unless you know what you're doing. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static SeqLoan ToSeqLoanUnsafe(this A[] value, ArrayPool pool) => new (value, pool, 0, value.Length); /// /// This creates a Seq from an Array without any copying of data, so it's super fast /// However because the input array is mutable it weakens the guarantees of the immutable Seq, so this is not /// advised unless you know what you're doing. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static SeqLoan ToSeqLoanUnsafe(this A[] value, int length, ArrayPool pool) => new (value, pool, 0, length); } ================================================ FILE: LanguageExt.Core/Guard.cs ================================================ using System; using static LanguageExt.Prelude; namespace LanguageExt; /// /// Used by various error-producing monads to have a contextual `where` /// /// /// See `Prelude.guard(...)` /// public readonly struct Guard { public readonly bool Flag; readonly Func onFalse; internal Guard(bool flag, Func onFalse) => (Flag, this.onFalse) = (flag, onFalse ?? throw new ArgumentNullException(nameof(onFalse))); internal Guard(bool flag, E onFalse) { if (isnull(onFalse)) throw new ArgumentNullException(nameof(onFalse)); (Flag, this.onFalse) = (flag, () => onFalse); } public Guard Cast() => new (Flag, OnFalse); public Func OnFalse => onFalse ?? throw new InvalidOperationException( "Guard isn't initialised. It was probably created via new Guard() or default(Guard), and so it has no OnFalse handler"); public Guard SelectMany(Func> bind, Func project) => Flag ? bind(default!).Cast() : Cast(); public Guard Select(Func _) => Cast(); } ================================================ FILE: LanguageExt.Core/Immutable Collections/Arr/Arr.Module.cs ================================================ using System; using System.Linq; using System.Collections.Generic; using static LanguageExt.Prelude; using System.Diagnostics.Contracts; namespace LanguageExt; public partial class Arr { /// /// Create an empty array /// [Pure] public static Arr empty() => Arr.Empty; /// /// Create a new empty array /// /// Lst T [Pure] public static Arr create() => Arr.Empty; /// /// Create a singleton array /// /// Single value /// Collection with a single item in it [Pure] public static Arr singleton(A value) => [value]; /// /// Create an array from a initial set of items /// /// Items /// Lst T [Pure] public static Arr create(ReadOnlySpan items) => items.IsEmpty ? empty() : new Arr(items); /// /// Create an array from a initial set of items /// /// Items /// Lst T [Pure] public static Arr create(params T[] items) => new (items); /// /// Create an array from an initial set of items /// /// Items /// Lst T [Pure] public static Arr createRange(IEnumerable items) => new (items); /// /// Add an item to the array /// /// Array /// Item to add /// A new Lst T [Pure] public static Arr add(Arr array, T value) => array.Add(value); /// /// Add a range of items to the array /// /// Array /// Items to add /// A new Lst T [Pure] public static Arr addRange(Arr array, IEnumerable value) => array.AddRange(value); /// /// Remove an item from the array /// /// Array /// value to remove /// A new Lst T [Pure] public static Arr remove(Arr array, T value) => array.Remove(value); /// /// Remove an item at a specified index in the array /// /// Array /// Index of item to remove /// A new Lst T [Pure] public static Arr removeAt(Arr array, int index) => array.RemoveAt(index); /// /// Reverses the array (Reverse in LINQ) /// /// Array item type /// Array to reverse /// Reversed list [Pure] public static T[] rev(T[] array) { var l = array.Length; var n = new T[l]; var i = 0; var j = l - 1; for (; i < l; i++, j--) { n[i] = array[j]; } return n; } /// /// Reverses the array (Reverse in LINQ) /// /// Array item type /// Array to reverse /// Reversed list [Pure] public static Arr rev(Arr array) => array.Reverse(); /// /// Monadic join /// [Pure] public static A[] flatten(A[][] ma) => ma.Bind(identity).ToArray(); /// /// Monadic join /// [Pure] public static Arr flatten(Arr> ma) => ma.Bind(identity); } ================================================ FILE: LanguageExt.Core/Immutable Collections/Arr/Arr.cs ================================================ using System; using System.Linq; using System.Collections; using System.Collections.Generic; using System.Diagnostics.Contracts; using System.Numerics; using static LanguageExt.Prelude; using LanguageExt.Traits; using LanguageExt.ClassInstances; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; namespace LanguageExt; /// /// Immutable array /// Native array O(1) read performance. Modifications require copying of the entire /// array to generate the newly mutated version. This is will be very expensive /// for large arrays. /// /// Value type [Serializable] [CollectionBuilder(typeof(Arr), nameof(Arr.create))] public readonly struct Arr : IReadOnlyList, IEquatable>, IComparable>, Monoid>, IComparisonOperators, Arr, bool>, IAdditionOperators, Arr, Arr>, IAdditiveIdentity, Arr>, TokenStream, A>, IComparable, K { /// /// Empty array /// public static Arr Empty { get; } = new (System.Array.Empty()); readonly A[]? value; readonly int start; readonly int length; readonly Atom? hashCode; A[] Value { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => value ?? Empty.Value; } /// /// Ctor /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public Arr(IEnumerable initial) { hashCode = Atom(0); value = initial.ToArray(); start = 0; length = value.Length; } /// /// Ctor /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public Arr(ReadOnlySpan initial) { hashCode = Atom(0); value = initial.ToArray(); start = 0; length = value.Length; } /// /// Ctor /// [MethodImpl(MethodImplOptions.AggressiveInlining)] internal Arr(A[] value) { hashCode = Atom(0); this.value = value; start = 0; length = value.Length; } /// /// Ctor /// [MethodImpl(MethodImplOptions.AggressiveInlining)] internal Arr(A[] value, int start, int length) { if(start < 0) throw new ArgumentOutOfRangeException(nameof(start)); if(start + length > value.Length) throw new ArgumentOutOfRangeException(nameof(length)); hashCode = Atom(0); this.value = value; this.start = start; this.length = length; } /// /// Create a readonly span of this array. This doesn't do any copying, so it is very fast. /// /// Offset from the beginning of the array /// The number of items to take. This will be clamped /// to the maximum number of items available /// A read-only span of values /// Thrown If the start index is outside the range of the array [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public ReadOnlySpan AsSpan() => new (Value, start, length); /// /// Create a readonly sub-span of this array. This doesn't do any copying, so is very fast, but be aware that any /// items outside the splice are still active. /// /// Offset from the beginning of the array /// A read-only span of values /// Thrown If the start index is outside the range of the array [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public ReadOnlySpan AsSpan(int start) { if (start < 0 || start >= length) throw new IndexOutOfRangeException(nameof(start)); var t = Math.Max(0, length - start); return new(Value, this.start + start, t); } /// /// Create a readonly sub-span of this array. This doesn't do any copying, so is very fast, but be aware that any /// items outside the splice are still active. /// /// Offset from the beginning of the array /// The number of items to take. This will be clamped to the maximum number of items available /// A read-only span of values /// Thrown If the start index is outside the range of the array [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public ReadOnlySpan AsSpan(int start, int count) { if (start < 0 || start >= length) throw new IndexOutOfRangeException(nameof(start)); var t = Math.Max(0, Math.Min(count, length - start)); return new(Value, this.start + start, t); } /// /// Create a subarray of this array. This doesn't do any copying, so is very fast, but be aware that any items /// outside the splice are still active. /// /// Offset from the beginning of the array /// /// Thrown If the start index is outside the range of the array [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Arr Splice(int start) { var arr = Value; if (start < 0 || start >= length) throw new IndexOutOfRangeException(nameof(start)); var t = Math.Max(0, length - start); return new Arr(arr, this.start + start, t); } /// /// Create a subarray of this array. This doesn't do any copying, so is very fast, but be aware that any items /// outside the splice are still active. /// /// Offset from the beginning of the array /// The number of items to take. This will be clamped to the maximum number of items available /// /// Thrown If the start index is outside the range of the array [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Arr Splice(int start, int count) { var arr = Value; if (start < 0 || start >= length) throw new IndexOutOfRangeException(nameof(start)); var t = Math.Max(0, Math.Min(count, length - start)); return new Arr(arr, this.start + start, t); } [Pure] public Arr Tail => IsEmpty ? this : Splice(1, length - 1); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator Arr(A[] xs) => new (xs); /// /// Head lens /// [Pure] public static Lens, A> head => Lens, A>.New( Get: la => la.Count == 0 ? throw new IndexOutOfRangeException() : la[0], Set: a => la => la.Count == 0 ? throw new IndexOutOfRangeException() : la.SetItem(0, a)); /// /// Head or none lens /// [Pure] public static Lens, Option> headOrNone => Lens, Option>.New( Get: la => la.Count == 0 ? None : Some(la[0]), Set: a => la => la.Count == 0 || a.IsNone ? la : la.SetItem(0, a.Value!)); /// /// Last lens /// [Pure] public static Lens, A> last => Lens, A>.New( Get: la => la.Count == 0 ? throw new IndexOutOfRangeException() : la[^1], Set: a => la => la.Count == 0 ? throw new IndexOutOfRangeException() : la.SetItem(la.Count - 1, a)); /// /// Last or none lens /// [Pure] public static Lens, Option> lastOrNone => Lens, Option>.New( Get: la => la.Count == 0 ? None : Some(la[^1]), Set: a => la => la.Count == 0 || a.IsNone ? la : la.SetItem(la.Count - 1, a.Value!)); /// /// Item at index lens /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Lens, A> item(int index) => Lens, A>.New( Get: la => la.Count == 0 ? throw new IndexOutOfRangeException() : la[index], Set: a => la => la.Count == 0 ? throw new IndexOutOfRangeException() : la.SetItem(index, a)); /// /// Item or none at index lens /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Lens, Option> itemOrNone(int index) => Lens, Option>.New( Get: la => la.Count < index - 1 ? None : Some(la[index]), Set: a => la => la.Count < index - 1 || a.IsSome ? la : la.SetItem(index, a.Value!)); /// /// Lens map /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Lens, Arr> map(Lens lens) => Lens, Arr>.New( Get: la => la.Map(lens.Get), Set: lb => la => la.Zip(lb).Map(ab => lens.Set(ab.Item2, ab.Item1)).ToArr()); /// /// Index accessor /// [Pure] public A this[Index index] { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => index.IsFromEnd switch { false => Value[start + index.Value], true => Value[start + length - index.Value - 1] }; } /// /// Reference version for use in pattern-matching /// /// /// /// Empty collection = result is null /// Singleton collection = result is A /// More = result is (A, Seq〈A〉) -- head and tail /// /// Example: /// /// var res = arr.Case switch /// { /// /// A value => ..., /// (var x, var xs) => ..., /// _ => ... /// } /// /// [Pure] public object? Case => IsEmpty ? null : Count == 1 ? this[0] : toSeq(this).Case; /// /// Is the stack empty /// [Pure] public bool IsEmpty { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => value == null || length == 0; } /// /// Number of items in the stack /// [Pure] public int Count { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => value == null ? 0 : length; } /// /// Alias of Count /// [Pure] public int Length { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => value == null ? 0 : length; } [Pure] int IReadOnlyCollection.Count { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => Count; } [Pure] A IReadOnlyList.this[int index] { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => Value[start + index]; } /// /// Add an item to the end of the array /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Arr Add(A valueToAdd) { var self = Value; return self.Length == 0 ? new Arr([valueToAdd]) : Insert(self.Length, valueToAdd); } /// /// Add a range of items to the end of the array /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Arr AddRange(IEnumerable items) => InsertRange(Count, items); /// /// Clear the array /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Arr Clear() => Empty; /// /// Get enumerator /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Enumerator GetEnumerator() => new (this); public struct Enumerator { readonly A[] arr; int index; int end; internal Enumerator(in Arr arr) { this.arr = arr.Value; index = arr.start -1; end = arr.start + arr.length; } public readonly A Current => arr[index]; public bool MoveNext() => ++index < end; } /// /// Find the index of an item /// [Pure] public int IndexOf(A item, int index = 0, int count = -1, IEqualityComparer? equalityComparer = null) { var eq = equalityComparer ?? EqualityComparer.Default; for (; index >= 0 && index < length && count != 0; index++, count--) { if (eq.Equals(item, this[index])) return index; } return -1; } /// /// Find the index of an item /// [Pure] public int LastIndexOf(A item, int index = -1, int count = -1, IEqualityComparer? equalityComparer = null) { var eq = equalityComparer ?? EqualityComparer.Default; var arr = Value; index = index < 0 ? length - 1 : index; for (; index >= 0 && index < length && count != 0; index--, count--) { if (eq.Equals(item, this[index])) return index; } return -1; } /// /// Find the index of an item /// [Pure] public int IndexOf(A item, int index = 0, int count = -1) where EQ : Eq { var arr = Value; for (; index < length && count != 0; index++, count--) { if (EQ.Equals(item, this[index])) return index; } return -1; } /// /// Find the index of an item /// [Pure] public int LastIndexOf(A item, int index = -1, int count = -1) where EQ : Eq { var arr = Value; index = index < 0 ? length - 1 : index; for (; index >= 0 && index < length && count != 0; index--, count--) { if (EQ.Equals(item, this[index])) return index; } return -1; } /// /// Insert value at specified index /// [Pure] public Arr Insert(int index, A valueToInsert) { var arr = Value; if (index < 0 || index > Length) throw new IndexOutOfRangeException(nameof(index)); if (length == 0) { return new Arr([valueToInsert]); } var xs = new A[length + 1]; xs[index] = valueToInsert; if (index != 0) { System.Array.Copy(arr, start, xs, 0, index); } if (index != arr.Length) { System.Array.Copy(arr, start + index, xs, index + 1, length - index); } return new Arr(xs); } /// /// Insert range of values at specified index /// [Pure] public Arr InsertRange(int index, IEnumerable items) { var arr = Value; if (index < 0 || index > Length) throw new IndexOutOfRangeException(nameof(index)); if (length == 0) { return new Arr(items); } var insertArr = items.ToArray(); var count = insertArr.Length; if (count == 0) { return this; } var newArray = new A[length + count]; if (index != 0) { System.Array.Copy(arr, start, newArray, 0, index); } if (index != arr.Length) { System.Array.Copy(arr, start + index, newArray, index + count, length - index); } insertArr.CopyTo(newArray, index); return new Arr(newArray); } /// /// Remove an item from the array /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Arr Remove(A valueToRemove) => Remove>(valueToRemove); /// /// Remove an item from the array /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Arr Remove(A valueToRemove, IEqualityComparer equalityComparer) { var index = IndexOf(valueToRemove, 0, -1, equalityComparer); return index < 0 ? this : RemoveAt(index); } /// /// Remove an item from the array /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Arr Remove(A valueToRemove) where EQ : Eq { var index = IndexOf(valueToRemove); return index < 0 ? this : RemoveAt(index); } /// /// Remove all items that match a predicate /// [Pure] public Arr RemoveAll(Predicate pred) { if (IsEmpty) return this; List? removeIndices = null; for (var i = 0; i < Length; i++) { if (pred(this[i])) { if (removeIndices == null) { removeIndices = new List(); } removeIndices.Add(i); } } return removeIndices != null ? RemoveAtRange(removeIndices) : this; } [Pure] private Arr RemoveAtRange(List remove) { var arr = Value; if (remove.Count == 0) return this; var newArray = new A[length - remove.Count]; var copied = 0; var removed = 0; var lastIndexRemoved = -1; foreach (var item in remove) { var copyLength = lastIndexRemoved == -1 ? item : (item - lastIndexRemoved - 1); System.Array.Copy(arr, start + copied + removed, newArray, copied, copyLength); removed++; copied += copyLength; lastIndexRemoved = item; } System.Array.Copy(arr, start + copied + removed, newArray, copied, length - (copied + removed)); return new Arr(newArray); } /// /// Remove item at location /// /// /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Arr RemoveAt(int index) => RemoveRange(index, 1); /// /// Remove a range of items /// [Pure] public Arr RemoveRange(int index, int count) { var arr = Value; if (index < 0 || index > Length) throw new IndexOutOfRangeException(nameof(index)); if (!(count >= 0 && index + count <= Length)) throw new IndexOutOfRangeException(nameof(index)); if (count == 0) return this; var newArray = new A[length - count]; System.Array.Copy(arr, start, newArray, 0, index); System.Array.Copy(arr, start + index + count, newArray, index, length - index - count); return new Arr(newArray); } /// /// Set an item at the specified index /// [Pure] public Arr SetItem(int index, A valueToSet) { var arr = Value; if (index < 0 || index >= arr.Length) throw new IndexOutOfRangeException(nameof(index)); var newArray = new A[Length]; System.Array.Copy(arr, start, newArray, 0, length); newArray[index] = valueToSet; return new Arr(newArray); } [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] IEnumerator IEnumerable.GetEnumerator() => Value.GetEnumerator(); [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] IEnumerator IEnumerable.GetEnumerator() => // ReSharper disable once NotDisposedResourceIsReturned Value.AsEnumerable().GetEnumerator(); [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Iterable AsIterable() => Iterable.createRange(this); [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Seq ToSeq() => toSeq(this); /// /// Format the collection as `[a, b, c, ...]` /// The ellipsis is used for collections over 50 items /// To get a formatted string with all the items, use `ToFullString` /// or `ToFullArrayString`. /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public override string ToString() => CollectionFormat.ToShortArrayString(this, Count); /// /// Format the collection as `a, b, c, ...` /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public string ToFullString(string separator = ", ") => CollectionFormat.ToFullString(this, separator); /// /// Format the collection as `[a, b, c, ...]` /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public string ToFullArrayString(string separator = ", ") => CollectionFormat.ToFullArrayString(this, separator); [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Iterable Skip(int amount) { var s = start; var e = start + length; return Iterable.createRange(Go(s, e, Value)); static IEnumerable Go(int s, int e, A[] v) { for (var i = s; i < e; i++) { yield return v[i]; } } } //Value.Skip(start + amount).AsIterable(); /// /// Reverse the order of the items in the array /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Arr Reverse() { var l = Count; var n = new A[l]; var v = Value; var i = 0; var j = l - 1; for (; i < l; i++, j--) { n[i] = v[j + start]; } return new Arr(n); } /// /// Impure iteration of the bound values in the structure /// /// /// Returns the original unmodified structure /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public Arr Do(Action f) { this.Iter(f); return this; } /// /// Map /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Arr Map(Func map) { var arr = Value; var newArray = new B[length]; for (var i = 0; i < length; i++) { newArray[i] = map(arr[start + i]); } return new Arr(newArray); } /// /// Map each element of a structure to an action, evaluate these actions from /// left to right, and collect the results. /// /// /// Traversable structure /// Applicative functor trait /// Bound value (output) [Pure] public K> Traverse(Func> f) where F : Applicative => F.Map(x => x.As(), Traversable.traverse(f, this)); /// /// Map each element of a structure to an action, evaluate these actions from /// left to right, and collect the results. /// /// /// Traversable structure /// Monad trait /// Bound value (output) [Pure] public K> TraverseM(Func> f) where M : Monad => M.Map(x => x.As(), Traversable.traverseM(f, this)); /// /// Filter /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Arr Filter(Func pred) => RemoveAll(x => !pred(x)); [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Arr operator +(Arr lhs, A rhs) => lhs.Add(rhs); [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Arr operator +(A lhs, Arr rhs) => rhs.Insert(0, lhs); [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Arr operator +(Arr lhs, Arr rhs) => rhs.InsertRange(0, lhs); /// /// Choice operator /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Arr operator |(Arr x, K y) => x.Choose(y).As(); /// /// Choice operator /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Arr operator |(K x, Arr y) => x.Choose(y).As(); [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Arr Combine(Arr rhs) => rhs.InsertRange(0, this); [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public override bool Equals(object? obj) => obj is Arr @as && Equals(@as); /// /// Get the hash code /// Lazily (and once only) calculates the hash from the elements in the array /// Empty array hash == 0 /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public override int GetHashCode() { if (hashCode is null) return CalcHashCode(); var self = this; return hashCode == 0 ? hashCode.Swap(_ => self.CalcHashCode()) : hashCode; } int CalcHashCode() => FNV32.Hash, A>(Value, start, length); [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public int CompareTo(object? obj) => obj is Arr t ? CompareTo(t) : 1; [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(Arr other) { if (Count != other.Count) return false; var ia = GetEnumerator(); var ib = other.GetEnumerator(); while (ia.MoveNext() && ib.MoveNext()) { if (!EqDefault.Equals(ia.Current, ib.Current)) return false; } return true; } [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public int CompareTo(Arr other) { if (Count < other.Count) return -1; if (Count > other.Count) return 1; var ia = GetEnumerator(); var ib = other.GetEnumerator(); while (ia.MoveNext() && ib.MoveNext()) { var cmp = OrdDefault.Compare(ia.Current, ib.Current); if (cmp != 0) return cmp; } return 0; } [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(Arr lhs, Arr rhs) => lhs.Equals(rhs); [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(Arr lhs, Arr rhs) => !(lhs == rhs); [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Arr Bind(Func> f) { var res = new List(); foreach (var t in this) { foreach (var u in f(t)) { res.Add(u); } } return new Arr(res); } /// /// Implicit conversion from an untyped empty list /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator Arr(SeqEmpty _) => Empty; public static bool operator >(Arr left, Arr right) => left.CompareTo(right) > 0; public static bool operator >=(Arr left, Arr right) => left.CompareTo(right) >= 0; public static bool operator <(Arr left, Arr right) => left.CompareTo(right) < 0; public static bool operator <=(Arr left, Arr right) => left.CompareTo(right) <= 0; public static Arr AdditiveIdentity => Empty; static bool TokenStream, A>.IsTab(A token) => false; static bool TokenStream, A>.IsNewline(A token) => false; static ReadOnlySpan TokenStream, A>.TokenToString(A token) => (token?.ToString() ?? "").AsSpan() ; static Arr TokenStream, A>.TokenToChunk(in A token) => Arr.singleton(token); static Arr TokenStream, A>.TokensToChunk(in ReadOnlySpan token) => [..token]; static ReadOnlySpan TokenStream, A>.ChunkToTokens(in Arr tokens) => tokens.As().AsSpan(); static int TokenStream, A>.ChunkLength(in Arr tokens) => tokens.As().Count; static bool TokenStream, A>.Take1(in Arr stream, out A head, out Arr tail) { var s = stream.As(); if (s.IsEmpty) { head = default!; tail = stream; return false; } else { head = s[0]; tail = s.Tail; return true; } } static bool TokenStream, A>.Take(int amount, in Arr stream, out Arr head, out Arr tail) { // If the requested length `amount` is 0 (or less), `false` should // not be returned, instead `true` and `(out Empty, out stream)` should be returned. if (amount <= 0) { head = Empty; tail = stream; return true; } // If the requested length is greater than 0 and the stream is // empty, `false` should be returned indicating end-of-input. if (stream.Length <= 0) { head = Empty; tail = stream; return false; } // In other cases, take chunk of length `amount` (or shorter if the // stream is not long enough) from the input stream and return the // chunk along with the rest of the stream. amount = Math.Min(amount, stream.Length); var start = stream.start; var value = stream.Value; head = new Arr(value, start, amount); tail = new Arr(value, start + amount, stream.Length - amount); return true; } static void TokenStream, A>.TakeWhile(Func predicate, in Arr stream, out Arr head, out Arr tail) { var s = stream.As(); var array = s.Value; var start = s.start; var length = s.length; var current = start; var offset = 0; while(current < length) { if (predicate(array[current])) { current++; offset++; } else { head = new Arr(array, start, current - start); tail = new Arr(array, current, length - offset); return; } } head = stream; tail = Empty; } } ================================================ FILE: LanguageExt.Core/Immutable Collections/Arr/Extensions/Arr.Extensions.MapApply.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public static partial class ArrExtensions { /// /// Functor map operation /// /// /// Unwraps the value within the functor, passes it to the map function `f` provided, and /// then takes the mapped value and wraps it back up into a new functor. /// /// Functor to map /// Mapping function /// Mapped functor public static Arr Map(this Func f, K ma) => Functor.map(f, ma).As(); /// /// Functor map operation /// /// /// Unwraps the value within the functor, passes it to the map function `f` provided, and /// then takes the mapped value and wraps it back up into a new functor. /// /// Functor to map /// Mapping function /// Mapped functor public static Arr Map(this Func f, Arr ma) => Functor.map(f, ma).As(); /// /// Applicative action: runs the first applicative, ignores the result, and returns the second applicative /// public static Arr Action(this Arr ma, K mb) => Applicative.action(ma, mb).As(); /// /// Applicative action: runs the first applicative, ignores the result, and returns the second applicative /// public static Arr Action(this K ma, K mb) => Applicative.action(ma, mb).As(); /// /// Applicative functor apply operation /// /// /// Unwraps the value within the `ma` applicative-functor, passes it to the unwrapped function(s) within `mf`, and /// then takes the resulting value and wraps it back up into a new applicative-functor. /// /// Value(s) applicative functor /// Mapping function(s) /// Mapped applicative functor public static Arr Apply(this Arr> mf, K ma) => Applicative.apply(mf, ma).As(); /// /// Applicative functor apply operation /// /// /// Unwraps the value within the `ma` applicative-functor, passes it to the unwrapped function(s) within `mf`, and /// then takes the resulting value and wraps it back up into a new applicative-functor. /// /// Value(s) applicative functor /// Mapping function(s) /// Mapped applicative functor public static Arr Apply(this K> mf, K ma) => Applicative.apply(mf, ma).As(); } ================================================ FILE: LanguageExt.Core/Immutable Collections/Arr/Extensions/Arr.Extensions.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics.Contracts; using System.Linq; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; public static partial class ArrExtensions { public static Arr As(this K xs) => (Arr)xs; /// /// Monadic join /// [Pure] public static A[] Flatten(this A[][] ma) => ma.Bind(identity).ToArray(); /// /// Monadic join /// [Pure] public static Arr Flatten(this Arr> ma) => ma.Bind(identity); [Pure] public static Arr Filter(this Arr ma, Func f) => Where(ma, f); [Pure] public static Arr Where(this Arr ma, Func f) { var mb = new List(); foreach (var a in ma) { if (f(a)) { mb.Add(a); } } return new Arr(mb); } [Pure] public static Arr Map(this Arr ma, Func f) => Select(ma, f); [Pure] public static Arr Select(this Arr ma, Func f) { var mb = new B[ma.Count]; var index = 0; foreach (var a in ma) { mb[index] = f(a); index++; } return new Arr(mb); } [Pure] public static Arr Bind(this Arr ma, Func> f) => SelectMany(ma, f); [Pure] public static Arr SelectMany(this Arr ma, Func> f) { var mb = new List(); foreach (var a in ma) { foreach (var b in f(a)) { mb.Add(b); } } return new Arr(mb.ToArray()); } [Pure] public static Arr SelectMany(this Arr ma, Func> bind, Func project) { var mc = new List(); foreach (var a in ma) { foreach (var b in bind(a)) { mc.Add(project(a, b)); } } return new Arr(mc.ToArray()); } /// /// Convert to a queryable /// [Pure] public static IQueryable AsQueryable(this Arr source) => // NOTE TO FUTURE ME: Don't delete this thinking it's not needed! // NOTE FROM FUTURE ME: Next time you leave a message for your future self, explain your reasoning. source.AsEnumerable().AsQueryable(); } ================================================ FILE: LanguageExt.Core/Immutable Collections/Arr/Operators/Arr.Operators.cs ================================================ using LanguageExt.Traits; namespace LanguageExt; public static partial class ArrExtensions { extension(K _) { /// /// Downcast operator /// public static Arr operator +(K ma) => (Arr)ma; public static Arr operator >> (K ma, Lower lower) => (Arr)ma; } } ================================================ FILE: LanguageExt.Core/Immutable Collections/Arr/Prelude/Arr.Prelude.mapapply.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public static partial class Prelude { /// /// Functor map operation /// /// /// Unwraps the value within the functor, passes it to the map function `f` provided, and /// then takes the mapped value and wraps it back up into a new functor. /// /// Functor to map /// Mapping function /// Mapped functor public static Arr map(Func f, K ma) => Functor.map(f, ma).As(); /// /// Applicative action: runs the first applicative, ignores the result, and returns the second applicative /// public static Arr action(K ma, K mb) => Applicative.action(ma, mb).As(); /// /// Applicative functor apply operation /// /// /// Unwraps the value within the `ma` applicative-functor, passes it to the unwrapped function(s) within `mf`, and /// then takes the resulting value and wraps it back up into a new applicative-functor. /// /// Value(s) applicative functor /// Mapping function(s) /// Mapped applicative functor public static Arr apply(K> mf, K ma) => Applicative.apply(mf, ma).As(); } ================================================ FILE: LanguageExt.Core/Immutable Collections/Arr/Trait/Arr.TraitImpl.cs ================================================ using System; using System.Linq; using LanguageExt.Traits; using System.Collections.Generic; using LanguageExt.Common; using static LanguageExt.Prelude; namespace LanguageExt; public partial class Arr : Monad, Traversable, Alternative, MonoidK, Natural, Natural, Natural, Natural, Natural { static K Monad.Bind(K ma, Func> f) { return new Arr(Go()); IEnumerable Go() { foreach (var x in ma.As()) { foreach (var y in f(x).As()) { yield return y; } } } } static K Monad.Recur(A value, Func>> f) => createRange(Monad.enumerableRecur(value, x =>f(x).As().AsEnumerable())); static K Functor.Map(Func f, K ma) => ma.As().Map(f); static K Applicative.Pure(A value) => singleton(value); static K Applicative.Apply(K> mf, K ma) { var ff = mf.As(); if(ff.IsEmpty) return Arr.Empty; var fa = ma.As(); var size = ff.Count * fa.Count; var bs = new B[size]; var ix = 0; foreach (var f in ff) { foreach (var a in fa) { bs[ix] = f(a); ix++; } } return new Arr(bs); } static K Applicative.Apply(K> mf, Memo ma) { var ff = mf.As(); if(ff.IsEmpty) return Arr.Empty; var fa = ma.Value.As(); var size = ff.Count * fa.Count; var bs = new B[size]; var ix = 0; foreach (var f in ff) { foreach (var a in fa) { bs[ix] = f(a); ix++; } } return new Arr(bs); } static K MonoidK.Empty() => Arr.Empty; static K Alternative.Empty() => Arr.Empty; static K SemigroupK.Combine(K ma, K mb) => ma.As() + mb.As(); static K Choice.Choose(K ma, K mb) => ma.IsEmpty ? mb : ma; static K Choice.Choose(K ma, Memo mb) => ma.IsEmpty ? mb.Value : ma; static int Foldable.Count(K ta) => ta.As().Count; static bool Foldable.IsEmpty(K ta) => ta.As().IsEmpty; static S Foldable.FoldWhile(Func> f, Func<(S State, A Value), bool> predicate, S state, K ta) { var arr = ta.As(); foreach (var x in arr) { if (!predicate((state, x))) return state; state = f(x)(state); } return state; } static S Foldable.FoldBackWhile(Func> f, Func<(S State, A Value), bool> predicate, S state, K ta) { var arr = ta.As(); for (var i = arr.Length - 1; i >= 0; i--) { var x = arr[i]; if (!predicate((state, x))) return state; state = f(state)(x); } return state; } static Fold Foldable.FoldStep(K ta, S initialState) { var array = ta.As(); var count = array.Length; if(count == 0) return Fold.Done(initialState); var index = 0; return go(initialState); Fold go(S state) { if (index == count) { return Fold.Done(state); } else { return Fold.Loop(state, array[index++], go); } } } static Fold Foldable.FoldStepBack(K ta, S initialState) { var array = ta.As(); var count = array.Length; if(count == 0) return Fold.Done(initialState); var index = count; return go(initialState); Fold go(S state) { if (index == 0) { return Fold.Done(state); } else { return Fold.Loop(state, array[--index], go); } } } static Option Foldable.At(K ta, Index index) { var arr = ta.As(); return index.Value >= 0 && index.Value < arr.Length ? Some(arr[index]) : Option.None; } static Arr Foldable.ToArr(K ta) => ta.As(); static Lst Foldable.ToLst(K ta) => new(ta.As()); static Iterable Foldable.ToIterable(K ta) => ta.As().AsIterable(); static Seq Foldable.ToSeq(K ta) => Seq.FromArray(ta.As().ToArray()); static K> Traversable.Traverse(Func> f, K ta) { return Foldable.fold(addItem, F.Pure(new SeqStrict(new B[ta.As().Count], 0, 0, 0, 0)), ta) .Map(bs => new Arr(bs.data.AsSpan().Slice(bs.start, bs.Count)).Kind()); Func>, K>> addItem(A value) => state => Applicative.lift((bs, b) => (SeqStrict)bs.Add(b), state, f(value)); } static K> Traversable.TraverseM(Func> f, K ta) { return Foldable.fold(addItem, F.Pure(new SeqStrict(new B[ta.As().Count], 0, 0, 0, 0)), ta) .Map(bs => new Arr(bs.data.AsSpan().Slice(bs.start, bs.Count)).Kind()); Func>, K>> addItem(A value) => state => state.Bind( bs => f(value).Bind( b => F.Pure((SeqStrict)bs.Add(b)))); } static K Natural.Transform(K fa) => toSeq(fa.As().ToSeq()); static K Natural.Transform(K fa) => fa.As().AsIterable(); static K Natural.Transform(K fa) => toList(fa.As()); static K Natural.Transform(K fa) => toSet(fa.As()); static K Natural.Transform(K fa) => toHashSet(fa.As()); } ================================================ FILE: LanguageExt.Core/Immutable Collections/BiMap/BiMap.cs ================================================ using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics.Contracts; using System.Linq; namespace LanguageExt; /// /// Map type indexed both on the A and B for rapid lookup of either /// /// A /// B [Serializable] public readonly struct BiMap : IEnumerable<(A Left, B Right)>, IComparable>, IEquatable>, IComparable { readonly Map Left; readonly Map Right; BiMap(Map left, Map right) { Left = left; Right = right; } public BiMap(IEnumerable<(A Left, B Right)> items) : this(items, true) { } public BiMap(IEnumerable<(A Left, B Right)> items, bool tryAdd) : this(new Map(items), new Map(items.Select(pair => (pair.Right, pair.Left)))) { } /// /// Atomically adds a new item to the map /// /// Null is not allowed for a Key or a Value /// Key /// Value /// Throws ArgumentException if the key already exists /// Throws ArgumentNullException the key or value are null /// New Map with the item added [Pure] public BiMap Add(A left, B right) => new (Left.Add(left, right), Right.Add(right, left)); /// /// Atomically adds a new item to the map. /// If the key already exists, then the new item is ignored /// /// Null is not allowed for a Key or a Value /// Key /// Value /// Throws ArgumentNullException the key or value are null /// New Map with the item added [Pure] public BiMap TryAdd(A left, B right) => new (Left.TryAdd(left, right), Right.TryAdd(right, left)); /// /// Atomically adds a range of items to the map. /// /// Null is not allowed for a Key or a Value /// Range of tuples to add /// Throws ArgumentException if any of the keys already exist /// Throws ArgumentNullException the keys or values are null /// New Map with the items added [Pure] public BiMap AddRange(IEnumerable<(A, B)> range) => new (Left.AddRange(range), Right.AddRange(range.Select(pair => (pair.Item2, pair.Item1)))); /// /// Atomically adds a range of items to the map. /// /// Null is not allowed for a Key or a Value /// Range of tuples to add /// Throws ArgumentException if any of the keys already exist /// Throws ArgumentNullException the keys or values are null /// New Map with the items added [Pure] public BiMap AddRange(IEnumerable> range) => new (Left.AddRange(range), Right.AddRange(range.Select(pair => (pair.Item2, pair.Item1)))); /// /// Atomically adds a range of items to the map. /// /// Null is not allowed for a Key or a Value /// Range of tuples to add /// Throws ArgumentException if any of the keys already exist /// Throws ArgumentNullException the keys or values are null /// New Map with the items added [Pure] public BiMap AddRange(IEnumerable> range) => new (Left.AddRange(range), Right.AddRange(range.Select(pair => (pair.Value, pair.Key)))); /// /// Atomically adds a range of items to the map. /// /// Null is not allowed for a Key or a Value /// Range of tuples to add /// Throws ArgumentNullException the keys or values are null /// New Map with the items added [Pure] public BiMap TryAddRange(IEnumerable<(A, B)> range) => new (Left.TryAddRange(range), Right.TryAddRange(range.Select(pair => (pair.Item2, pair.Item1)))); /// /// Atomically adds a range of items to the map. /// /// Null is not allowed for a Key or a Value /// Range of tuples to add /// Throws ArgumentNullException the keys or values are null /// New Map with the items added [Pure] public BiMap TryAddRange(IEnumerable> range) => new (Left.TryAddRange(range), Right.TryAddRange(range.Select(pair => (pair.Item2, pair.Item1)))); /// /// Atomically adds a range of items to the map. /// /// Null is not allowed for a Key or a Value /// Range of tuples to add /// Throws ArgumentNullException the keys or values are null /// New Map with the items added [Pure] public BiMap TryAddRange(IEnumerable> range) => new (Left.TryAddRange(range), Right.TryAddRange(range.Select(pair => (pair.Value, pair.Key)))); /// /// Atomically adds a range of items to the map. If any of the keys exist already /// then they're replaced. /// /// Null is not allowed for a Key or a Value /// Range of tuples to add /// Throws ArgumentNullException the keys or values are null /// New Map with the items added [Pure] public BiMap AddOrUpdateRange(IEnumerable> range) => new (Left.AddOrUpdateRange(range), Right.AddOrUpdateRange(range.Select(pair => (pair.Item2, pair.Item1)))); /// /// Atomically adds a range of items to the map. If any of the keys exist already /// then they're replaced. /// /// Null is not allowed for a Key or a Value /// Range of tuples to add /// Throws ArgumentNullException the keys or values are null /// New Map with the items added [Pure] public BiMap AddOrUpdateRange(IEnumerable<(A, B)> range) => new (Left.AddOrUpdateRange(range), Right.AddOrUpdateRange(range.Select(pair => (pair.Item2, pair.Item1)))); /// /// Atomically adds a range of items to the map. If any of the keys exist already /// then they're replaced. /// /// Null is not allowed for a Key or a Value /// Range of KeyValuePairs to add /// Throws ArgumentNullException the keys or values are null /// New Map with the items added [Pure] public BiMap AddOrUpdateRange(IEnumerable> range) => new (Left.AddOrUpdateRange(range), Right.AddOrUpdateRange(range.Select(pair => (pair.Value, pair.Key)))); /// /// Atomically removes an item from the map /// If the key doesn't exists, the request is ignored. /// /// Key /// New map with the item removed [Pure] public BiMap Remove(A left) => new (Left.Remove(left), Left.Find(left).Map(Right.Remove).IfNone(Right)); /// /// Atomically removes an item from the map /// If the key doesn't exists, the request is ignored. /// /// Key /// New map with the item removed [Pure] public BiMap Remove(B right) => new (Right.Find(right).Map(Left.Remove).IfNone(Left), Right.Remove(right)); /// /// 'this' accessor /// /// Key /// value [Pure] public A this[B value] => Right[value]; /// /// 'this' accessor /// /// Key /// value [Pure] public B this[A value] => Left[value]; /// /// Is the map empty /// [Pure] public bool IsEmpty => Left.IsEmpty; /// /// Number of items in the map /// [Pure] public int Count => Left.Count; /// /// Alias of Count /// [Pure] public int Length => Left.Length; /// /// Atomically updates an existing item /// /// Null is not allowed for a Key or a Value /// Key /// Value /// Throws ArgumentNullException the key or value are null /// New Map with the item added [Pure] public BiMap SetItem(A left, B right) => new (Left.SetItem(left, right), Right.SetItem(right, left)); /// /// Atomically updates an existing item, unless it doesn't exist, in which case /// it is ignored /// /// Null is not allowed for a Key or a Value /// Key /// Value /// Throws ArgumentNullException the value is null /// New Map with the item added [Pure] public BiMap TrySetItem(A left, B right) => new (Left.TrySetItem(left, right), Right.SetItem(right, left)); /// /// Atomically adds a new item to the map. /// If the key already exists, the new item replaces it. /// /// Null is not allowed for a Key or a Value /// Key /// Value /// Throws ArgumentNullException the key or value are null /// New Map with the item added [Pure] public BiMap AddOrUpdate(A left, B right) => new (Left.AddOrUpdate(left, right), Right.AddOrUpdate(right, left)); /// /// Checks for existence of a key in the map /// /// Key to check /// True if an item with the key supplied is in the map [Pure] public bool ContainsKey(A value) => Left.ContainsKey(value); /// /// Checks for existence of a key in the map /// /// Key to check /// True if an item with the key supplied is in the map [Pure] public bool ContainsKey(B value) => Right.ContainsKey(value); /// /// Clears all items from the map /// /// Functionally equivalent to calling Map.empty as the original structure is untouched /// Empty map [Pure] public BiMap Clear() => default; /// /// Enumerable of map lefts in-order /// [Pure] public Iterable LeftKeys => Left.Keys; /// /// Enumerable of map rights in-order /// [Pure] public Iterable RightKeys => Right.Keys; /// /// Enumerable of map lefts in-rights-order /// [Pure] public Iterable LeftValues => Left.Values; /// /// Enumerable of map rights in-lefts-order /// [Pure] public Iterable RightValues => Right.Values; /// /// Convert the map to an `IReadOnlyDictionary` /// /// [Pure] public IReadOnlyDictionary ToDictionaryLeft() => Left; /// /// Convert the map to an `IReadOnlyDictionary` /// /// [Pure] public IReadOnlyDictionary ToDictionaryRight() => Right; /// /// Enumerable of in-order tuples that make up the map /// /// Tuples [Pure] [Obsolete("Use Pairs instead")] public Iterable<(A Key, B Value)> Tuples => Left.Pairs; /// /// Enumerable of in-order tuples that make up the map /// /// Tuples [Pure] public Iterable<(A Key, B Value)> Pairs => Left.Pairs; [Pure] public Seq<(A Key, B Value)> ToSeq() => Prelude.toSeq(this); [Pure] public bool Equals(BiMap y) => Left.Equals(y.Left); [Pure] public Option Find(A value) => Left.Find(value); [Pure] public Option Find(B value) => Right.Find(value); [Pure] public MapEnumerator GetEnumerator() => Left.GetEnumerator(); [Pure] IEnumerator<(A Left, B Right)> IEnumerable<(A Left, B Right)>.GetEnumerator() => Left.GetEnumerator(); [Pure] IEnumerator IEnumerable.GetEnumerator() => Left.GetEnumerator(); [Pure] public int CompareTo(BiMap rhs) => Left.CompareTo(rhs.Left); [Pure] public int CompareTo(object? obj) => obj is BiMap t ? CompareTo(t) : 1; [Pure] public override bool Equals(object? obj) => obj is BiMap bm && Equals(bm); [Pure] public static bool operator ==(BiMap lhs, BiMap rhs) => lhs.Left.Equals(rhs.Left); [Pure] public static bool operator !=(BiMap lhs, BiMap rhs) => !(lhs == rhs); [Pure] public static bool operator <(BiMap lhs, BiMap rhs) => lhs.CompareTo(rhs) < 0; [Pure] public static bool operator <=(BiMap lhs, BiMap rhs) => lhs.CompareTo(rhs) <= 0; [Pure] public static bool operator >(BiMap lhs, BiMap rhs) => lhs.CompareTo(rhs) > 0; [Pure] public static bool operator >=(BiMap lhs, BiMap rhs) => lhs.CompareTo(rhs) >= 0; [Pure] public static BiMap operator +(BiMap lhs, BiMap rhs) => new (lhs.Left + rhs.Left, lhs.Right + rhs.Right); [Pure] public static BiMap operator -(BiMap lhs, BiMap rhs) => new (lhs.Left - rhs.Left, lhs.Right - rhs.Right); [Pure] public override int GetHashCode() => Left.GetHashCode(); /// /// Implicit conversion from an untyped empty list /// public static implicit operator BiMap(SeqEmpty _) => default; } ================================================ FILE: LanguageExt.Core/Immutable Collections/HashMap/HashMap.Eq.cs ================================================ #pragma warning disable CS0693 // Type parameter has the same name as the type parameter from outer type using LanguageExt.Traits; using static LanguageExt.Prelude; using System; using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics.Contracts; using System.Linq; using System.Numerics; using System.Runtime.CompilerServices; using LanguageExt.ClassInstances; namespace LanguageExt; /// /// Unsorted immutable hash-map /// /// Key type /// Value [CollectionBuilder(typeof(HashMap), nameof(HashMap.createRange))] public readonly struct HashMap : IReadOnlyDictionary, IEnumerable<(K Key, V Value)>, IEquatable>, Monoid>, IEqualityOperators, HashMap, bool>, IAdditionOperators, HashMap, HashMap>, ISubtractionOperators, HashMap, HashMap>, IAdditiveIdentity, HashMap>, K, V> where EqK : Eq { [Pure] public static HashMap Empty { get; } = new(TrieMap.Empty); readonly TrieMap value; internal TrieMap Value => value ?? TrieMap.Empty; [MethodImpl(MethodImplOptions.AggressiveInlining)] internal HashMap(TrieMap value) => this.value = value; [MethodImpl(MethodImplOptions.AggressiveInlining)] public HashMap(IEnumerable<(K Key, V Value)> items) : this(items, true) { } [MethodImpl(MethodImplOptions.AggressiveInlining)] public HashMap(IEnumerable<(K Key, V Value)> items, bool tryAdd) => value = new TrieMap(items, tryAdd); [MethodImpl(MethodImplOptions.AggressiveInlining)] static HashMap Wrap(TrieMap value) => new (value); [MethodImpl(MethodImplOptions.AggressiveInlining)] static HashMap Wrap(TrieMap value) => new (value); /// /// 'this' accessor /// /// Key /// Optional value [Pure] public V this[K key] { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => Value[key]; } /// /// Is the map empty /// [Pure] public bool IsEmpty { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => value?.IsEmpty ?? true; } /// /// Number of items in the map /// [Pure] public int Count { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => value?.Count ?? 0; } /// /// Alias of Count /// [Pure] public int Length { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => value?.Count ?? 0; } /// /// Atomically filter out items that return false when a predicate is applied /// /// Predicate /// New map with items filtered [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public HashMap Filter(Func pred) => Wrap(Value.Filter(pred)); /// /// Atomically filter out items that return false when a predicate is applied /// /// Predicate /// New map with items filtered [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public HashMap Filter(Func pred) => Wrap(Value.Filter(pred)); /// /// Atomically maps the map to a new map /// /// Mapped items in a new map [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public HashMap Map(Func mapper) => Wrap(Value.Map(mapper)); /// /// Atomically maps the map to a new map /// /// Mapped items in a new map [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public HashMap Map(Func mapper) => Wrap(Value.Map(mapper)); /// /// Atomically adds a new item to the map /// /// Null is not allowed for a Key or a Value /// Key /// Value /// Throws ArgumentException if the key already exists /// Throws ArgumentNullException the key or value are null /// New Map with the item added [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public HashMap Add(K key, V value) => Wrap(Value.Add(key, value)); /// /// Atomically adds a new item to the map. /// If the key already exists, then the new item is ignored /// /// Null is not allowed for a Key or a Value /// Key /// Value /// Throws ArgumentNullException the key or value are null /// New Map with the item added [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public HashMap TryAdd(K key, V value) => Wrap(Value.TryAdd(key, value)); /// /// Atomically adds a new item to the map. /// If the key already exists, the new item replaces it. /// /// Null is not allowed for a Key or a Value /// Key /// Value /// Throws ArgumentNullException the key or value are null /// New Map with the item added [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public HashMap AddOrUpdate(K key, V value) => Wrap(Value.AddOrUpdate(key, value)); /// /// Retrieve a value from the map by key, map it to a new value, /// put it back. If it doesn't exist, add a new one based on None result. /// /// Key to find /// Throws Exception if None returns null /// Throws Exception if Some returns null /// New map with the mapped value [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public HashMap AddOrUpdate(K key, Func Some, Func None) => Wrap(Value.AddOrUpdate(key, Some, None)); /// /// Retrieve a value from the map by key, map it to a new value, /// put it back. If it doesn't exist, add a new one based on None result. /// /// Key to find /// Throws ArgumentNullException if None is null /// Throws Exception if Some returns null /// New map with the mapped value [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public HashMap AddOrUpdate(K key, Func Some, V None) => Wrap(Value.AddOrUpdate(key, Some, None)); /// /// Atomically adds a range of items to the map. /// /// Null is not allowed for a Key or a Value /// Range of tuples to add /// Throws ArgumentException if any of the keys already exist /// Throws ArgumentNullException the keys or values are null /// New Map with the items added [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public HashMap AddRange(IEnumerable> range) => Wrap(Value.AddRange(range)); /// /// Atomically adds a range of items to the map. /// /// Null is not allowed for a Key or a Value /// Range of tuples to add /// Throws ArgumentException if any of the keys already exist /// Throws ArgumentNullException the keys or values are null /// New Map with the items added [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public HashMap AddRange(IEnumerable<(K, V)> range) => Wrap(Value.AddRange(range)); /// /// Atomically adds a range of items to the map. If any of the keys exist already /// then they're ignored. /// /// Null is not allowed for a Key or a Value /// Range of tuples to add /// Throws ArgumentNullException the keys or values are null /// New Map with the items added [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public HashMap TryAddRange(IEnumerable> range) => Wrap(Value.TryAddRange(range)); /// /// Atomically adds a range of items to the map. If any of the keys exist already /// then they're ignored. /// /// Null is not allowed for a Key or a Value /// Range of tuples to add /// Throws ArgumentNullException the keys or values are null /// New Map with the items added [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public HashMap TryAddRange(IEnumerable<(K, V)> range) => Wrap(Value.TryAddRange(range)); /// /// Atomically adds a range of items to the map. If any of the keys exist already /// then they're ignored. /// /// Null is not allowed for a Key or a Value /// Range of KeyValuePairs to add /// Throws ArgumentNullException the keys or values are null /// New Map with the items added [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public HashMap TryAddRange(IEnumerable> range) => Wrap(Value.TryAddRange(range)); /// /// Atomically adds a range of items to the map. If any of the keys exist already /// then they're replaced. /// /// Null is not allowed for a Key or a Value /// Range of tuples to add /// Throws ArgumentNullException the keys or values are null /// New Map with the items added [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public HashMap AddOrUpdateRange(IEnumerable> range) => Wrap(Value.AddOrUpdateRange(range)); /// /// Atomically adds a range of items to the map. If any of the keys exist already /// then they're replaced. /// /// Null is not allowed for a Key or a Value /// Range of tuples to add /// Throws ArgumentNullException the keys or values are null /// New Map with the items added [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public HashMap AddOrUpdateRange(IEnumerable<(K, V)> range) => Wrap(Value.AddOrUpdateRange(range)); /// /// Atomically adds a range of items to the map. If any of the keys exist already /// then they're replaced. /// /// Null is not allowed for a Key or a Value /// Range of KeyValuePairs to add /// Throws ArgumentNullException the keys or values are null /// New Map with the items added [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public HashMap AddOrUpdateRange(IEnumerable> range) => Wrap(Value.AddOrUpdateRange(range)); /// /// Atomically removes an item from the map /// If the key doesn't exists, the request is ignored. /// /// Key /// New map with the item removed [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public HashMap Remove(K key) => Wrap(Value.Remove(key)); /// /// Retrieve a value from the map by key /// /// Key to find /// Found value [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Option Find(K key) => Value.Find(key); /// /// Retrieve a value from the map by key as an enumerable /// /// Key to find /// Found value [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Seq FindSeq(K key) => Value.FindAll(key); /// /// Retrieve a value from the map by key and pattern match the /// result. /// /// Key to find /// Found value [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public R Find(K key, Func Some, Func None) => Value.Find(key,Some,None); /// /// Try to find the key in the map, if it doesn't exist, add a new /// item by invoking the delegate provided. /// /// Key to find /// Delegate to get the value /// Updated map and added value [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public (HashMap Map, V Value) FindOrAdd(K key, Func None) => Value.FindOrAdd(key, None).Map((x, y) => (Wrap(x), y)); /// /// Try to find the key in the map, if it doesn't exist, add a new /// item provided. /// /// Key to find /// Delegate to get the value /// Updated map and added value [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public (HashMap, V Value) FindOrAdd(K key, V value) => Value.FindOrAdd(key, value).Map((x, y) => (Wrap(x), y)); /// /// Try to find the key in the map, if it doesn't exist, add a new /// item by invoking the delegate provided. /// /// Key to find /// Delegate to get the value /// Updated map and added value [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public (HashMap Map, Option Value) FindOrMaybeAdd(K key, Func> None) => Value.FindOrMaybeAdd(key, None).Map((x, y) => (Wrap(x), y)); /// /// Try to find the key in the map, if it doesn't exist, add a new /// item by invoking the delegate provided. /// /// Key to find /// Delegate to get the value /// Updated map and added value [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public (HashMap Map, Option Value) FindOrMaybeAdd(K key, Option None) => Value.FindOrMaybeAdd(key, None).Map((x, y) => (Wrap(x), y)); /// /// Atomically updates an existing item /// /// Null is not allowed for a Key or a Value /// Key /// Value /// Throws ArgumentNullException the key or value are null /// New Map with the item added [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public HashMap SetItem(K key, V value) => Wrap(Value.SetItem(key, value)); /// /// Retrieve a value from the map by key, map it to a new value, /// put it back. /// /// Key to set /// Throws ArgumentException if the item isn't found /// Throws Exception if Some returns null /// New map with the mapped value [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public HashMap SetItem(K key, Func Some) => Wrap(Value.SetItem(key, Some)); /// /// Atomically updates an existing item, unless it doesn't exist, in which case /// it is ignored /// /// Null is not allowed for a Key or a Value /// Key /// Value /// Throws ArgumentNullException the value is null /// New Map with the item added [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public HashMap TrySetItem(K key, V value) => Wrap(Value.TrySetItem(key, value)); /// /// Atomically sets an item by first retrieving it, applying a map, and then putting it back. /// Silently fails if the value doesn't exist /// /// Key to set /// delegate to map the existing value to a new one before setting /// Throws Exception if Some returns null /// Throws ArgumentNullException the key or value are null /// New map with the item set [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public HashMap TrySetItem(K key, Func Some) => Wrap(Value.SetItem(key, Some)); /// /// Checks for existence of a key in the map /// /// Key to check /// True if an item with the key supplied is in the map [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool ContainsKey(K key) => Value.ContainsKey(key); /// /// Checks for existence of a key in the map /// /// Key to check /// True if an item with the key supplied is in the map [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Contains(K key, V value) => Value.Contains(key, value); /// /// Checks for existence of a key in the map /// /// Key to check /// True if an item with the key supplied is in the map [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Contains(K key, V value) where EqV : Eq => Value.Contains(key, value); /// /// Clears all items from the map /// /// Functionally equivalent to calling Map.empty as the original structure is untouched /// Empty map [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public HashMap Clear() => Wrap(Value.Clear()); /// /// Atomically adds a range of items to the map /// /// Range of KeyValuePairs to add /// Throws ArgumentException if any of the keys already exist /// New Map with the items added [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public HashMap AddRange(IEnumerable> pairs) => Wrap(Value.AddRange(pairs)); /// /// Atomically sets a series of items using the KeyValuePairs provided /// /// Items to set /// Throws ArgumentException if any of the keys aren't in the map /// New map with the items set [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public HashMap SetItems(IEnumerable> items) => Wrap(Value.SetItems(items)); /// /// Atomically sets a series of items using the Tuples provided. /// /// Items to set /// Throws ArgumentException if any of the keys aren't in the map /// New map with the items set [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public HashMap SetItems(IEnumerable> items) => Wrap(Value.SetItems(items)); /// /// Atomically sets a series of items using the Tuples provided. /// /// Items to set /// Throws ArgumentException if any of the keys aren't in the map /// New map with the items set [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public HashMap SetItems(IEnumerable<(K, V)> items) => Wrap(Value.SetItems(items)); /// /// Atomically sets a series of items using the KeyValuePairs provided. If any of the /// items don't exist then they're silently ignored. /// /// Items to set /// New map with the items set [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public HashMap TrySetItems(IEnumerable> items) => Wrap(Value.TrySetItems(items)); /// /// Atomically sets a series of items using the Tuples provided If any of the /// items don't exist then they're silently ignored. /// /// Items to set /// New map with the items set [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public HashMap TrySetItems(IEnumerable> items) => Wrap(Value.TrySetItems(items)); /// /// Atomically sets a series of items using the Tuples provided If any of the /// items don't exist then they're silently ignored. /// /// Items to set /// New map with the items set [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public HashMap TrySetItems(IEnumerable<(K, V)> items) => Wrap(Value.TrySetItems(items)); /// /// Atomically sets a series of items using the keys provided to find the items /// and the Some delegate maps to a new value. If the items don't exist then /// they're silently ignored. /// /// Keys of items to set /// Function map the existing item to a new one /// New map with the items set [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public HashMap TrySetItems(IEnumerable keys, Func Some) => Wrap(Value.TrySetItems(keys, Some)); /// /// Atomically removes a set of keys from the map /// /// Keys to remove /// New map with the items removed [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public HashMap RemoveRange(IEnumerable keys) => Wrap(Value.RemoveRange(keys)); /// /// Returns true if a Key/Value pair exists in the map /// /// Pair to find /// True if exists, false otherwise [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Contains(KeyValuePair pair) => Value.Contains(pair.Key, pair.Value); /// /// Returns true if a Key/Value pair exists in the map /// /// Pair to find /// True if exists, false otherwise [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Contains(KeyValuePair pair) where EqV : Eq => Value.Contains(pair.Key, pair.Value); /// /// Enumerable of map keys /// [Pure] public Iterable Keys { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => Value.Keys; } /// /// Enumerable of map values /// [Pure] public Iterable Values { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => Value.Values; } /// /// Map the map the a dictionary /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public IDictionary ToDictionary( Func<(K Key, V Value), KR> keySelector, Func<(K Key, V Value), VR> valueSelector) where KR : notnull => Value.AsIterable().ToDictionary(keySelector, valueSelector); #region IEnumerable interface /// /// GetEnumerator - IEnumerable interface /// public IEnumerator<(K Key, V Value)> GetEnumerator() => Value.GetEnumerator(); /// /// GetEnumerator - IEnumerable interface /// IEnumerator IEnumerable.GetEnumerator() => Value.GetEnumerator(); [Pure] public Seq<(K Key, V Value)> ToSeq() => toSeq(Value.AsIterable()); /// /// Allocation free conversion to a TrackingHashMap /// [Pure] public HashMap ToTrackingHashMap() => new (value); /// /// Format the collection as `[(key: value), (key: value), (key: value), ...]` /// The elipsis is used for collections over 50 items /// To get a formatted string with all the items, use `ToFullString` /// or `ToFullArrayString`. /// [Pure] public override string ToString() => CollectionFormat.ToShortArrayString(AsEnumerable().Map(kv => $"({kv.Key}: {kv.Value})"), Count); /// /// Format the collection as `(key: value), (key: value), (key: value), ...` /// [Pure] public string ToFullString(string separator = ", ") => CollectionFormat.ToFullString(AsEnumerable().Map(kv => $"({kv.Key}: {kv.Value})"), separator); /// /// Format the collection as `[(key: value), (key: value), (key: value), ...]` /// [Pure] public string ToFullArrayString(string separator = ", ") => CollectionFormat.ToFullArrayString(AsEnumerable().Map(kv => $"({kv.Key}: {kv.Value})"), separator); [Pure] public Iterable<(K Key, V Value)> AsEnumerable() => Value.AsIterable(); #endregion /// /// Implicit conversion from an untyped empty list /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator HashMap(SeqEmpty _) => Empty; /// /// Equality of keys and values with `EqDefault〈V〉` used for values /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(HashMap lhs, HashMap rhs) => lhs.Equals(rhs); /// /// In-equality of keys and values with `EqDefault〈V〉` used for values /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(HashMap lhs, HashMap rhs) => !(lhs == rhs); [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static HashMap operator +(HashMap lhs, HashMap rhs) => lhs.Combine(rhs); [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public HashMap Combine(HashMap rhs) => Wrap(Value.Append(rhs.Value)); [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static HashMap operator -(HashMap lhs, HashMap rhs) => lhs.Subtract(rhs); [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public HashMap Subtract(HashMap rhs) => Wrap(Value.Subtract(rhs.Value)); /// /// Returns True if 'other' is a proper subset of this set /// /// True if 'other' is a proper subset of this set [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool IsProperSubsetOf(IEnumerable<(K Key, V Value)> other) => Value.IsProperSubsetOf(other); /// /// Returns True if 'other' is a proper subset of this set /// /// True if 'other' is a proper subset of this set [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool IsProperSubsetOf(IEnumerable other) => Value.IsProperSubsetOf(other); /// /// Returns True if 'other' is a proper superset of this set /// /// True if 'other' is a proper superset of this set [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool IsProperSupersetOf(IEnumerable<(K Key, V Value)> other) => Value.IsProperSupersetOf(other); /// /// Returns True if 'other' is a proper superset of this set /// /// True if 'other' is a proper superset of this set [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool IsProperSupersetOf(IEnumerable other) => Value.IsProperSupersetOf(other); /// /// Returns True if 'other' is a superset of this set /// /// True if 'other' is a superset of this set [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool IsSubsetOf(IEnumerable<(K Key, V Value)> other) => Value.IsSubsetOf(other); /// /// Returns True if 'other' is a superset of this set /// /// True if 'other' is a superset of this set [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool IsSubsetOf(IEnumerable other) => Value.IsSubsetOf(other); /// /// Returns True if 'other' is a superset of this set /// /// True if 'other' is a superset of this set [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool IsSubsetOf(HashMap other) => Value.IsSubsetOf(other.Value); /// /// Returns True if 'other' is a superset of this set /// /// True if 'other' is a superset of this set [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool IsSupersetOf(IEnumerable<(K Key, V Value)> other) => Value.IsSupersetOf(other); /// /// Returns True if 'other' is a superset of this set /// /// True if 'other' is a superset of this set [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool IsSupersetOf(IEnumerable rhs) => Value.IsSupersetOf(rhs); /// /// Returns True if other overlaps this set /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Overlaps(IEnumerable<(K Key, V Value)> other) => Value.Overlaps(other); /// /// Returns True if other overlaps this set /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Overlaps(IEnumerable other) => Value.Overlaps(other); /// /// Returns the elements that are in both this and other /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public HashMap Intersect(IEnumerable rhs) => Wrap(Value.Intersect(rhs)); /// /// Returns the elements that are in both this and other /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public HashMap Intersect(IEnumerable<(K Key, V Value)> rhs) => Wrap(Value.Intersect(rhs)); /// /// Returns the elements that are in both this and other /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public HashMap Intersect(IEnumerable<(K Key, V Value)> rhs, WhenMatched Merge) => Wrap(Value.Intersect(rhs, Merge)); /// /// Returns this - other. Only the items in this that are not in /// other will be returned. /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public HashMap Except(IEnumerable rhs) => Wrap(Value.Except(rhs)); /// /// Returns this - other. Only the items in this that are not in /// other will be returned. /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public HashMap Except(IEnumerable<(K Key, V Value)> rhs) => Wrap(Value.Except(rhs)); /// /// Only items that are in one set or the other will be returned. /// If an item is in both, it is dropped. /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public HashMap SymmetricExcept(HashMap rhs) => Wrap(Value.SymmetricExcept(rhs.Value)); /// /// Only items that are in one set or the other will be returned. /// If an item is in both, it is dropped. /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public HashMap SymmetricExcept(IEnumerable<(K Key, V Value)> rhs) => Wrap(Value.SymmetricExcept(rhs)); /// /// Finds the union of two sets and produces a new set with /// the results /// /// Other set to union with /// A set which contains all items from both sets [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public HashMap Union(IEnumerable<(K, V)> rhs) => this.TryAddRange(rhs); /// /// Union two maps. /// /// /// The `WhenMatched` merge function is called when keys are present in both map to allow resolving to a /// sensible value. /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public HashMap Union(IEnumerable<(K, V)> other, WhenMatched Merge) => Wrap(Value.Union(other, static (_, v) => v, static (_, v) => v, Merge)); /// /// Union two maps. /// /// /// The `WhenMatched` merge function is called when keys are present in both map to allow resolving to a /// sensible value. /// /// /// The `WhenMissing` function is called when there is a key in the right-hand side, but not the left-hand-side. /// This allows the `V2` value-type to be mapped to the target `V` value-type. /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public HashMap Union( IEnumerable<(K, W)> other, WhenMissing MapRight, WhenMatched Merge) => Wrap(Value.Union(other, static (_, v) => v, MapRight, Merge)); /// /// Union two maps. /// /// /// The `WhenMatched` merge function is called when keys are present in both map to allow resolving to a /// sensible value. /// /// /// The `WhenMissing` function is called when there is a key in the left-hand side, but not the right-hand-side. /// This allows the `V` value-type to be mapped to the target `V2` value-type. /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public HashMap Union( IEnumerable<(K, W)> other, WhenMissing MapLeft, WhenMatched Merge) => Wrap(Value.Union(other, MapLeft, static (_, v) => v, Merge)); /// /// Union two maps. /// /// /// The `WhenMatched` merge function is called when keys are present in both map to allow resolving to a /// sensible value. /// /// /// The `WhenMissing MapLeft` function is called when there is a key in the left-hand side, but not the /// right-hand-side. This allows the `V` value-type to be mapped to the target `R` value-type. /// /// /// The `WhenMissing MapRight` function is called when there is a key in the right-hand side, but not the /// left-hand-side. This allows the `V2` value-type to be mapped to the target `R` value-type. /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public HashMap Union( IEnumerable<(K, W)> other, WhenMissing MapLeft, WhenMissing MapRight, WhenMatched Merge) => Wrap(Value.Union(other, MapLeft, MapRight, Merge)); /// /// Equality of keys /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public override bool Equals(object? obj) => obj is HashMap hm && Equals(hm); /// /// Equality of keys and values with `EqDefault〈V〉` used for values /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(HashMap other) => Value.Equals>(other.Value); /// /// Equality of keys and values /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(HashMap other) where EqV : Eq => Value.Equals(other.Value); /// /// Equality of keys only /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool EqualKeys(HashMap other) => Value.Equals>(other.Value); [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public override int GetHashCode() => Value.GetHashCode(); /// /// Impure iteration of the bound values in the structure /// /// /// Returns the original unmodified structure /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public HashMap Do(Action f) { this.Iter(f); return this; } /// /// Atomically maps the map to a new map /// /// Mapped items in a new map [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public HashMap Select(Func mapper) => Map(mapper); /// /// Atomically maps the map to a new map /// /// Mapped items in a new map [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public HashMap Select(Func mapper) => Map(mapper); /// /// Atomically filter out items that return false when a predicate is applied /// /// Predicate /// New map with items filtered [Pure] [EditorBrowsable(EditorBrowsableState.Never)] [MethodImpl(MethodImplOptions.AggressiveInlining)] public HashMap Where(Func pred) => Filter(pred); /// /// Atomically filter out items that return false when a predicate is applied /// /// Predicate /// New map with items filtered [Pure] [EditorBrowsable(EditorBrowsableState.Never)] [MethodImpl(MethodImplOptions.AggressiveInlining)] public HashMap Where(Func pred) => Filter(pred); /// /// Return true if all items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool ForAll(Func pred) { foreach (var item in AsEnumerable()) { if (!pred(item.Key, item.Value)) return false; } return true; } /// /// Return true if all items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool ForAll(Func<(K Key, V Value), bool> pred) => AsEnumerable().Map(kv => (kv.Key, kv.Value)).ForAll(pred); /// /// Return true if all items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool ForAll(Func pred) => Values.ForAll(pred); /// /// Return true if *any* items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Exists(Func pred) { foreach (var item in AsEnumerable()) { if (pred(item.Key, item.Value)) return true; } return false; } /// /// Return true if *any* items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Exists(Func<(K Key, V Value), bool> pred) => AsEnumerable().Map(kv => (kv.Key, kv.Value)).Exists(pred); /// /// Return true if *any* items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Exists(Func pred) => Values.Exists(pred); /// /// Atomically iterate through all key/value pairs in the map (in order) and execute an /// action on each /// /// Action to execute /// Unit [MethodImpl(MethodImplOptions.AggressiveInlining)] public Unit Iter(Action action) { foreach (var item in this) { action(item.Key, item.Value); } return unit; } /// /// Atomically iterate through all values in the map (in order) and execute an /// action on each /// /// Action to execute /// Unit [MethodImpl(MethodImplOptions.AggressiveInlining)] public Unit Iter(Action action) { foreach (var item in this) { action(item.Value); } return unit; } /// /// Atomically iterate through all key/value pairs (as tuples) in the map (in order) /// and execute an action on each /// /// Action to execute /// Unit [MethodImpl(MethodImplOptions.AggressiveInlining)] public Unit Iter(Action> action) { foreach (var item in this) { action(new Tuple(item.Key, item.Value)); } return unit; } /// /// Atomically iterate through all key/value pairs (as tuples) in the map (in order) /// and execute an action on each /// /// Action to execute /// Unit [MethodImpl(MethodImplOptions.AggressiveInlining)] public Unit Iter(Action<(K Key, V Value)> action) { foreach (var item in this) { action(item); } return unit; } /// /// Atomically iterate through all key/value pairs in the map (in order) and execute an /// action on each /// /// Action to execute /// Unit [MethodImpl(MethodImplOptions.AggressiveInlining)] public Unit Iter(Action> action) { foreach (var item in this) { action(new KeyValuePair(item.Key, item.Value)); } return unit; } /// /// Atomically folds all items in the map (in order) using the folder function provided. /// /// State type /// Initial state /// Fold function /// Folded state [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public S Fold(S state, Func folder) => AsEnumerable().Fold(state, (s, x) => folder(s, x.Key, x.Value)); [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] IEnumerator> IEnumerable>.GetEnumerator() => AsEnumerable().Map(p => new KeyValuePair(p.Key, p.Value)).GetEnumerator(); [Pure] IEnumerable IReadOnlyDictionary.Keys => Keys; [Pure] IEnumerable IReadOnlyDictionary.Values => Values; /// /// Get a IReadOnlyDictionary for this map. No mapping is required, so this is very fast. /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public IReadOnlyDictionary ToReadOnlyDictionary() => this; [Pure] public bool TryGetValue(K key, out V value) { var v = Find(key); if (v.IsSome) { value = (V)v; return true; } else { value = default!; return false; } } [Pure] [Obsolete(Change.UseCollectionIntialiser)] public static implicit operator HashMap(ValueTuple<(K, V)> items) => new (new[] { items.Item1 }); [Pure] [Obsolete(Change.UseCollectionIntialiser)] public static implicit operator HashMap(((K, V), (K, V)) items) => new (new[] { items.Item1, items.Item2 }); [Pure] [Obsolete(Change.UseCollectionIntialiser)] public static implicit operator HashMap(((K, V), (K, V), (K, V)) items) => new (new[] { items.Item1, items.Item2, items.Item3 }); [Pure] [Obsolete(Change.UseCollectionIntialiser)] public static implicit operator HashMap(((K, V), (K, V), (K, V), (K, V)) items) => new (new[] { items.Item1, items.Item2, items.Item3, items.Item4 }); [Pure] [Obsolete(Change.UseCollectionIntialiser)] public static implicit operator HashMap(((K, V), (K, V), (K, V), (K, V), (K, V)) items) => new (new[] { items.Item1, items.Item2, items.Item3, items.Item4, items.Item5 }); [Pure] [Obsolete(Change.UseCollectionIntialiser)] public static implicit operator HashMap(((K, V), (K, V), (K, V), (K, V), (K, V), (K, V)) items) => new (new[] { items.Item1, items.Item2, items.Item3, items.Item4, items.Item5, items.Item6 }); [Pure] [Obsolete(Change.UseCollectionIntialiser)] public static implicit operator HashMap(((K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V)) items) => new (new[] { items.Item1, items.Item2, items.Item3, items.Item4, items.Item5, items.Item6, items.Item7 }); [Pure] [Obsolete(Change.UseCollectionIntialiser)] public static implicit operator HashMap(((K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V)) items) => new (new[] { items.Item1, items.Item2, items.Item3, items.Item4, items.Item5, items.Item6, items.Item7, items.Item8 }); [Pure] [Obsolete(Change.UseCollectionIntialiser)] public static implicit operator HashMap(((K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V)) items) => new (new[] { items.Item1, items.Item2, items.Item3, items.Item4, items.Item5, items.Item6, items.Item7, items.Item8, items.Item9 }); [Pure] [Obsolete(Change.UseCollectionIntialiser)] public static implicit operator HashMap(((K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V)) items) => new (new[] { items.Item1, items.Item2, items.Item3, items.Item4, items.Item5, items.Item6, items.Item7, items.Item8, items.Item9, items.Item10 }); [Pure] [Obsolete(Change.UseCollectionIntialiser)] public static implicit operator HashMap(((K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V)) items) => new (new[] { items.Item1, items.Item2, items.Item3, items.Item4, items.Item5, items.Item6, items.Item7, items.Item8, items.Item9, items.Item10, items.Item11 }); [Pure] [Obsolete(Change.UseCollectionIntialiser)] public static implicit operator HashMap(((K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V)) items) => new (new[] { items.Item1, items.Item2, items.Item3, items.Item4, items.Item5, items.Item6, items.Item7, items.Item8, items.Item9, items.Item10, items.Item11, items.Item12 }); [Pure] [Obsolete(Change.UseCollectionIntialiser)] public static implicit operator HashMap(((K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V)) items) => new (new[] { items.Item1, items.Item2, items.Item3, items.Item4, items.Item5, items.Item6, items.Item7, items.Item8, items.Item9, items.Item10, items.Item11, items.Item12, items.Item13 }); [Pure] [Obsolete(Change.UseCollectionIntialiser)] public static implicit operator HashMap(((K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V)) items) => new (new[] { items.Item1, items.Item2, items.Item3, items.Item4, items.Item5, items.Item6, items.Item7, items.Item8, items.Item9, items.Item10, items.Item11, items.Item12, items.Item13, items.Item14 }); [Pure] [Obsolete(Change.UseCollectionIntialiser)] public static implicit operator HashMap(((K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V)) items) => new (new[] { items.Item1, items.Item2, items.Item3, items.Item4, items.Item5, items.Item6, items.Item7, items.Item8, items.Item9, items.Item10, items.Item11, items.Item12, items.Item13, items.Item14, items.Item15 }); [Pure] [Obsolete(Change.UseCollectionIntialiser)] public static implicit operator HashMap(((K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V)) items) => new (new[] { items.Item1, items.Item2, items.Item3, items.Item4, items.Item5, items.Item6, items.Item7, items.Item8, items.Item9, items.Item10, items.Item11, items.Item12, items.Item13, items.Item14, items.Item15, items.Item16 }); public static HashMap AdditiveIdentity => Empty; } ================================================ FILE: LanguageExt.Core/Immutable Collections/HashMap/HashMap.Extensions.Eq.cs ================================================ #pragma warning disable CS0693 // Type parameter has the same name as the type parameter from outer type using System.Collections.Generic; using System.Diagnostics.Contracts; using System.Linq; using System.Runtime.CompilerServices; using LanguageExt.Traits; namespace LanguageExt; public static partial class HashMapExtensions { public static HashMap As(this K, V> ma) where EqKey : Eq => (HashMap)ma; /// /// Create an immutable hash-map /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static HashMap ToHashMap(this IEnumerable<(K, V)> items) where EqK : Eq => new(items); /// /// Create an immutable hash-map /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static HashMap ToHashMap(this IEnumerable<(K1, K2, V)> items) where EqK : Eq<(K1, K2)> => new (items.Select(x => ((x.Item1, x.Item2), x.Item3))); /// /// Create an immutable hash-map /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static HashMap ToHashMap(this IEnumerable<(K1, K2, K3, V)> items) where EqK : Eq<(K1, K2, K3)> => new (items.Select(x => ((x.Item1, x.Item2, x.Item3), x.Item4))); /// /// Create an immutable hash-map /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static HashMap ToHashMap(this IEnumerable<(K1, K2, K3, K4, V)> items) where EqK : Eq<(K1, K2, K3, K4)> => new (items.Select(x => ((x.Item1, x.Item2, x.Item3, x.Item4), x.Item5))); /// /// Convert to a queryable /// [Pure] public static IQueryable<(K Key, V Value)> AsQueryable(this HashMap source) where EqK : Eq => // NOTE TO FUTURE ME: Don't delete this thinking it's not needed! source.Value.AsQueryable(); } ================================================ FILE: LanguageExt.Core/Immutable Collections/HashMap/HashMap.Extensions.MapApply.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public static partial class HashMapExtensions { /// /// Functor map operation /// /// /// Unwraps the value within the functor, passes it to the map function `f` provided, and /// then takes the mapped value and wraps it back up into a new functor. /// /// Functor to map /// Mapping function /// Mapped functor public static HashMap Map(this Func f, K, A> ma) => Functor.map(f, ma).As(); /// /// Functor map operation /// /// /// Unwraps the value within the functor, passes it to the map function `f` provided, and /// then takes the mapped value and wraps it back up into a new functor. /// /// Functor to map /// Mapping function /// Mapped functor public static HashMap Map(this Func f, HashMap ma) => Functor.map(f, ma).As(); /// /// Functor map operation /// /// /// Unwraps the value within the functor, passes it to the map function `f` provided, and /// then takes the mapped value and wraps it back up into a new functor. /// /// Functor to map /// Mapping function /// Mapped functor public static HashMap Map(this Func f, K, A> ma) where EqKey : Eq => ma.As().Map(f); /// /// Functor map operation /// /// /// Unwraps the value within the functor, passes it to the map function `f` provided, and /// then takes the mapped value and wraps it back up into a new functor. /// /// Functor to map /// Mapping function /// Mapped functor public static HashMap Map(this Func f, HashMap ma) where EqKey : Eq => ma.Map(f); } ================================================ FILE: LanguageExt.Core/Immutable Collections/HashMap/HashMap.Extensions.cs ================================================ #pragma warning disable CS0693 // Type parameter has the same name as the type parameter from outer type using System.Collections.Generic; using System.Diagnostics.Contracts; using System.Linq; using System.Runtime.CompilerServices; using LanguageExt.Traits; namespace LanguageExt; public static partial class HashMapExtensions { public static HashMap As(this K, V> ma) => (HashMap)ma; /// /// Create an immutable hash-map /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static HashMap ToHashMap(this IEnumerable<(K, V)> items) => new(items); /// /// Create an immutable hash-map /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static HashMap<(K1, K2), V> ToHashMap(this IEnumerable<(K1, K2, V)> items) => new (items.Select(x => ((x.Item1, x.Item2), x.Item3))); /// /// Create an immutable hash-map /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static HashMap<(K1, K2, K3), V> ToHashMap(this IEnumerable<(K1, K2, K3, V)> items) => new (items.Select(x => ((x.Item1, x.Item2, x.Item3), x.Item4))); /// /// Create an immutable hash-map /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static HashMap<(K1, K2, K3, K4), V> ToHashMap(this IEnumerable<(K1, K2, K3, K4, V)> items) => new (items.Select(x => ((x.Item1, x.Item2, x.Item3, x.Item4), x.Item5))); /// /// Convert to a queryable /// [Pure] public static IQueryable<(K Key, V Value)> AsQueryable(this HashMap source) => // NOTE TO FUTURE ME: Don't delete this thinking it's not needed! source.Value.AsQueryable(); } ================================================ FILE: LanguageExt.Core/Immutable Collections/HashMap/HashMap.Module.Eq.cs ================================================ #pragma warning disable CS0693 // Type parameter has the same name as the type parameter from outer type using System; using System.Collections.Generic; using System.Diagnostics.Contracts; using System.Linq; using System.Runtime.CompilerServices; using LanguageExt.Traits; namespace LanguageExt; /// /// Immutable hash-map module /// public partial class HashMap { /// /// Clears all items from the map /// /// Map to clear /// Functionally equivalent to calling Map.empty as the original structure is untouched /// Empty map [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static HashMap clear(HashMap map) where EqK : Eq => HashMap.Empty; /// /// Creates a new empty Map /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static HashMap empty() where EqK : Eq => HashMap.Empty; /// /// Creates a new empty HashMap /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static HashMap create() where EqK : Eq => HashMap.Empty; /// /// Create a singleton collection /// /// Single value /// Collection with a single item in it [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static HashMap singleton(K key, V value) where EqK : Eq => [(key, value)]; /// /// Creates a new Map seeded with the keyValues provided /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static HashMap create(Tuple head, params Tuple[] tail) where EqK : Eq => createRange(head.Cons(tail)); /// /// Creates a new Map seeded with the keyValues provided /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static HashMap create((K, V) head, params (K, V)[] tail) where EqK : Eq => createRange(head.Cons(tail)); /// /// Creates a new Map seeded with the keyValues provided /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static HashMap create(KeyValuePair head, params KeyValuePair[] tail) where EqK : Eq => createRange(head.Cons(tail)); /// /// Creates a new Map seeded with the keyValues provided /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static HashMap createRange(IEnumerable> keyValues) where EqK : Eq => createRange(keyValues.Select(static kv => (kv.Item1, kv.Item2))); /// /// Creates a new Map seeded with the keyValues provided /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static HashMap createRange(IEnumerable<(K, V)> keyValues) where EqK : Eq => new (new TrieMap(keyValues)); /// /// Creates a new Map seeded with the keyValues provided /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static HashMap createRange(ReadOnlySpan<(K, V)> keyValues) where EqK : Eq => keyValues.IsEmpty ? HashMap.Empty : new (new TrieMap(keyValues)); /// /// Creates a new Map seeded with the keyValues provided /// [Pure] public static HashMap createRange(IEnumerable> keyValues) where EqK : Eq => createRange(keyValues.Select(static kv => (kv.Key, kv.Value))); /// /// Atomically adds a new item to the map /// /// Null is not allowed for a Key or a Value /// Key /// Value /// Throws ArgumentException if the key already exists /// Throws ArgumentNullException the key or value are null /// New Map with the item added [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static HashMap add(HashMap map, K key, V value) where EqK : Eq => map.Add(key, value); /// /// Atomically adds a new item to the map. /// If the key already exists, then the new item is ignored /// /// Null is not allowed for a Key or a Value /// Key /// Value /// Throws ArgumentNullException the key or value are null /// New Map with the item added [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static HashMap tryAdd(HashMap map, K key, V value) where EqK : Eq => map.TryAdd(key, value); /// /// Atomically adds a new item to the map. /// If the key already exists, the new item replaces it. /// /// Null is not allowed for a Key or a Value /// Key /// Value /// Throws ArgumentNullException the key or value are null /// New Map with the item added [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static HashMap addOrUpdate(HashMap map, K key, V value) where EqK : Eq => map.AddOrUpdate(key, value); /// /// Retrieve a value from the map by key, map it to a new value, /// put it back. If it doesn't exist, add a new one based on None result. /// /// Key to find /// Throws Exception if None returns null /// Throws Exception if Some returns null /// New map with the mapped value [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static HashMap addOrUpdate(HashMap map, K key, Func Some, Func None) where EqK : Eq => map.AddOrUpdate(key, Some, None); /// /// Retrieve a value from the map by key, map it to a new value, /// put it back. If it doesn't exist, add a new one based on None result. /// /// Key to find /// Throws ArgumentNullException if None is null /// Throws Exception if Some returns null /// New map with the mapped value [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static HashMap addOrUpdate(HashMap map, K key, Func Some, V None) where EqK : Eq => map.AddOrUpdate(key, Some, None); /// /// Atomically adds a range of items to the map. /// /// Null is not allowed for a Key or a Value /// Range of tuples to add /// Throws ArgumentException if any of the keys already exist /// Throws ArgumentNullException the keys or values are null /// New Map with the items added [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static HashMap addRange(HashMap map, IEnumerable> keyValues) where EqK : Eq => map.AddRange(keyValues); /// /// Atomically adds a range of items to the map. /// /// Null is not allowed for a Key or a Value /// Range of tuples to add /// Throws ArgumentException if any of the keys already exist /// Throws ArgumentNullException the keys or values are null /// New Map with the items added [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static HashMap addRange(HashMap map, IEnumerable<(K, V)> keyValues) where EqK : Eq => map.AddRange(keyValues); /// /// Atomically adds a range of items to the map. /// /// Null is not allowed for a Key or a Value /// Range of tuples to add /// Throws ArgumentException if any of the keys already exist /// Throws ArgumentNullException the keys or values are null /// New Map with the items added [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static HashMap addRange(HashMap map, IEnumerable> keyValues) where EqK : Eq => map.AddRange(keyValues); /// /// Atomically adds a range of items to the map. If any of the keys exist already /// then they're ignored. /// /// Null is not allowed for a Key or a Value /// Range of tuples to add /// Throws ArgumentNullException the keys or values are null /// New Map with the items added [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static HashMap tryAddRange(HashMap map, IEnumerable> keyValues) where EqK : Eq => map.TryAddRange(keyValues); /// /// Atomically adds a range of items to the map. If any of the keys exist already /// then they're ignored. /// /// Null is not allowed for a Key or a Value /// Range of tuples to add /// Throws ArgumentNullException the keys or values are null /// New Map with the items added [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static HashMap tryAddRange(HashMap map, IEnumerable<(K, V)> keyValues) where EqK : Eq => map.TryAddRange(keyValues); /// /// Atomically adds a range of items to the map. If any of the keys exist already /// then they're ignored. /// /// Null is not allowed for a Key or a Value /// Range of KeyValuePairs to add /// Throws ArgumentNullException the keys or values are null /// New Map with the items added [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static HashMap tryAddRange(HashMap map, IEnumerable> keyValues) where EqK : Eq => map.TryAddRange(keyValues); /// /// Atomically adds a range of items to the map. If any of the keys exist already /// then they're replaced. /// /// Range of tuples to add /// New Map with the items added [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static HashMap addOrUpdateRange(HashMap map, IEnumerable> range) where EqK : Eq => map.AddOrUpdateRange(range); /// /// Atomically adds a range of items to the map. If any of the keys exist already /// then they're replaced. /// /// Range of tuples to add /// New Map with the items added [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static HashMap addOrUpdateRange(HashMap map, IEnumerable<(K, V)> range) where EqK : Eq => map.AddOrUpdateRange(range); /// /// Atomically adds a range of items to the map. If any of the keys exist already /// then they're replaced. /// /// Null is not allowed for a Key or a Value /// Range of KeyValuePairs to add /// Throws ArgumentNullException the keys or values are null /// New Map with the items added [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static HashMap addOrUpdateRange(HashMap map, IEnumerable> range) where EqK : Eq => map.AddOrUpdateRange(range); /// /// Atomically removes an item from the map /// If the key doesn't exists, the request is ignored. /// /// Key /// New map with the item removed [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static HashMap remove(HashMap map, K key) where EqK : Eq => map.Remove(key); /// /// Checks for existence of a key in the map /// /// Key to check /// True if an item with the key supplied is in the map [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool containsKey(HashMap map, K key) where EqK : Eq => map.ContainsKey(key); /// /// Checks for existence of a key in the map /// /// Key to check /// True if an item with the key supplied is in the map [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool contains(HashMap map, KeyValuePair kv) where EqK : Eq => map.Contains(kv.Key, kv.Value); /// /// Checks for existence of a key in the map /// /// Key to check /// True if an item with the key supplied is in the map [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool contains(HashMap map, Tuple kv) where EqK : Eq => map.Contains(kv.Item1, kv.Item2); /// /// Checks for existence of a key in the map /// /// Key to check /// True if an item with the key supplied is in the map [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool contains(HashMap map, (K, V) kv) where EqK : Eq => map.Contains(kv.Item1, kv.Item2); /// /// Atomically updates an existing item /// /// Null is not allowed for a Key or a Value /// Key /// Value /// Throws ArgumentNullException the key or value are null /// New Map with the item added [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static HashMap setItem(HashMap map, K key, V value) where EqK : Eq => map.SetItem(key, value); /// /// Atomically updates an existing item, unless it doesn't exist, in which case /// it is ignored /// /// Null is not allowed for a Key or a Value /// Key /// Value /// Throws ArgumentNullException the value is null /// New Map with the item added [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static HashMap trySetItem(HashMap map, K key, V value) where EqK : Eq => map.TrySetItem(key, value); /// /// Atomically sets an item by first retrieving it, applying a map (Some), and then putting /// it back. Silently fails if the value doesn't exist. /// /// Key to set /// Throws Exception if Some returns null /// delegate to map the existing value to a new one before setting /// New map with the item set [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static HashMap trySetItem(HashMap map, K key, Func Some) where EqK : Eq => map.TrySetItem(key, Some); /// /// Atomically sets a series of items using the Tuples provided /// /// Items to set /// Throws ArgumentException if any of the keys aren't in the map /// New map with the items set [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static HashMap setItems(HashMap map, IEnumerable> items) where EqK : Eq => map.SetItems(items); /// /// Atomically sets a series of items using the Tuples provided /// /// Items to set /// Throws ArgumentException if any of the keys aren't in the map /// New map with the items set [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static HashMap setItems(HashMap map, IEnumerable<(K, V)> items) where EqK : Eq => map.SetItems(items); /// /// Atomically sets a series of items using the KeyValuePairs provided /// /// Items to set /// Throws ArgumentException if any of the keys aren't in the map /// New map with the items set [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static HashMap setItems(HashMap map, IEnumerable> items) where EqK : Eq => map.SetItems(items); /// /// Atomically sets a series of items using the Tuples provided. /// /// Items to set /// Throws ArgumentException if any of the keys aren't in the map /// New map with the items set [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static HashMap trySetItems(HashMap map, IEnumerable> items) where EqK : Eq => map.SetItems(items); /// /// Atomically sets a series of items using the Tuples provided. /// /// Items to set /// Throws ArgumentException if any of the keys aren't in the map /// New map with the items set [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static HashMap trySetItems(HashMap map, IEnumerable<(K, V)> items) where EqK : Eq => map.SetItems(items); /// /// Atomically sets a series of items using the KeyValuePairs provided. If any of the /// items don't exist then they're silently ignored. /// /// Items to set /// New map with the items set [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static HashMap trySetItems(HashMap map, IEnumerable> items) where EqK : Eq => map.TrySetItems(items); /// /// Atomically sets a series of items using the keys provided to find the items /// and the Some delegate maps to a new value. If the items don't exist then /// they're silently ignored. /// /// Keys of items to set /// Function map the existing item to a new one /// New map with the items set [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static HashMap trySetItems(HashMap map, IEnumerable keys, Func Some) where EqK : Eq => map.TrySetItems(keys, Some); /// /// Retrieve a value from the map by key /// /// Key to find /// Found value [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Option find(HashMap map, K key) where EqK : Eq => map.Find(key); /// /// Retrieve a value from the map by key as an enumerable /// /// Key to find /// Found value [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static IEnumerable findSeq(HashMap map, K key) where EqK : Eq => map.FindSeq(key); /// /// Retrieve a value from the map by key and pattern match the /// result. /// /// Key to find /// Found value [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static R find(HashMap map, K key, Func Some, Func None) where EqK : Eq => map.Find(key, Some, None); /// /// Retrieve a value from the map by key, map it to a new value, /// put it back. /// /// Key to find /// New map with the mapped value [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static HashMap setItem(HashMap map, K key, Func mapper) where EqK : Eq => map.SetItem(key, mapper); /// /// Atomically iterate through all key/value pairs in the map (in order) and execute an /// action on each /// /// Action to execute /// Unit [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Unit iter(HashMap map, Action action) where EqK : Eq => map.Iter(action); /// /// Atomically iterate through all key/value pairs in the map (in order) and execute an /// action on each /// /// Action to execute /// Unit [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Unit iter(HashMap map, Action action) where EqK : Eq => map.Iter(action); /// /// Return true if all items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool forall(HashMap map, Func pred) where EqK : Eq => map.ForAll(pred); /// /// Return true if all items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool forall(HashMap map, Func pred) where EqK : Eq => map.ForAll(pred); /// /// Return true if all items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool forall(HashMap map, Func<(K Key, V Value), bool> pred) where EqK : Eq => map.ForAll(pred); /// /// Atomically maps the map to a new map /// /// Mapped items in a new map [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static HashMap map(HashMap map, Func f) where EqK : Eq => map.Select(f); /// /// Atomically maps the map to a new map /// /// Mapped items in a new map [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static HashMap map(HashMap map, Func f) where EqK : Eq => map.Select(f); /// /// Atomically filter out items that return false when a predicate is applied /// /// Predicate /// New map with items filtered [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static HashMap filter(HashMap map, Func predicate) where EqK : Eq => map.Filter(predicate); /// /// Atomically filter out items that return false when a predicate is applied /// /// Predicate /// New map with items filtered [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static HashMap filter(HashMap map, Func predicate) where EqK : Eq => map.Filter(predicate); /// /// Number of items in the map /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int length(HashMap map) where EqK : Eq => map.Count; /// /// Atomically folds all items in the map (in order) using the folder function provided. /// /// State type /// Initial state /// Fold function /// Folded state [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static S fold(HashMap map, S state, Func folder) where EqK : Eq => map.Fold(state, folder); /// /// Return true if *any* items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool exists(HashMap map, Func pred) where EqK : Eq => map.Exists(pred); /// /// Return true if *any* items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool exists(HashMap map, Func<(K Key, V Value), bool> pred) where EqK : Eq => map.Exists(pred); /// /// Return true if *any* items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool exists(HashMap map, Func pred) where EqK : Eq => map.Exists(pred); } ================================================ FILE: LanguageExt.Core/Immutable Collections/HashMap/HashMap.Module.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics.Contracts; using System.Linq; using System.Runtime.CompilerServices; using LanguageExt.ClassInstances; namespace LanguageExt; /// /// Immutable hash-map module /// public static partial class HashMap { /// /// Clears all items from the map /// /// Map to clear /// Functionally equivalent to calling Map.empty as the original structure is untouched /// Empty map [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static HashMap clear(HashMap map) => HashMap.Empty; /// /// Creates a new empty HashMap /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static HashMap empty() => HashMap.Empty; /// /// Create a singleton collection /// /// Single value /// Collection with a single item in it [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static HashMap singleton((K, V) value) => [value]; /// /// Create a singleton collection /// /// Single value /// Collection with a single item in it [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static HashMap singleton(K key, V value) => [(key, value)]; /// /// Creates a new empty HashMap /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static HashMap create() => HashMap.Empty; /// /// Creates a new Map seeded with the keyValues provided /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static HashMap create(Tuple head, params Tuple[] tail) => createRange(head.Cons(tail)); /// /// Creates a new Map seeded with the keyValues provided /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static HashMap create((K, V) head, params (K, V)[] tail) => createRange(head.Cons(tail)); /// /// Creates a new Map seeded with the keyValues provided /// [Pure] public static HashMap create(KeyValuePair head, params KeyValuePair[] tail) => createRange(head.Cons(tail)); /// /// Creates a new Map seeded with the keyValues provided /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static HashMap createRange(IEnumerable> keyValues) => createRange(keyValues.Select(static kv => (kv.Item1, kv.Item2))); /// /// Creates a new Map seeded with the keyValues provided /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static HashMap createRange(IEnumerable<(K, V)> keyValues) => new (new TrieMap, K, V>(keyValues)); /// /// Creates a new Map seeded with the keyValues provided /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static HashMap createRange(ReadOnlySpan<(K, V)> keyValues) => keyValues.IsEmpty ? HashMap.Empty : new(new TrieMap, K, V>(keyValues)); /// /// Creates a new Map seeded with the keyValues provided /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static HashMap createRange(IEnumerable> keyValues) => createRange(keyValues.Select(static kv => (kv.Key, kv.Value))); /// /// Atomically adds a new item to the map /// /// Null is not allowed for a Key or a Value /// Key /// Value /// Throws ArgumentException if the key already exists /// Throws ArgumentNullException the key or value are null /// New Map with the item added [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static HashMap add(HashMap map, K key, V value) => map.Add(key, value); /// /// Atomically adds a new item to the map. /// If the key already exists, then the new item is ignored /// /// Null is not allowed for a Key or a Value /// Key /// Value /// Throws ArgumentNullException the key or value are null /// New Map with the item added [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static HashMap tryAdd(HashMap map, K key, V value) => map.TryAdd(key, value); /// /// Atomically adds a new item to the map. /// If the key already exists, the new item replaces it. /// /// Null is not allowed for a Key or a Value /// Key /// Value /// Throws ArgumentNullException the key or value are null /// New Map with the item added [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static HashMap addOrUpdate(HashMap map, K key, V value) => map.AddOrUpdate(key, value); /// /// Retrieve a value from the map by key, map it to a new value, /// put it back. If it doesn't exist, add a new one based on None result. /// /// Key to find /// Throws Exception if None returns null /// Throws Exception if Some returns null /// New map with the mapped value [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static HashMap addOrUpdate(HashMap map, K key, Func Some, Func None) => map.AddOrUpdate(key, Some, None); /// /// Retrieve a value from the map by key, map it to a new value, /// put it back. If it doesn't exist, add a new one based on None result. /// /// Key to find /// Throws ArgumentNullException if None is null /// Throws Exception if Some returns null /// New map with the mapped value [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static HashMap addOrUpdate(HashMap map, K key, Func Some, V None) => map.AddOrUpdate(key, Some, None); /// /// Atomically adds a range of items to the map. /// /// Null is not allowed for a Key or a Value /// Range of tuples to add /// Throws ArgumentException if any of the keys already exist /// Throws ArgumentNullException the keys or values are null /// New Map with the items added [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static HashMap addRange(HashMap map, IEnumerable> keyValues) => map.AddRange(keyValues); /// /// Atomically adds a range of items to the map. /// /// Null is not allowed for a Key or a Value /// Range of tuples to add /// Throws ArgumentException if any of the keys already exist /// Throws ArgumentNullException the keys or values are null /// New Map with the items added [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static HashMap addRange(HashMap map, IEnumerable<(K, V)> keyValues) => map.AddRange(keyValues); /// /// Atomically adds a range of items to the map. /// /// Null is not allowed for a Key or a Value /// Range of tuples to add /// Throws ArgumentException if any of the keys already exist /// Throws ArgumentNullException the keys or values are null /// New Map with the items added [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static HashMap addRange(HashMap map, IEnumerable> keyValues) => map.AddRange(keyValues); /// /// Atomically adds a range of items to the map. If any of the keys exist already /// then they're ignored. /// /// Null is not allowed for a Key or a Value /// Range of tuples to add /// Throws ArgumentNullException the keys or values are null /// New Map with the items added [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static HashMap tryAddRange(HashMap map, IEnumerable> keyValues) => map.TryAddRange(keyValues); /// /// Atomically adds a range of items to the map. If any of the keys exist already /// then they're ignored. /// /// Null is not allowed for a Key or a Value /// Range of tuples to add /// Throws ArgumentNullException the keys or values are null /// New Map with the items added [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static HashMap tryAddRange(HashMap map, IEnumerable<(K, V)> keyValues) => map.TryAddRange(keyValues); /// /// Atomically adds a range of items to the map. If any of the keys exist already /// then they're ignored. /// /// Null is not allowed for a Key or a Value /// Range of KeyValuePairs to add /// Throws ArgumentNullException the keys or values are null /// New Map with the items added [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static HashMap tryAddRange(HashMap map, IEnumerable> keyValues) => map.TryAddRange(keyValues); /// /// Atomically adds a range of items to the map. If any of the keys exist already /// then they're replaced. /// /// Range of tuples to add /// New Map with the items added [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static HashMap addOrUpdateRange(HashMap map, IEnumerable> range) => map.AddOrUpdateRange(range); /// /// Atomically adds a range of items to the map. If any of the keys exist already /// then they're replaced. /// /// Range of tuples to add /// New Map with the items added [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static HashMap addOrUpdateRange(HashMap map, IEnumerable<(K, V)> range) => map.AddOrUpdateRange(range); /// /// Atomically adds a range of items to the map. If any of the keys exist already /// then they're replaced. /// /// Null is not allowed for a Key or a Value /// Range of KeyValuePairs to add /// Throws ArgumentNullException the keys or values are null /// New Map with the items added [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static HashMap addOrUpdateRange(HashMap map, IEnumerable> range) => map.AddOrUpdateRange(range); /// /// Atomically removes an item from the map /// If the key doesn't exists, the request is ignored. /// /// Key /// New map with the item removed [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static HashMap remove(HashMap map, K key) => map.Remove(key); /// /// Checks for existence of a key in the map /// /// Key to check /// True if an item with the key supplied is in the map [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool containsKey(HashMap map, K key) => map.ContainsKey(key); /// /// Checks for existence of a key in the map /// /// Key to check /// True if an item with the key supplied is in the map [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool contains(HashMap map, KeyValuePair kv) => map.Contains(kv.Key, kv.Value); /// /// Checks for existence of a key in the map /// /// Key to check /// True if an item with the key supplied is in the map [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool contains(HashMap map, Tuple kv) => map.Contains(kv.Item1, kv.Item2); /// /// Checks for existence of a key in the map /// /// Key to check /// True if an item with the key supplied is in the map [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool contains(HashMap map, (K, V) kv) => map.Contains(kv.Item1, kv.Item2); /// /// Atomically updates an existing item /// /// Null is not allowed for a Key or a Value /// Key /// Value /// Throws ArgumentNullException the key or value are null /// New Map with the item added [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static HashMap setItem(HashMap map, K key, V value) => map.SetItem(key, value); /// /// Atomically updates an existing item, unless it doesn't exist, in which case /// it is ignored /// /// Null is not allowed for a Key or a Value /// Key /// Value /// Throws ArgumentNullException the value is null /// New Map with the item added [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static HashMap trySetItem(HashMap map, K key, V value) => map.TrySetItem(key, value); /// /// Atomically sets an item by first retrieving it, applying a map (Some), and then putting /// it back. Silently fails if the value doesn't exist. /// /// Key to set /// Throws Exception if Some returns null /// delegate to map the existing value to a new one before setting /// New map with the item set [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static HashMap trySetItem(HashMap map, K key, Func Some) => map.TrySetItem(key, Some); /// /// Atomically sets a series of items using the Tuples provided /// /// Items to set /// Throws ArgumentException if any of the keys aren't in the map /// New map with the items set [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static HashMap setItems(HashMap map, IEnumerable> items) => map.SetItems(items); /// /// Atomically sets a series of items using the Tuples provided /// /// Items to set /// Throws ArgumentException if any of the keys aren't in the map /// New map with the items set [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static HashMap setItems(HashMap map, IEnumerable<(K, V)> items) => map.SetItems(items); /// /// Atomically sets a series of items using the KeyValuePairs provided /// /// Items to set /// Throws ArgumentException if any of the keys aren't in the map /// New map with the items set [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static HashMap setItems(HashMap map, IEnumerable> items) => map.SetItems(items); /// /// Atomically sets a series of items using the Tuples provided. /// /// Items to set /// Throws ArgumentException if any of the keys aren't in the map /// New map with the items set [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static HashMap trySetItems(HashMap map, IEnumerable> items) => map.SetItems(items); /// /// Atomically sets a series of items using the Tuples provided. /// /// Items to set /// Throws ArgumentException if any of the keys aren't in the map /// New map with the items set [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static HashMap trySetItems(HashMap map, IEnumerable<(K, V)> items) => map.SetItems(items); /// /// Atomically sets a series of items using the KeyValuePairs provided. If any of the /// items don't exist then they're silently ignored. /// /// Items to set /// New map with the items set [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static HashMap trySetItems(HashMap map, IEnumerable> items) => map.TrySetItems(items); /// /// Atomically sets a series of items using the keys provided to find the items /// and the Some delegate maps to a new value. If the items don't exist then /// they're silently ignored. /// /// Keys of items to set /// Function map the existing item to a new one /// New map with the items set [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static HashMap trySetItems(HashMap map, IEnumerable keys, Func Some) => map.TrySetItems(keys, Some); /// /// Retrieve a value from the map by key /// /// Key to find /// Found value [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Option find(HashMap map, K key) => map.Find(key); /// /// Retrieve a value from the map by key and pattern match the /// result. /// /// Key to find /// Found value [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static R find(HashMap map, K key, Func Some, Func None) => map.Find(key, Some, None); /// /// Retrieve a value from the map by key, map it to a new value, /// put it back. /// /// Key to find /// New map with the mapped value [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static HashMap setItem(HashMap map, K key, Func mapper) => map.SetItem(key, mapper); /// /// Atomically iterate through all key/value pairs in the map (in order) and execute an /// action on each /// /// Action to execute /// Unit [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Unit iter(HashMap map, Action action) => map.Iter(action); /// /// Atomically iterate through all key/value pairs in the map (in order) and execute an /// action on each /// /// Action to execute /// Unit [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Unit iter(HashMap map, Action action) => map.Iter(action); /// /// Return true if all items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool forall(HashMap map, Func pred) => map.ForAll(pred); /// /// Return true if all items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool forall(HashMap map, Func pred) => map.ForAll(pred); /// /// Return true if all items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool forall(HashMap map, Func<(K Key, V Value), bool> pred) => map.ForAll(pred); /// /// Atomically maps the map to a new map /// /// Mapped items in a new map [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static HashMap map(HashMap map, Func f) => map.Select(f); /// /// Atomically maps the map to a new map /// /// Mapped items in a new map [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static HashMap map(HashMap map, Func f) => map.Select(f); /// /// Atomically filter out items that return false when a predicate is applied /// /// Predicate /// New map with items filtered [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static HashMap filter(HashMap map, Func predicate) => map.Filter(predicate); /// /// Atomically filter out items that return false when a predicate is applied /// /// Predicate /// New map with items filtered [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static HashMap filter(HashMap map, Func predicate) => map.Filter(predicate); } ================================================ FILE: LanguageExt.Core/Immutable Collections/HashMap/HashMap.Prelude.mapapply.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public static partial class Prelude { /// /// Functor map operation /// /// /// Unwraps the value within the functor, passes it to the map function `f` provided, and /// then takes the mapped value and wraps it back up into a new functor. /// /// Functor to map /// Mapping function /// Mapped functor public static HashMap map(Func f, K, A> ma) => Functor.map(f, ma).As(); /// /// Functor map operation /// /// /// Unwraps the value within the functor, passes it to the map function `f` provided, and /// then takes the mapped value and wraps it back up into a new functor. /// /// Functor to map /// Mapping function /// Mapped functor public static HashMap map(this Func f, K, A> ma) where EqKey : Eq => Functor.map(f, ma).As(); } ================================================ FILE: LanguageExt.Core/Immutable Collections/HashMap/HashMap.Trait.Implementations.Eq.cs ================================================ using System; using System.Linq; using LanguageExt.Traits; namespace LanguageExt; public partial class HashMapEq : Foldable>, MonoidK>, Functor> where EqKey : Eq { static int Foldable>.Count(K, A> ta) => ta.As().Count; static bool Foldable>.IsEmpty(K, A> ta) => ta.As().IsEmpty; static K, A> SemigroupK>.Combine(K, A> lhs, K, A> rhs) => lhs.As() + rhs.As(); static K, A> MonoidK>.Empty() => HashMap.Empty; public static K, B> Map(Func f, K, A> ma) => new HashMap(ma.As().Value.Select(kv => (kv.Key, f(kv.Value)))); static Fold Foldable>.FoldStep(K, A> ta, S initialState) { var iter = ta.As().Values.GetIterator(); return go(initialState); Fold go(S state) { if (iter.IsEmpty) { return Fold.Done(state); } else { iter = iter.Tail.Split(); return Fold.Loop(state, iter.Head, go); } } } static Fold Foldable>.FoldStepBack(K, A> ta, S initialState) => // Order is undefined in a HashMap, so reversing the order makes no sense, // so let's take the most efficient option: ta.FoldStep(initialState); } ================================================ FILE: LanguageExt.Core/Immutable Collections/HashMap/HashMap.Trait.Implementations.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using LanguageExt.Traits; namespace LanguageExt; public partial class HashMap : Foldable>, Functor>, MonoidK> { static K, B> Functor>.Map(Func f, K, A> ma) { return new HashMap(Go()); IEnumerable<(Key, B)> Go() { foreach (var x in ma.As()) { yield return (x.Key, f(x.Value)); } } } static int Foldable>.Count(K, A> ta) => ta.As().Count; static bool Foldable>.IsEmpty(K, A> ta) => ta.As().IsEmpty; static K, A> SemigroupK>.Combine(K, A> lhs, K, A> rhs) => lhs.As() + rhs.As(); static K, A> MonoidK>.Empty() => HashMap.Empty; static Fold Foldable>.FoldStep(K, A> ta, S initialState) { // ReSharper disable once GenericEnumeratorNotDisposed var iter = ta.As().Values.GetEnumerator(); return go(initialState); Fold go(S state) => iter.MoveNext() ? Fold.Loop(state, iter.Current, go) : Fold.Done(state); } static Fold Foldable>.FoldStepBack(K, A> ta, S initialState) => // Order is undefined in a HashMap, so reversing the order makes no sense, // so let's take the most efficient option: ta.FoldStep(initialState); } ================================================ FILE: LanguageExt.Core/Immutable Collections/HashMap/HashMap.cs ================================================ using LanguageExt.ClassInstances; using static LanguageExt.Prelude; using System; using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics.Contracts; using LanguageExt.Traits; using System.Linq; using System.Numerics; using System.Runtime.CompilerServices; #pragma warning disable CS0693 // Type parameter has the same name as the type parameter from outer type namespace LanguageExt; /// /// Unsorted immutable hash-map /// /// Key type /// Value [CollectionBuilder(typeof(HashMap), nameof(HashMap.createRange))] public readonly struct HashMap : IReadOnlyDictionary, IEnumerable<(K Key, V Value)>, IEquatable>, IEqualityOperators, HashMap, bool>, IAdditionOperators, HashMap, HashMap>, ISubtractionOperators, HashMap, HashMap>, IAdditiveIdentity, HashMap>, Monoid>, K, V> { [Pure] public static HashMap Empty { get; } = new(TrieMap, K, V>.Empty); readonly TrieMap, K, V> value; internal TrieMap, K, V> Value => value ?? TrieMap, K, V>.Empty; [MethodImpl(MethodImplOptions.AggressiveInlining)] internal HashMap(TrieMap, K, V> value) => this.value = value; [MethodImpl(MethodImplOptions.AggressiveInlining)] public HashMap(IEnumerable<(K Key, V Value)> items) : this(items, true) { } [MethodImpl(MethodImplOptions.AggressiveInlining)] public HashMap(IEnumerable<(K Key, V Value)> items, bool tryAdd) => value = new TrieMap, K, V>(items, tryAdd); /// /// Item at index lens /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Lens, V> item(K key) => Lens, V>.New( Get: la => la[key], Set: a => la => la.AddOrUpdate(key, a) ); /// /// Item or none at index lens /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Lens, Option> itemOrNone(K key) => Lens, Option>.New( Get: la => la.Find(key), Set: a => la => a.Match(Some: x => la.AddOrUpdate(key, x), None: () => la.Remove(key)) ); /// /// Lens map /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Lens, HashMap> map(Lens lens) => Lens, HashMap>.New( Get: la => la.Map(lens.Get), Set: lb => la => { foreach (var item in lb) { la = la.AddOrUpdate(item.Key, lens.Set(item.Value, la[item.Key])); } return la; }); [MethodImpl(MethodImplOptions.AggressiveInlining)] static HashMap Wrap(TrieMap, K, V> value) => new (value); [MethodImpl(MethodImplOptions.AggressiveInlining)] static HashMap Wrap(TrieMap, K, U> value) => new (value); /// /// 'this' accessor /// /// Key /// Optional value [Pure] public V this[K key] { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => Value[key]; } /// /// Is the map empty /// [Pure] public bool IsEmpty { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => value?.IsEmpty ?? true; } /// /// Number of items in the map /// [Pure] public int Count { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => value?.Count ?? 0; } /// /// Atomically filter out items that return false when a predicate is applied /// /// Predicate /// New map with items filtered [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public HashMap Filter(Func pred) => Wrap(Value.Filter(pred)); /// /// Atomically filter out items that return false when a predicate is applied /// /// Predicate /// New map with items filtered [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public HashMap Filter(Func pred) => Wrap(Value.Filter(pred)); /// /// Atomically maps the map to a new map /// /// Mapped items in a new map [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public HashMap Map(Func mapper) => Wrap(Value.Map(mapper)); /// /// Atomically maps the map to a new map /// /// Mapped items in a new map [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public HashMap Map(Func mapper) => Wrap(Value.Map(mapper)); /// /// Atomically adds a new item to the map /// /// Null is not allowed for a Key or a Value /// Key /// Value /// Throws ArgumentException if the key already exists /// Throws ArgumentNullException the key or value are null /// New Map with the item added [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public HashMap Add(K key, V valueToAdd) => Wrap(Value.Add(key, valueToAdd)); /// /// Atomically adds a new item to the map. /// If the key already exists, then the new item is ignored /// /// Null is not allowed for a Key or a Value /// Key /// Value /// Throws ArgumentNullException the key or value are null /// New Map with the item added [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public HashMap TryAdd(K key, V valueToAdd) => Wrap(Value.TryAdd(key, valueToAdd)); /// /// Atomically adds a new item to the map. /// If the key already exists, the new item replaces it. /// /// Null is not allowed for a Key or a Value /// Key /// Value /// Throws ArgumentNullException the key or value are null /// New Map with the item added [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public HashMap AddOrUpdate(K key, V value) => Wrap(Value.AddOrUpdate(key, value)); /// /// Retrieve a value from the map by key, map it to a new value, /// put it back. If it doesn't exist, add a new one based on None result. /// /// Key to find /// Throws Exception if None returns null /// Throws Exception if Some returns null /// New map with the mapped value [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public HashMap AddOrUpdate(K key, Func Some, Func None) => Wrap(Value.AddOrUpdate(key, Some, None)); /// /// Retrieve a value from the map by key, map it to a new value, /// put it back. If it doesn't exist, add a new one based on None result. /// /// Key to find /// Throws ArgumentNullException if None is null /// Throws Exception if Some returns null /// New map with the mapped value [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public HashMap AddOrUpdate(K key, Func Some, V None) => Wrap(Value.AddOrUpdate(key, Some, None)); /// /// Atomically adds a range of items to the map. /// /// Null is not allowed for a Key or a Value /// Range of tuples to add /// Throws ArgumentException if any of the keys already exist /// Throws ArgumentNullException the keys or values are null /// New Map with the items added [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public HashMap AddRange(IEnumerable> range) => Wrap(Value.AddRange(range)); /// /// Atomically adds a range of items to the map. /// /// Null is not allowed for a Key or a Value /// Range of tuples to add /// Throws ArgumentException if any of the keys already exist /// Throws ArgumentNullException the keys or values are null /// New Map with the items added [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public HashMap AddRange(IEnumerable<(K Key, V Value)> range) => Wrap(Value.AddRange(range)); /// /// Atomically adds a range of items to the map. If any of the keys exist already /// then they're ignored. /// /// Null is not allowed for a Key or a Value /// Range of tuples to add /// Throws ArgumentNullException the keys or values are null /// New Map with the items added [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public HashMap TryAddRange(IEnumerable> range) => Wrap(Value.TryAddRange(range)); /// /// Atomically adds a range of items to the map. If any of the keys exist already /// then they're ignored. /// /// Null is not allowed for a Key or a Value /// Range of tuples to add /// Throws ArgumentNullException the keys or values are null /// New Map with the items added [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public HashMap TryAddRange(IEnumerable<(K Key, V Value)> range) => Wrap(Value.TryAddRange(range)); /// /// Atomically adds a range of items to the map. If any of the keys exist already /// then they're ignored. /// /// Null is not allowed for a Key or a Value /// Range of KeyValuePairs to add /// Throws ArgumentNullException the keys or values are null /// New Map with the items added [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public HashMap TryAddRange(IEnumerable> range) => Wrap(Value.TryAddRange(range)); /// /// Atomically adds a range of items to the map. If any of the keys exist already /// then they're replaced. /// /// Null is not allowed for a Key or a Value /// Range of tuples to add /// Throws ArgumentNullException the keys or values are null /// New Map with the items added [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public HashMap AddOrUpdateRange(IEnumerable> range) => Wrap(Value.AddOrUpdateRange(range)); /// /// Atomically adds a range of items to the map. If any of the keys exist already /// then they're replaced. /// /// Null is not allowed for a Key or a Value /// Range of tuples to add /// Throws ArgumentNullException the keys or values are null /// New Map with the items added [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public HashMap AddOrUpdateRange(IEnumerable<(K Key, V Value)> range) => Wrap(Value.AddOrUpdateRange(range)); /// /// Atomically adds a range of items to the map. If any of the keys exist already /// then they're replaced. /// /// Null is not allowed for a Key or a Value /// Range of KeyValuePairs to add /// Throws ArgumentNullException the keys or values are null /// New Map with the items added [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public HashMap AddOrUpdateRange(IEnumerable> range) => Wrap(Value.AddOrUpdateRange(range)); /// /// Atomically removes an item from the map /// If the key doesn't exists, the request is ignored. /// /// Key /// New map with the item removed [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public HashMap Remove(K key) => Wrap(Value.Remove(key)); /// /// Retrieve a value from the map by key /// /// Key to find /// Found value [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Option Find(K key) => Value.Find(key); /// /// Retrieve a value from the map by key and pattern match the /// result. /// /// Key to find /// Found value [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public R Find(K key, Func Some, Func None) => Value.Find(key, Some, None); /// /// Try to find the key in the map, if it doesn't exist, add a new /// item by invoking the delegate provided. /// /// Key to find /// Delegate to get the value /// Updated map and added value [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public (HashMap Map, V Value) FindOrAdd(K key, Func None) => Value.FindOrAdd(key, None).Map((x, y) => (Wrap(x), y)); /// /// Try to find the key in the map, if it doesn't exist, add a new /// item provided. /// /// Key to find /// value /// Updated map and added value [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public (HashMap, V Value) FindOrAdd(K key, V valueToFindOrAdd) => Value.FindOrAdd(key, valueToFindOrAdd).Map((x, y) => (Wrap(x), y)); /// /// Try to find the key in the map, if it doesn't exist, add a new /// item by invoking the delegate provided. /// /// Key to find /// Delegate to get the value /// Updated map and added value [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public (HashMap Map, Option Value) FindOrMaybeAdd(K key, Func> None) => Value.FindOrMaybeAdd(key, None).Map((x, y) => (Wrap(x), y)); /// /// Try to find the key in the map, if it doesn't exist, add a new /// item by invoking the delegate provided. /// /// Key to find /// Delegate to get the value /// Updated map and added value [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public (HashMap Map, Option Value) FindOrMaybeAdd(K key, Option None) => Value.FindOrMaybeAdd(key, None).Map((x, y) => (Wrap(x), y)); /// /// Atomically updates an existing item /// /// Null is not allowed for a Key or a Value /// Key /// Value /// Throws ArgumentNullException the key or value are null /// New Map with the item added [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public HashMap SetItem(K key, V valueToSet) => Wrap(Value.SetItem(key, valueToSet)); /// /// Retrieve a value from the map by key, map it to a new value, /// put it back. /// /// Key to set /// Throws ArgumentException if the item isn't found /// Throws Exception if Some returns null /// New map with the mapped value [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public HashMap SetItem(K key, Func Some) => Wrap(Value.SetItem(key, Some)); /// /// Atomically updates an existing item, unless it doesn't exist, in which case /// it is ignored /// /// Null is not allowed for a Key or a Value /// Key /// Value /// Throws ArgumentNullException the value is null /// New Map with the item added [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public HashMap TrySetItem(K key, V valueToSet) => Wrap(Value.TrySetItem(key, valueToSet)); /// /// Atomically sets an item by first retrieving it, applying a map, and then putting it back. /// Silently fails if the value doesn't exist /// /// Key to set /// delegate to map the existing value to a new one before setting /// Throws Exception if Some returns null /// Throws ArgumentNullException the key or value are null /// New map with the item set [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public HashMap TrySetItem(K key, Func Some) => Wrap(Value.TrySetItem(key, Some)); /// /// Checks for existence of a key in the map /// /// Key to check /// True if an item with the key supplied is in the map [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool ContainsKey(K key) => Value.ContainsKey(key); /// /// Checks for existence of a key and value in the map /// /// Key to check /// Value to check /// True if an item with the key supplied is in the map [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Contains(K key, V value) => Value.Contains(key, value); /// /// Checks for existence of a value in the map /// /// Value to check /// True if an item with the value supplied is in the map [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Contains(V value) where EqV : Eq => Value.Contains(value); /// /// Checks for existence of a key and value in the map /// /// Key to check /// Value to check /// True if an item with the key supplied is in the map [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Contains(K key, V value) where EqV : Eq => Value.Contains(key, value); /// /// Atomically adds a range of items to the map /// /// Range of KeyValuePairs to add /// Throws ArgumentException if any of the keys already exist /// New Map with the items added [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public HashMap AddRange(IEnumerable> pairs) => Wrap(Value.AddRange(pairs)); /// /// Atomically sets a series of items using the KeyValuePairs provided /// /// Items to set /// Throws ArgumentException if any of the keys aren't in the map /// New map with the items set [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public HashMap SetItems(IEnumerable> items) => Wrap(Value.SetItems(items)); /// /// Atomically sets a series of items using the Tuples provided. /// /// Items to set /// Throws ArgumentException if any of the keys aren't in the map /// New map with the items set [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public HashMap SetItems(IEnumerable> items) => Wrap(Value.SetItems(items)); /// /// Atomically sets a series of items using the Tuples provided. /// /// Items to set /// Throws ArgumentException if any of the keys aren't in the map /// New map with the items set [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public HashMap SetItems(IEnumerable<(K Key, V Value)> items) => Wrap(Value.SetItems(items)); /// /// Atomically sets a series of items using the KeyValuePairs provided. If any of the /// items don't exist then they're silently ignored. /// /// Items to set /// New map with the items set [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public HashMap TrySetItems(IEnumerable> items) => Wrap(Value.TrySetItems(items)); /// /// Atomically sets a series of items using the Tuples provided If any of the /// items don't exist then they're silently ignored. /// /// Items to set /// New map with the items set [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public HashMap TrySetItems(IEnumerable> items) => Wrap(Value.TrySetItems(items)); /// /// Atomically sets a series of items using the Tuples provided If any of the /// items don't exist then they're silently ignored. /// /// Items to set /// New map with the items set [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public HashMap TrySetItems(IEnumerable<(K Key, V Value)> items) => Wrap(Value.TrySetItems(items)); /// /// Atomically sets a series of items using the keys provided to find the items /// and the Some delegate maps to a new value. If the items don't exist then /// they're silently ignored. /// /// Keys of items to set /// Function map the existing item to a new one /// New map with the items set [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public HashMap TrySetItems(IEnumerable keys, Func Some) => Wrap(Value.TrySetItems(keys, Some)); /// /// Atomically removes a set of keys from the map /// /// Keys to remove /// New map with the items removed [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public HashMap RemoveRange(IEnumerable keys) => Wrap(Value.RemoveRange(keys)); /// /// Returns true if a Key/Value pair exists in the map /// /// Pair to find /// True if exists, false otherwise [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Contains((K Key, V Value) pair) => Value.Contains(pair.Key, pair.Value); /// /// Enumerable of map keys /// [Pure] public Iterable Keys { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => Value.Keys; } /// /// Enumerable of map values /// [Pure] public Iterable Values { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => Value.Values; } /// /// Map the map the a dictionary /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public IDictionary ToDictionary( Func<(K Key, V Value), KR> keySelector, Func<(K Key, V Value), VR> valueSelector) where KR : notnull => AsIterable().ToDictionary(x => keySelector(x), x => valueSelector(x)); /// /// GetEnumerator - IEnumerable interface /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public IEnumerator<(K Key, V Value)> GetEnumerator() => Value.GetEnumerator(); /// /// GetEnumerator - IEnumerable interface /// [MethodImpl(MethodImplOptions.AggressiveInlining)] IEnumerator IEnumerable.GetEnumerator() => // ReSharper disable once NotDisposedResourceIsReturned Value.GetEnumerator(); /// /// Allocation free conversion to a TrackingHashMap /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public HashMap ToTrackingHashMap() => new (value); /// /// Format the collection as `[(key: value), (key: value), (key: value), ...]` /// The elipsis is used for collections over 50 items /// To get a formatted string with all the items, use `ToFullString` /// or `ToFullArrayString`. /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public override string ToString() => CollectionFormat.ToShortArrayString(AsIterable().Map(kv => $"({kv.Key}: {kv.Value})"), Count); /// /// Format the collection as `(key: value), (key: value), (key: value), ...` /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public string ToFullString(string separator = ", ") => CollectionFormat.ToFullString(AsIterable().Map(kv => $"({kv.Key}: {kv.Value})"), separator); /// /// Format the collection as `[(key: value), (key: value), (key: value), ...]` /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public string ToFullArrayString(string separator = ", ") => CollectionFormat.ToFullArrayString(AsIterable().Map(kv => $"({kv.Key}: {kv.Value})"), separator); [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Iterable<(K Key, V Value)> AsIterable() => Value.AsIterable(); /// /// Implicit conversion from an untyped empty list /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator HashMap(SeqEmpty _) => Empty; /// /// Equality of keys and values with `EqDefault〈V〉` used for values /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(HashMap lhs, HashMap rhs) => lhs.Equals(rhs); /// /// In-equality of keys and values with `EqDefault〈V〉` used for values /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(HashMap lhs, HashMap rhs) => !(lhs == rhs); [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static HashMap operator +(HashMap lhs, HashMap rhs) => lhs.Combine(rhs); [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public HashMap Combine(HashMap rhs) => Wrap(Value.Append(rhs.Value)); [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static HashMap operator -(HashMap lhs, HashMap rhs) => lhs.Subtract(rhs); [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public HashMap Subtract(HashMap rhs) => Wrap(Value.Subtract(rhs.Value)); /// /// Returns True if 'other' is a proper subset of this set /// /// True if 'other' is a proper subset of this set [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool IsProperSubsetOf(IEnumerable<(K Key, V Value)> other) => Value.IsProperSubsetOf(other); /// /// Returns True if 'other' is a proper subset of this set /// /// True if 'other' is a proper subset of this set [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool IsProperSubsetOf(IEnumerable other) => Value.IsProperSubsetOf(other); /// /// Returns True if 'other' is a proper superset of this set /// /// True if 'other' is a proper superset of this set [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool IsProperSupersetOf(IEnumerable<(K Key, V Value)> other) => Value.IsProperSupersetOf(other); /// /// Returns True if 'other' is a proper superset of this set /// /// True if 'other' is a proper superset of this set [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool IsProperSupersetOf(IEnumerable other) => Value.IsProperSupersetOf(other); /// /// Returns True if 'other' is a superset of this set /// /// True if 'other' is a superset of this set [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool IsSubsetOf(IEnumerable<(K Key, V Value)> other) => Value.IsSubsetOf(other); /// /// Returns True if 'other' is a superset of this set /// /// True if 'other' is a superset of this set [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool IsSubsetOf(IEnumerable other) => Value.IsSubsetOf(other); /// /// Returns True if 'other' is a superset of this set /// /// True if 'other' is a superset of this set [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool IsSubsetOf(HashMap other) => Value.IsSubsetOf(other.Value); /// /// Returns True if 'other' is a superset of this set /// /// True if 'other' is a superset of this set [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool IsSupersetOf(IEnumerable<(K Key, V Value)> other) => Value.IsSupersetOf(other); /// /// Returns True if 'other' is a superset of this set /// /// True if 'other' is a superset of this set [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool IsSupersetOf(IEnumerable rhs) => Value.IsSupersetOf(rhs); /// /// Returns the elements that are in both this and other /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public HashMap Intersect(IEnumerable rhs) => Wrap(Value.Intersect(rhs)); /// /// Returns the elements that are in both this and other /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public HashMap Intersect(IEnumerable<(K Key, V Value)> rhs) => Wrap(Value.Intersect(rhs)); /// /// Returns the elements that are in both this and other /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public HashMap Intersect(IEnumerable<(K Key, V Value)> rhs, WhenMatched Merge) => Wrap(Value.Intersect(rhs, Merge)); /// /// Returns True if other overlaps this set /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Overlaps(IEnumerable<(K Key, V Value)> other) => Value.Overlaps(other); /// /// Returns True if other overlaps this set /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Overlaps(IEnumerable other) => Value.Overlaps(other); /// /// Returns this - other. Only the items in this that are not in /// other will be returned. /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public HashMap Except(IEnumerable rhs) => Wrap(Value.Except(rhs)); /// /// Returns this - other. Only the items in this that are not in /// other will be returned. /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public HashMap Except(IEnumerable<(K Key, V Value)> rhs) => Wrap(Value.Except(rhs)); /// /// Only items that are in one set or the other will be returned. /// If an item is in both, it is dropped. /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public HashMap SymmetricExcept(HashMap rhs) => Wrap(Value.SymmetricExcept(rhs.Value)); /// /// Only items that are in one set or the other will be returned. /// If an item is in both, it is dropped. /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public HashMap SymmetricExcept(IEnumerable<(K Key, V Value)> rhs) => Wrap(Value.SymmetricExcept(rhs)); /// /// Finds the union of two sets and produces a new set with /// the results /// /// Other set to union with /// A set which contains all items from both sets [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public HashMap Union(IEnumerable<(K, V)> rhs) => this.TryAddRange(rhs); /// /// Union two maps. /// /// /// The `WhenMatched` merge function is called when keys are present in both map to allow resolving to a /// sensible value. /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public HashMap Union(IEnumerable<(K, V)> other, WhenMatched Merge) => Wrap(Value.Union(other, static (_, v) => v, static (_, v) => v, Merge)); /// /// Union two maps. /// /// /// The `WhenMatched` merge function is called when keys are present in both map to allow resolving to a /// sensible value. /// /// /// The `WhenMissing` function is called when there is a key in the right-hand side, but not the left-hand-side. /// This allows the `V2` value-type to be mapped to the target `V` value-type. /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public HashMap Union(IEnumerable<(K, W)> other, WhenMissing MapRight, WhenMatched Merge) => Wrap(Value.Union(other, static (_, v) => v, MapRight, Merge)); /// /// Union two maps. /// /// /// The `WhenMatched` merge function is called when keys are present in both map to allow resolving to a /// sensible value. /// /// /// The `WhenMissing` function is called when there is a key in the left-hand side, but not the right-hand-side. /// This allows the `V` value-type to be mapped to the target `V2` value-type. /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public HashMap Union(IEnumerable<(K, W)> other, WhenMissing MapLeft, WhenMatched Merge) => Wrap(Value.Union(other, MapLeft, static (_, v) => v, Merge)); /// /// Union two maps. /// /// /// The `WhenMatched` merge function is called when keys are present in both map to allow resolving to a /// sensible value. /// /// /// The `WhenMissing MapLeft` function is called when there is a key in the left-hand side, but not the /// right-hand-side. This allows the `V` value-type to be mapped to the target `R` value-type. /// /// /// The `WhenMissing MapRight` function is called when there is a key in the right-hand side, but not the /// left-hand-side. This allows the `V2` value-type to be mapped to the target `R` value-type. /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public HashMap Union(IEnumerable<(K, W)> other, WhenMissing MapLeft, WhenMissing MapRight, WhenMatched Merge) => Wrap(Value.Union(other, MapLeft, MapRight, Merge)); /// /// Equality of keys and values with `EqDefault〈V〉` used for values /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public override bool Equals(object? obj) => obj is HashMap hm && Equals(hm); /// /// Equality of keys and values with `EqDefault〈V〉` used for values /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(HashMap other) => Value.Equals>(other.Value); /// /// Equality of keys and values with `EqV` used for values /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(HashMap other) where EqV : Eq => Value.Equals(other.Value); /// /// Equality of keys only /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool EqualsKeys(HashMap other) => Value.Equals>(other.Value); [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public override int GetHashCode() => Value.GetHashCode(); /// /// Impure iteration of the bound values in the structure /// /// /// Returns the original unmodified structure /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public HashMap Do(Action f) { this.Iter(f); return this; } /// /// Atomically maps the map to a new map /// /// Mapped items in a new map [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public HashMap Select(Func mapper) => Map(mapper); /// /// Atomically maps the map to a new map /// /// Mapped items in a new map [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public HashMap Select(Func mapper) => Map(mapper); /// /// Atomically filter out items that return false when a predicate is applied /// /// Predicate /// New map with items filtered [Pure] [EditorBrowsable(EditorBrowsableState.Never)] [MethodImpl(MethodImplOptions.AggressiveInlining)] public HashMap Where(Func pred) => Filter(pred); /// /// Atomically filter out items that return false when a predicate is applied /// /// Predicate /// New map with items filtered [Pure] [EditorBrowsable(EditorBrowsableState.Never)] [MethodImpl(MethodImplOptions.AggressiveInlining)] public HashMap Where(Func pred) => Filter(pred); /// /// Return true if all items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool ForAll(Func pred) { foreach (var item in AsIterable()) { if (!pred(item.Key, item.Value)) return false; } return true; } /// /// Return true if all items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool ForAll(Func<(K Key, V Value), bool> pred) => AsIterable().Map(kv => (kv.Key, kv.Value)).ForAll(pred); /// /// Return true if *any* items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Exists(Func pred) { foreach (var item in AsIterable()) { if (pred(item.Key, item.Value)) return true; } return false; } /// /// Return true if *any* items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Exists(Func<(K Key, V Value), bool> pred) => AsIterable().Map(kv => (kv.Key, kv.Value)).Exists(pred); /// /// Atomically iterate through all key/value pairs in the map (in order) and execute an /// action on each /// /// Action to execute /// Unit [MethodImpl(MethodImplOptions.AggressiveInlining)] public Unit Iter(Action action) { foreach (var item in this) { action(item.Key, item.Value); } return unit; } /// /// Atomically iterate through all key/value pairs (as tuples) in the map (in order) /// and execute an action on each /// /// Action to execute /// Unit [MethodImpl(MethodImplOptions.AggressiveInlining)] public Unit Iter(Action<(K Key, V Value)> action) { foreach (var item in this) { action(item); } return unit; } [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] IEnumerator> IEnumerable>.GetEnumerator() => AsIterable().Map(p => new KeyValuePair(p.Key, p.Value)).GetEnumerator(); [Pure] IEnumerable IReadOnlyDictionary.Keys => Keys; [Pure] IEnumerable IReadOnlyDictionary.Values => Values; [Pure] public bool TryGetValue(K key, out V value) { var v = Find(key); if (v.IsSome) { value = (V)v; return true; } else { value = default!; return false; } } /// /// Get a IReadOnlyDictionary for this map. No mapping is required, so this is very fast. /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public IReadOnlyDictionary ToReadOnlyDictionary() => this; [Pure] [Obsolete(Change.UseCollectionIntialiser)] public static implicit operator HashMap(ValueTuple<(K, V)> items) => new (new[] { items.Item1 }); [Pure] [Obsolete(Change.UseCollectionIntialiser)] public static implicit operator HashMap(((K, V), (K, V)) items) => new (new[] { items.Item1, items.Item2 }); [Pure] [Obsolete(Change.UseCollectionIntialiser)] public static implicit operator HashMap(((K, V), (K, V), (K, V)) items) => new (new[] { items.Item1, items.Item2, items.Item3 }); [Pure] [Obsolete(Change.UseCollectionIntialiser)] public static implicit operator HashMap(((K, V), (K, V), (K, V), (K, V)) items) => new (new[] { items.Item1, items.Item2, items.Item3, items.Item4 }); [Pure] [Obsolete(Change.UseCollectionIntialiser)] public static implicit operator HashMap(((K, V), (K, V), (K, V), (K, V), (K, V)) items) => new (new[] { items.Item1, items.Item2, items.Item3, items.Item4, items.Item5 }); [Pure] [Obsolete(Change.UseCollectionIntialiser)] public static implicit operator HashMap(((K, V), (K, V), (K, V), (K, V), (K, V), (K, V)) items) => new (new[] { items.Item1, items.Item2, items.Item3, items.Item4, items.Item5, items.Item6 }); [Pure] [Obsolete(Change.UseCollectionIntialiser)] public static implicit operator HashMap(((K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V)) items) => new (new[] { items.Item1, items.Item2, items.Item3, items.Item4, items.Item5, items.Item6, items.Item7 }); [Pure] [Obsolete(Change.UseCollectionIntialiser)] public static implicit operator HashMap(((K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V)) items) => new (new[] { items.Item1, items.Item2, items.Item3, items.Item4, items.Item5, items.Item6, items.Item7, items.Item8 }); [Pure] [Obsolete(Change.UseCollectionIntialiser)] public static implicit operator HashMap(((K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V)) items) => new (new[] { items.Item1, items.Item2, items.Item3, items.Item4, items.Item5, items.Item6, items.Item7, items.Item8, items.Item9 }); [Pure] [Obsolete(Change.UseCollectionIntialiser)] public static implicit operator HashMap(((K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V)) items) => new (new[] { items.Item1, items.Item2, items.Item3, items.Item4, items.Item5, items.Item6, items.Item7, items.Item8, items.Item9, items.Item10 }); [Pure] [Obsolete(Change.UseCollectionIntialiser)] public static implicit operator HashMap(((K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V)) items) => new (new[] { items.Item1, items.Item2, items.Item3, items.Item4, items.Item5, items.Item6, items.Item7, items.Item8, items.Item9, items.Item10, items.Item11 }); [Pure] [Obsolete(Change.UseCollectionIntialiser)] public static implicit operator HashMap(((K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V)) items) => new (new[] { items.Item1, items.Item2, items.Item3, items.Item4, items.Item5, items.Item6, items.Item7, items.Item8, items.Item9, items.Item10, items.Item11, items.Item12 }); [Pure] [Obsolete(Change.UseCollectionIntialiser)] public static implicit operator HashMap(((K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V)) items) => new (new[] { items.Item1, items.Item2, items.Item3, items.Item4, items.Item5, items.Item6, items.Item7, items.Item8, items.Item9, items.Item10, items.Item11, items.Item12, items.Item13 }); [Pure] [Obsolete(Change.UseCollectionIntialiser)] public static implicit operator HashMap(((K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V)) items) => new (new[] { items.Item1, items.Item2, items.Item3, items.Item4, items.Item5, items.Item6, items.Item7, items.Item8, items.Item9, items.Item10, items.Item11, items.Item12, items.Item13, items.Item14 }); [Pure] [Obsolete(Change.UseCollectionIntialiser)] public static implicit operator HashMap(((K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V)) items) => new (new[] { items.Item1, items.Item2, items.Item3, items.Item4, items.Item5, items.Item6, items.Item7, items.Item8, items.Item9, items.Item10, items.Item11, items.Item12, items.Item13, items.Item14, items.Item15 }); [Pure] [Obsolete(Change.UseCollectionIntialiser)] public static implicit operator HashMap(((K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V)) items) => new (new[] { items.Item1, items.Item2, items.Item3, items.Item4, items.Item5, items.Item6, items.Item7, items.Item8, items.Item9, items.Item10, items.Item11, items.Item12, items.Item13, items.Item14, items.Item15, items.Item16 }); public static HashMap AdditiveIdentity => Empty; } ================================================ FILE: LanguageExt.Core/Immutable Collections/HashSet/Extensions/HashSet.Extensions.MapApply.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public static partial class HashSetExtensions { /// /// Functor map operation /// /// /// Unwraps the value within the functor, passes it to the map function `f` provided, and /// then takes the mapped value and wraps it back up into a new functor. /// /// Functor to map /// Mapping function /// Mapped functor public static HashSet Map(this Func f, K ma) => Functor.map(f, ma).As(); /// /// Functor map operation /// /// /// Unwraps the value within the functor, passes it to the map function `f` provided, and /// then takes the mapped value and wraps it back up into a new functor. /// /// Functor to map /// Mapping function /// Mapped functor public static HashSet Map(this Func f, HashSet ma) => Functor.map(f, ma).As(); /// /// Applicative action: runs the first applicative, ignores the result, and returns the second applicative /// public static HashSet Action(this HashSet ma, K mb) => Applicative.action(ma, mb).As(); /// /// Applicative action: runs the first applicative, ignores the result, and returns the second applicative /// public static HashSet Action(this K ma, K mb) => Applicative.action(ma, mb).As(); /// /// Applicative functor apply operation /// /// /// Unwraps the value within the `ma` applicative-functor, passes it to the unwrapped function(s) within `mf`, and /// then takes the resulting value and wraps it back up into a new applicative-functor. /// /// Value(s) applicative functor /// Mapping function(s) /// Mapped applicative functor public static HashSet Apply(this HashSet> mf, K ma) => Applicative.apply(mf, ma).As(); /// /// Applicative functor apply operation /// /// /// Unwraps the value within the `ma` applicative-functor, passes it to the unwrapped function(s) within `mf`, and /// then takes the resulting value and wraps it back up into a new applicative-functor. /// /// Value(s) applicative functor /// Mapping function(s) /// Mapped applicative functor public static HashSet Apply(this K> mf, K ma) => Applicative.apply(mf, ma).As(); } ================================================ FILE: LanguageExt.Core/Immutable Collections/HashSet/Extensions/HashSet.Extensions.cs ================================================  using System; using System.Diagnostics.Contracts; using System.Linq; using LanguageExt.Traits; namespace LanguageExt; public static partial class HashSetExtensions { [Pure] public static HashSet As(this K ma) => (HashSet)ma; /// /// Convert to a queryable /// [Pure] public static IQueryable AsQueryable(this HashSet source) => // NOTE TO FUTURE ME: Don't delete this thinking it's not needed! source.Value.AsQueryable(); } ================================================ FILE: LanguageExt.Core/Immutable Collections/HashSet/HashSet.Eq.cs ================================================ using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics.Contracts; using System.Numerics; using System.Runtime.CompilerServices; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; /// /// Unsorted immutable hash-set /// /// Key type [CollectionBuilder(typeof(HashSet), nameof(HashSet.createRange))] public readonly struct HashSet : IEnumerable, IEquatable>, IEqualityOperators, HashSet, bool>, IAdditionOperators, HashSet, HashSet>, ISubtractionOperators, HashSet, HashSet>, IAdditiveIdentity, HashSet>, Monoid> where EqA : Eq { public static HashSet Empty { get; } = new (TrieSet.Empty); readonly TrieSet value; TrieSet Value => value ?? TrieSet.Empty; internal HashSet(TrieSet value) { this.value = value; } HashSet Wrap(TrieSet value) => new (value); /// /// Ctor that takes an initial (distinct) set of items /// /// public HashSet(IEnumerable items) : this(items, true) { } /// /// Ctor that takes an initial (distinct) set of items /// public HashSet(IEnumerable items, bool tryAdd) => value = new TrieSet(items, tryAdd); /// /// Ctor that takes an initial (distinct) set of items /// /// public HashSet(ReadOnlySpan items) : this(items, true) { } /// /// Ctor that takes an initial (distinct) set of items /// public HashSet(ReadOnlySpan items, bool tryAdd) => value = new TrieSet(items, tryAdd); /// /// Item at index lens /// [Pure] public static Lens, bool> item(A key) => Lens, bool>.New( Get: la => la.Contains(key), Set: a => la => a ? la.AddOrUpdate(key) : la.Remove(key)); /// /// Lens map /// [Pure] public static Lens, HashSet> map(Lens lens) => Lens, HashSet>.New( Get: la => la.Map(lens.Get), Set: lb => la => { foreach (var item in lb) { la = la.Find(item).Match(Some: x => la.AddOrUpdate(lens.Set(x, item)), None: () => la); } return la; }); /// /// 'this' accessor /// /// Key /// Optional value [Pure] public A this[A key] => Value[key]; /// /// Is the set empty /// [Pure] public bool IsEmpty { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => value?.IsEmpty ?? true; } /// /// Number of items in the set /// [Pure] public int Count { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => value?.Count ?? 0; } /// /// Alias of Count /// [Pure] public int Length { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => value?.Count ?? 0; } /// /// Impure iteration of the bound values in the structure /// /// /// Returns the original unmodified structure /// public HashSet Do(Action f) { IterableExtensions.AsIterable(this).Iter(f); return this; } /// /// Maps the values of this set into a new set of values using the /// mapper function to tranform the source values. /// /// Mapped element type /// HSet /// Mapping function /// Mapped enumerable [Pure] public HashSet Map(Func mapper) where EqR : Eq { IEnumerable Yield(TrieSet map, Func f) { foreach (var item in map) { yield return f(item); } } return new HashSet(Yield(Value, mapper)); } /// /// Maps the values of this set into a new set of values using the /// mapper function to tranform the source values. /// /// HSet /// Mapping function /// Mapped enumerable [Pure] public HashSet Map(Func mapper) => Map(mapper); /// /// Filters items from the set using the predicate. If the predicate /// returns True for any item then it remains in the set, otherwise /// it's dropped. /// /// Element type /// HSet /// Predicate /// Filtered enumerable [Pure] public HashSet Filter(Func pred) { IEnumerable Yield(TrieSet map, Func f) { foreach (var item in map) { if (f(item)) { yield return item; } } } return new HashSet(Yield(Value, pred)); } /// /// Maps the values of this set into a new set of values using the /// mapper function to tranform the source values. /// /// Mapped element type /// HSet /// Mapping function /// Mapped enumerable [Pure] public HashSet Select(Func mapper) where EqR : Eq => Map(mapper); /// /// Filters items from the set using the predicate. If the predicate /// returns True for any item then it remains in the set, otherwise /// it's dropped. /// /// HSet /// Predicate /// Filtered enumerable [Pure] public HashSet Where(Func pred) => Filter(pred); /// /// Add an item to the set /// /// Value to add to the set /// New set with the item added [Pure] public HashSet Add(A key) => Wrap(Value.Add(key)); /// /// Attempt to add an item to the set. If an item already /// exists then return the Set as-is. /// /// Value to add to the set /// New set with the item maybe added [Pure] public HashSet TryAdd(A key) => Wrap(Value.TryAdd(key)); /// /// Add an item to the set. If an item already /// exists then replace it. /// /// Value to add to the set /// New set with the item maybe added [Pure] public HashSet AddOrUpdate(A key) => Wrap(Value.AddOrUpdate(key)); /// /// Atomically adds a range of items to the set. /// /// Null is not allowed for a Key /// Range of keys to add /// Throws ArgumentException if any of the keys already exist /// Throws ArgumentNullException if any of the keys are null /// New HSet with the items added [Pure] public HashSet AddRange(IEnumerable range) => Wrap(Value.AddRange(range)); /// /// Atomically adds a range of items to the set. If an item already exists, it's ignored. /// /// Null is not allowed for a Key /// Range of keys to add /// Throws ArgumentNullException if any of the keys are null /// New HSet with the items added [Pure] public HashSet TryAddRange(IEnumerable range) => Wrap(Value.TryAddRange(range)); /// /// Atomically adds a range of items to the set. If an item already exists then replace it. /// /// Null is not allowed for a Key /// Range of keys to add /// Throws ArgumentNullException if any of the keys are null /// New HSet with the items added [Pure] public HashSet AddOrUpdateRange(IEnumerable range) => Wrap(Value.AddOrUpdateRange(range)); /// /// Atomically removes an item from the set /// If the key doesn't exists, the request is ignored. /// /// Key /// New map with the item removed [Pure] public HashSet Remove(A key) => Wrap(Value.Remove(key)); /// /// Retrieve a value from the set by key /// /// Key to find /// Found value [Pure] public Option Find(A key) => Value.Find(key); /// /// Retrieve a value from the set by key as an enumerable /// /// Key to find /// Found value [Pure] public Seq FindSeq(A key) => Find(key).ToSeq(); /// /// Retrieve a value from the set by key and pattern match the /// result. /// /// Key to find /// Found value [Pure] public R Find(A key, Func Some, Func None) => Find(key).Match(Some, None); /// /// Atomically updates an existing item /// /// Null is not allowed for a key /// Key /// Throws ArgumentNullException if the key is null /// New HSet with the item added [Pure] public HashSet SetItem(A key) => Wrap(Value.SetItem(key)); /// /// Atomically updates an existing item, unless it doesn't exist, in which case /// it is ignored /// /// Null is not allowed for a key /// Key /// Value /// Throws ArgumentNullException if the key is null /// New HSet with the item added [Pure] public HashSet TrySetItem(A key) => Wrap(Value.TrySetItem(key)); /// /// Checks for existence of a key in the set /// /// Key to check /// True if an item with the key supplied is in the set [Pure] public bool Contains(A key) => Value.ContainsKey(key); /// /// Clears all items from the set /// /// Functionally equivalent to calling HSet.empty as the original structure is untouched /// Empty HSet [Pure] public HashSet Clear() => Empty; /// /// Atomically sets a series of items /// /// Items to set /// Throws ArgumentException if any of the keys aren't in the map /// New HSet with the items set [Pure] public HashSet SetItems(IEnumerable items) => Wrap(Value.SetItems(items)); /// /// Atomically sets a series of items. If any of the items don't exist then they're silently ignored. /// /// Items to set /// New HSet with the items set [Pure] public HashSet TrySetItems(IEnumerable items) => Wrap(Value.TrySetItems(items)); /// /// Atomically removes a list of keys from the set /// /// Keys to remove /// New HSet with the items removed [Pure] public HashSet RemoveRange(IEnumerable keys) => Wrap(Value.RemoveRange(keys)); /// /// GetEnumerator - IEnumerable interface /// [Pure] public IEnumerator GetEnumerator() => // ReSharper disable once NotDisposedResourceIsReturned Value.GetEnumerator(); /// /// GetEnumerator - IEnumerable interface /// [Pure] IEnumerator IEnumerable.GetEnumerator() => // ReSharper disable once NotDisposedResourceIsReturned Value.GetEnumerator(); [Pure] public Seq ToSeq() => toSeq(this); /// /// Format the collection as `[a, b, c, ...]` /// The elipsis is used for collections over 50 items /// To get a formatted string with all the items, use `ToFullString` /// or `ToFullArrayString`. /// [Pure] public override string ToString() => CollectionFormat.ToShortArrayString(this, Count); /// /// Format the collection as `a, b, c, ...` /// [Pure] public string ToFullString(string separator = ", ") => CollectionFormat.ToFullString(this, separator); /// /// Format the collection as `[a, b, c, ...]` /// [Pure] public string ToFullArrayString(string separator = ", ") => CollectionFormat.ToFullArrayString(this, separator); [Pure] public Iterable AsIterable() => Iterable.createRange(this); /// /// Implicit conversion from an untyped empty list /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator HashSet(SeqEmpty _) => Empty; [Pure] public static HashSet operator +(HashSet lhs, HashSet rhs) => lhs.Combine(rhs); [Pure] public HashSet Combine(HashSet rhs) => Wrap(Value.Append(rhs.Value)); [Pure] public static HashSet operator -(HashSet lhs, HashSet rhs) => lhs.Subtract(rhs); [Pure] public HashSet Subtract(HashSet rhs) => Wrap(Value.Subtract(rhs.Value)); [Pure] public static bool operator ==(HashSet lhs, HashSet rhs) => lhs.Equals(rhs); [Pure] public static bool operator !=(HashSet lhs, HashSet rhs) => !(lhs == rhs); [Pure] public bool Equals(HashSet other) => Value.Equals(other.Value); /// /// Returns True if 'other' is a proper subset of this set /// /// True if 'other' is a proper subset of this set [Pure] public bool IsProperSubsetOf(IEnumerable other) => Value.IsProperSubsetOf(other); /// /// Returns True if 'other' is a proper superset of this set /// /// True if 'other' is a proper superset of this set [Pure] public bool IsProperSupersetOf(IEnumerable other) => Value.IsProperSupersetOf(other); /// /// Returns True if 'other' is a superset of this set /// /// True if 'other' is a superset of this set [Pure] public bool IsSubsetOf(IEnumerable other) => Value.IsSubsetOf(other); /// /// Returns True if 'other' is a superset of this set /// /// True if 'other' is a superset of this set [Pure] public bool IsSupersetOf(IEnumerable rhs) => Value.IsSupersetOf(rhs); /// /// Returns the elements that are in both this and other /// [Pure] public HashSet Intersect(IEnumerable rhs) => Wrap(Value.Intersect(rhs)); /// /// Returns True if other overlaps this set /// [Pure] public bool Overlaps(IEnumerable other) => Value.Overlaps(other); /// /// Returns this - other. Only the items in this that are not in /// other will be returned. /// [Pure] public HashSet Except(IEnumerable rhs) => Wrap(Value.Except(rhs)); /// /// Only items that are in one set or the other will be returned. /// If an item is in both, it is dropped. /// [Pure] public HashSet SymmetricExcept(HashSet rhs) => Wrap(Value.SymmetricExcept(rhs.Value)); /// /// Only items that are in one set or the other will be returned. /// If an item is in both, it is dropped. /// [Pure] public HashSet SymmetricExcept(IEnumerable rhs) => Wrap(Value.SymmetricExcept(rhs)); /// /// Finds the union of two sets and produces a new set with /// the results /// /// Other set to union with /// A set which contains all items from both sets [Pure] public HashSet Union(IEnumerable rhs) => TryAddRange(rhs); /// /// Copy the items from the set into the specified array /// /// Array to copy to /// Index into the array to start public Unit CopyTo(A[] array, int index) { var max = array.Length; using var iter = GetEnumerator(); for (var i = index; i < max && iter.MoveNext(); i++) { array[i] = iter.Current; } return default; } /// /// Copy the items from the set into the specified array /// /// Array to copy to /// Index into the array to start public Unit CopyTo(System.Array array, int index) { var max = array.Length; using var iter = GetEnumerator(); for (var i = index; i < max && iter.MoveNext(); i++) { array.SetValue(iter.Current, i); } return default; } [Pure] public override bool Equals(object? obj) => obj is HashSet hs && Equals(hs); [Pure] public override int GetHashCode() => Value.GetHashCode(); [Pure] public HashSet Bind(Func> f) { var self = this; IEnumerable Yield() { foreach (var x in self.AsIterable()) { foreach (var y in f(x)) { yield return y; } } } return new HashSet(Yield(), true); } [Pure] public HashSet SelectMany(Func> bind, Func project) { var self = this; IEnumerable Yield() { foreach (var x in self.AsIterable()) { foreach (var y in bind(x)) { yield return project(x, y); } } } return new HashSet(Yield(), true); } public static HashSet AdditiveIdentity => Empty; } ================================================ FILE: LanguageExt.Core/Immutable Collections/HashSet/HashSet.Module.Eq.cs ================================================ using System; using System.Linq; using System.Collections.Generic; using System.Diagnostics.Contracts; using LanguageExt.Traits; namespace LanguageExt; /// /// Immutable hash-set module /// public partial class HashSet { /// /// True if the set has no elements /// /// Element type /// True if the set has no elements [Pure] public static bool isEmpty(HashSet set) where EqT : Eq => set.IsEmpty; /// /// Create a new empty set /// /// Element type /// Empty HSet [Pure] public static HashSet create() where EqT : Eq => HashSet.Empty; /// /// Create a singleton collection /// /// Single value /// Collection with a single item in it [Pure] public static HashSet singleton(A value) where EqA : Eq => [value]; /// /// Create a new set pre-populated with the items in range /// /// Element type /// Range of items /// HSet [Pure] public static HashSet createRange(ReadOnlySpan range) where EqA : Eq => range.IsEmpty ? HashSet.Empty : new (range); /// /// Create a new set pre-populated with the items in range /// /// Element type /// Range of items /// HSet [Pure] public static HashSet createRange(IEnumerable range) where EqT : Eq => new (range); /// /// Create a new empty set /// /// Element type /// Empty HSet [Pure] public static HashSet empty() where EqT : Eq => HashSet.Empty; /// /// Add an item to the set /// /// Element type /// Set to add item to /// Value to add to the HSet /// New set with the item added [Pure] public static HashSet add(HashSet set, T value) where EqT : Eq => set.Add(value); /// /// Attempt to add an item to the set. If an item already /// exists then return the Set as-is. /// /// Element type /// Set to add item to /// Value to add to the HSet /// New set with the item maybe added [Pure] public static HashSet tryAdd(HashSet set, T value) where EqT : Eq => set.TryAdd(value); /// /// Add an item to the set. If an item already /// exists then replace it. /// /// Element type /// Set to add item to /// Value to add to the HSet /// New set with the item maybe added [Pure] public static HashSet addOrUpdate(HashSet set, T value) where EqT : Eq => set.AddOrUpdate(value); /// /// Attempts to find an item in the set. /// /// Element type /// HSet /// Value to find /// Some(T) if found, None otherwise [Pure] public static Option find(HashSet set, T value) where EqT : Eq => set.Find(value); /// /// Check the existence of an item in the set using a /// predicate. /// /// Note this scans the entire set. /// Element type /// HSet /// Predicate /// True if predicate returns true for any item [Pure] public static bool exists(HashSet set, Func pred) where EqT : Eq => set.AsIterable().Exists(pred); /// /// Returns true if both sets contain the same elements /// [Pure] public static bool equals(HashSet setA, HashSet setB) where EqT : Eq => setA.Equals(setB); /// /// Get the number of elements in the set /// /// Element type /// HSet /// Number of elements [Pure] public static int length(HashSet set) where EqT : Eq => set.Count(); /// /// Returns setA - setB. Only the items in setA that are not in /// setB will be returned. /// [Pure] public static HashSet subtract(HashSet setA, HashSet setB) where EqT : Eq => setA.Except(setB); /// /// Finds the union of two sets and produces a new set with /// the results /// /// Element type /// Set A /// Set A /// A set which contains all items from both sets [Pure] public static HashSet union(HashSet setA, HashSet setB) where EqT : Eq => setA.Union(setB); /// /// Filters items from the set using the predicate. If the predicate /// returns True for any item then it remains in the set, otherwise /// it's dropped. /// /// Element type /// HSet /// Predicate /// Filtered enumerable [Pure] public static HashSet filter(HashSet set, Func pred) where EqT : Eq => set.Filter(pred); /// /// Applies a function 'folder' to each element of the collection, threading an accumulator /// argument through the computation. The fold function takes the state argument, and /// applies the function 'folder' to it and the first element of the set. Then, it feeds this /// result into the function 'folder' along with the second element, and so on. It returns the /// final result. (Aggregate in LINQ) /// /// State type /// Set element type /// Set to fold /// Initial state /// Fold function /// Aggregate value [Pure] public static S fold(HashSet set, S state, Func folder) where EqT : Eq => set.AsIterable().Fold(state, folder); /// /// Applies a function 'folder' to each element of the collection (from last element to first), /// threading an aggregate state through the computation. The fold function takes the state /// argument, and applies the function 'folder' to it and the first element of the set. Then, /// it feeds this result into the function 'folder' along with the second element, and so on. It /// returns the final result. /// /// State type /// Set element type /// Set to fold /// Initial state /// Fold function /// Aggregate value [Pure] public static S foldBack(HashSet set, S state, Func folder) where EqT : Eq => set.AsIterable().FoldBack(state, folder); /// /// Returns the elements that are in both setA and setB /// [Pure] public static HashSet intersect(HashSet setA, HashSet setB) where EqT : Eq => setA.Intersect(setB); /// /// Returns the elements that are in both setA and setB /// [Pure] public static HashSet except(HashSet setA, HashSet setB) where EqT : Eq => setA.Except(setB); /// /// Only items that are in one set or the other will be returned. /// If an item is in both, it is dropped. /// [Pure] public static HashSet symmetricExcept(HashSet setA, HashSet setB) where EqT : Eq => setA.SymmetricExcept(setB); /// /// Maps the values of this set into a new set of values using the /// mapper function to transform the source values. /// /// Element type /// Mapped element type /// HSet /// Mapping function /// Mapped enumerable [Pure] public static HashSet map(HashSet set, Func mapper) where EqT : Eq where EqR : Eq => set.Map(mapper); /// /// Maps the values of this set into a new set of values using the /// mapper function to tranform the source values. /// /// Element type /// Mapped element type /// HSet /// Mapping function /// Mapped enumerable [Pure] public static HashSet map(HashSet set, Func mapper) where EqT : Eq => set.Map(mapper); /// /// Returns True if the value is in the set /// /// Element type /// HSet /// Value to check /// True if the item 'value' is in the Set 'set' [Pure] public static bool contains(HashSet set, T value) where EqT : Eq => set.Contains(value); /// /// Removes an item from the set (if it exists) /// /// Element type /// HSet /// Value to check /// New set with item removed [Pure] public static HashSet remove(HashSet set, T value) where EqT : Eq => set.Remove(value); /// /// Returns True if setB is a subset of setA /// /// Element type /// Set A /// Set B /// True is setB is a subset of setA [Pure] public static bool isSubHSet(HashSet setA, HashSet setB) where EqT : Eq => setA.IsSubsetOf(setB); /// /// Returns True if setB is a superset of setA /// /// Element type /// Set A /// Set B /// True is setB is a superset of setA [Pure] public static bool isSuperHSet(HashSet setA, HashSet setB) where EqT : Eq => setA.IsSupersetOf(setB); /// /// Returns True if setB is a proper subset of setA /// /// Element type /// Set A /// Set B /// True is setB is a proper subset of setA [Pure] public static bool isProperSubHSet(HashSet setA, HashSet setB) where EqT : Eq => setA.IsProperSubsetOf(setB); /// /// Returns True if setB is a proper superset of setA /// /// Element type /// Set A /// Set B /// True is setB is a proper subset of setA [Pure] public static bool isProperSuperHSet(HashSet setA, HashSet setB) where EqT : Eq => setA.IsProperSupersetOf(setB); /// /// Returns True if setA overlaps setB /// /// Element type /// Set A /// Set B /// True if setA overlaps setB [Pure] public static bool overlaps(HashSet setA, HashSet setB) where EqT : Eq => setA.Overlaps(setB); } ================================================ FILE: LanguageExt.Core/Immutable Collections/HashSet/HashSet.Module.cs ================================================ using System; using System.Linq; using System.Collections.Generic; using System.Diagnostics.Contracts; namespace LanguageExt; /// /// Immutable hash-set module /// public partial class HashSet { /// /// True if the set has no elements /// /// Element type /// True if the set has no elements [Pure] public static bool isEmpty(HashSet set) => set.IsEmpty; /// /// Create a new empty set /// /// Element type /// Empty HSet [Pure] public static HashSet create() => HashSet.Empty; /// /// Create a singleton collection /// /// Single value /// Collection with a single item in it [Pure] public static HashSet singleton(A value) => [value]; /// /// Create a new set pre-populated with the items in range /// /// Element type /// Range of items /// HSet [Pure] public static HashSet createRange(IEnumerable range) => new (range); /// /// Create a new set pre-populated with the items in range /// /// Element type /// Range of items /// HSet [Pure] public static HashSet createRange(ReadOnlySpan range) => range.IsEmpty ? HashSet.Empty : new (range); /// /// Create a new empty set /// /// Element type /// Empty HSet [Pure] public static HashSet empty() => HashSet.Empty; /// /// Add an item to the set /// /// Element type /// Set to add item to /// Value to add to the HSet /// New set with the item added [Pure] public static HashSet add(HashSet set, T value) => set.Add(value); /// /// Attempt to add an item to the set. If an item already /// exists then return the Set as-is. /// /// Element type /// Set to add item to /// Value to add to the HSet /// New set with the item maybe added [Pure] public static HashSet tryAdd(HashSet set, T value) => set.TryAdd(value); /// /// Add an item to the set. If an item already /// exists then replace it. /// /// Element type /// Set to add item to /// Value to add to the HSet /// New set with the item maybe added [Pure] public static HashSet addOrUpdate(HashSet set, T value) => set.AddOrUpdate(value); /// /// Attempts to find an item in the set. /// /// Element type /// HSet /// Value to find /// Some(T) if found, None otherwise [Pure] public static Option find(HashSet set, T value) => set.Find(value); /// /// Check the existence of an item in the set using a /// predicate. /// /// Note this scans the entire set. /// Element type /// HSet /// Predicate /// True if predicate returns true for any item [Pure] public static bool exists(HashSet set, Func pred) => set.Exists(pred); /// /// Returns true if both sets contain the same elements /// [Pure] public static bool equals(HashSet setA, HashSet setB) => setA.Equals(setB); /// /// Get the number of elements in the set /// /// Element type /// HSet /// Number of elements [Pure] public static int length(HashSet set) => set.Count(); /// /// Returns setA - setB. Only the items in setA that are not in /// setB will be returned. /// [Pure] public static HashSet subtract(HashSet setA, HashSet setB) => setA.Except(setB); /// /// Finds the union of two sets and produces a new set with /// the results /// /// Element type /// Set A /// Set A /// A set which contains all items from both sets [Pure] public static HashSet union(HashSet setA, HashSet setB) => setA.Union(setB); /// /// Filters items from the set using the predicate. If the predicate /// returns True for any item then it remains in the set, otherwise /// it's dropped. /// /// Element type /// HSet /// Predicate /// Filtered enumerable [Pure] public static HashSet filter(HashSet set, Func pred) => set.Filter(pred); /// /// Applies a function 'folder' to each element of the collection, threading an accumulator /// argument through the computation. The fold function takes the state argument, and /// applies the function 'folder' to it and the first element of the set. Then, it feeds this /// result into the function 'folder' along with the second element, and so on. It returns the /// final result. (Aggregate in LINQ) /// /// State type /// Set element type /// Set to fold /// Initial state /// Fold function /// Aggregate value [Pure] public static S fold(HashSet set, S state, Func folder) => set.Fold(state, folder); /// /// Applies a function 'folder' to each element of the collection (from last element to first), /// threading an aggregate state through the computation. The fold function takes the state /// argument, and applies the function 'folder' to it and the first element of the set. Then, /// it feeds this result into the function 'folder' along with the second element, and so on. It /// returns the final result. /// /// State type /// Set element type /// Set to fold /// Initial state /// Fold function /// Aggregate value [Pure] public static S foldBack(HashSet set, S state, Func folder) => set.FoldBack(state, folder); /// /// Returns the elements that are in both setA and setB /// [Pure] public static HashSet intersect(HashSet setA, IEnumerable setB) => setA.Intersect(setB); /// /// Returns the elements that are in both setA and setB /// [Pure] public static HashSet except(HashSet setA, IEnumerable setB) => setA.Except(setB); /// /// Only items that are in one set or the other will be returned. /// If an item is in both, it is dropped. /// [Pure] public static HashSet symmetricExcept(HashSet setA, IEnumerable setB) => setA.SymmetricExcept(setB); /// /// Maps the values of this set into a new set of values using the /// mapper function to tranform the source values. /// /// Element type /// Mapped element type /// HSet /// Mapping function /// Mapped enumerable [Pure] public static HashSet map(HashSet set, Func mapper) => set.Map(mapper); /// /// Returns True if the value is in the set /// /// Element type /// HSet /// Value to check /// True if the item 'value' is in the Set 'set' [Pure] public static bool contains(HashSet set, T value) => set.Contains(value); /// /// Removes an item from the set (if it exists) /// /// Element type /// HSet /// Value to check /// New set with item removed [Pure] public static HashSet remove(HashSet set, T value) => set.Remove(value); /// /// Returns True if setB is a subset of setA /// /// Element type /// Set A /// Set B /// True is setB is a subset of setA [Pure] public static bool isSubHSet(HashSet setA, IEnumerable setB) => setA.IsSubsetOf(setB); /// /// Returns True if setB is a superset of setA /// /// Element type /// Set A /// Set B /// True is setB is a superset of setA [Pure] public static bool isSuperHSet(HashSet setA, IEnumerable setB) => setA.IsSupersetOf(setB); /// /// Returns True if setB is a proper subset of setA /// /// Element type /// Set A /// Set B /// True is setB is a proper subset of setA [Pure] public static bool isProperSubHSet(HashSet setA, IEnumerable setB) => setA.IsProperSubsetOf(setB); /// /// Returns True if setB is a proper superset of setA /// /// Element type /// Set A /// Set B /// True is setB is a proper subset of setA [Pure] public static bool isProperSuperHSet(HashSet setA, IEnumerable setB) => setA.IsProperSupersetOf(setB); /// /// Returns True if setA overlaps setB /// /// Element type /// Set A /// Set B /// True if setA overlaps setB [Pure] public static bool overlaps(HashSet setA, IEnumerable setB) => setA.Overlaps(setB); } ================================================ FILE: LanguageExt.Core/Immutable Collections/HashSet/HashSet.cs ================================================ using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics.Contracts; using System.Numerics; using System.Runtime.CompilerServices; using LanguageExt.ClassInstances; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; /// /// Unsorted immutable hash-set /// /// Key type [CollectionBuilder(typeof(HashSet), nameof(HashSet.createRange))] public readonly struct HashSet : IEnumerable, IEquatable>, IEqualityOperators, HashSet, bool>, IAdditionOperators, HashSet, HashSet>, ISubtractionOperators, HashSet, HashSet>, IAdditiveIdentity, HashSet>, Monoid>, K { public static HashSet Empty { get; } = new (TrieSet, A>.Empty); readonly TrieSet, A> value; internal TrieSet, A> Value => value ?? TrieSet, A>.Empty; internal HashSet(TrieSet, A> value) { this.value = value; } HashSet Wrap(TrieSet, A> value) => new (value); static HashSet Wrap(TrieSet, B> value) => new (value); /// /// Ctor that takes an initial (distinct) set of items /// /// public HashSet(ReadOnlySpan items) => value = new TrieSet, A>(items); /// /// Ctor that takes an initial (distinct) set of items /// /// public HashSet(ReadOnlySpan items, bool tryAdd) => value = new TrieSet, A>(items, tryAdd); /// /// Ctor that takes an initial (distinct) set of items /// public HashSet(IEnumerable items) => value = new TrieSet, A>(items); /// /// Ctor that takes an initial (distinct) set of items /// public HashSet(IEnumerable items, bool tryAdd) => value = new TrieSet, A>(items, tryAdd); /// /// Item at index lens /// [Pure] public static Lens, bool> item(A key) => Lens, bool>.New( Get: la => la.Contains(key), Set: a => la => a ? la.AddOrUpdate(key) : la.Remove(key) ); /// /// Lens map /// [Pure] public static Lens, HashSet> map(Lens lens) => Lens, HashSet>.New( Get: la => la.Map(lens.Get), Set: lb => la => { foreach (var item in lb) { la = la.Find(item).Match(Some: x => la.AddOrUpdate(lens.Set(x, item)), None: () => la); } return la; }); /// /// 'this' accessor /// /// Key /// Optional value [Pure] public A this[A key] => Value[key]; /// /// Is the set empty /// [Pure] public bool IsEmpty { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => value?.IsEmpty ?? true; } /// /// Number of items in the set /// [Pure] public int Count { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => value?.Count ?? 0; } /// /// Alias of Count /// [Pure] public int Length { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => value?.Count ?? 0; } /// /// Impure iteration of the bound values in the structure /// /// /// Returns the original unmodified structure /// public HashSet Do(Action f) { this.Iter(f); return this; } /// /// Maps the values of this set into a new set of values using the /// mapper function to tranform the source values. /// /// Element type /// Mapped element type /// HSet /// Mapping function /// Mapped enumerable [Pure] public HashSet Map(Func mapper) { IEnumerable Yield(TrieSet, A> map, Func f) { foreach (var item in map) { yield return f(item); } } return new HashSet(Yield(Value, mapper)); } /// /// Map each element of a structure to an action, evaluate these actions from /// left to right, and collect the results. /// /// /// Traversable structure /// Applicative functor trait /// Bound value (output) [Pure] public K> Traverse(Func> f) where F : Applicative => F.Map(x => x.As(), Traversable.traverse(f, this)); /// /// Map each element of a structure to an action, evaluate these actions from /// left to right, and collect the results. /// /// /// Traversable structure /// Monad trait /// Bound value (output) [Pure] public K> TraverseM(Func> f) where M : Monad => M.Map(x => x.As(), Traversable.traverseM(f, this)); /// /// Filters items from the set using the predicate. If the predicate /// returns True for any item then it remains in the set, otherwise /// it's dropped. /// /// Element type /// HSet /// Predicate /// Filtered enumerable [Pure] public HashSet Filter(Func pred) { IEnumerable Yield(TrieSet, A> map, Func f) { foreach (var item in map) { if (f(item)) { yield return item; } } } return new HashSet(Yield(Value, pred)); } /// /// Maps the values of this set into a new set of values using the /// mapper function to tranform the source values. /// /// Element type /// Mapped element type /// HSet /// Mapping function /// Mapped enumerable [Pure] public HashSet Select(Func mapper) => Map(mapper); /// /// Filters items from the set using the predicate. If the predicate /// returns True for any item then it remains in the set, otherwise /// it's dropped. /// /// Element type /// HSet /// Predicate /// Filtered enumerable [Pure] public HashSet Where(Func pred) => Filter(pred); /// /// Add an item to the set /// /// Value to add to the set /// New set with the item added [Pure] public HashSet Add(A key) => Wrap(Value.Add(key)); /// /// Attempt to add an item to the set. If an item already /// exists then return the Set as-is. /// /// Value to add to the set /// New set with the item maybe added [Pure] public HashSet TryAdd(A key) => Wrap(Value.TryAdd(key)); /// /// Add an item to the set. If an item already /// exists then replace it. /// /// Value to add to the set /// New set with the item maybe added [Pure] public HashSet AddOrUpdate(A key) => Wrap(Value.AddOrUpdate(key)); /// /// Atomically adds a range of items to the set. /// /// Null is not allowed for a Key /// Range of keys to add /// Throws ArgumentException if any of the keys already exist /// Throws ArgumentNullException if any of the keys are null /// New HSet with the items added [Pure] public HashSet AddRange(IEnumerable range) => Wrap(Value.AddRange(range)); /// /// Atomically adds a range of items to the set. If an item already exists, it's ignored. /// /// Null is not allowed for a Key /// Range of keys to add /// Throws ArgumentNullException if any of the keys are null /// New HSet with the items added [Pure] public HashSet TryAddRange(IEnumerable range) => Wrap(Value.TryAddRange(range)); /// /// Atomically adds a range of items to the set. If an item already exists then replace it. /// /// Null is not allowed for a Key /// Range of keys to add /// Throws ArgumentNullException if any of the keys are null /// New HSet with the items added [Pure] public HashSet AddOrUpdateRange(IEnumerable range) => Wrap(Value.AddOrUpdateRange(range)); /// /// Atomically removes an item from the set /// If the key doesn't exists, the request is ignored. /// /// Key /// New map with the item removed [Pure] public HashSet Remove(A key) => Wrap(Value.Remove(key)); /// /// Retrieve a value from the set by key /// /// Key to find /// Found value [Pure] public Option Find(A key) => Value.Find(key); /// /// Retrieve a value from the set by key as an enumerable /// /// Key to find /// Found value [Pure] public Seq FindSeq(A key) => Find(key).ToSeq(); /// /// Retrieve a value from the set by key and pattern match the /// result. /// /// Key to find /// Found value [Pure] public R Find(A key, Func Some, Func None) => Find(key).Match(Some, None); /// /// Atomically updates an existing item /// /// Null is not allowed for a key /// Key /// Throws ArgumentNullException if the key is null /// New HSet with the item added [Pure] public HashSet SetItem(A key) => Wrap(Value.SetItem(key)); /// /// Atomically updates an existing item, unless it doesn't exist, in which case /// it is ignored /// /// Null is not allowed for a key /// Key /// Value /// Throws ArgumentNullException if the key is null /// New HSet with the item added [Pure] public HashSet TrySetItem(A key) => Wrap(Value.TrySetItem(key)); /// /// Checks for existence of a key in the set /// /// Key to check /// True if an item with the key supplied is in the set [Pure] public bool Contains(A key) => Value.ContainsKey(key); /// /// Clears all items from the set /// /// Functionally equivalent to calling HSet.empty as the original structure is untouched /// Empty HSet [Pure] public HashSet Clear() => Empty; /// /// Atomically sets a series of items /// /// Items to set /// Throws ArgumentException if any of the keys aren't in the map /// New HSet with the items set [Pure] public HashSet SetItems(IEnumerable items) => Wrap(Value.SetItems(items)); /// /// Atomically sets a series of items. If any of the items don't exist then they're silently ignored. /// /// Items to set /// New HSet with the items set [Pure] public HashSet TrySetItems(IEnumerable items) => Wrap(Value.TrySetItems(items)); /// /// Atomically removes a list of keys from the set /// /// Keys to remove /// New HSet with the items removed [Pure] public HashSet RemoveRange(IEnumerable keys) => Wrap(Value.RemoveRange(keys)); /// /// GetEnumerator - IEnumerable interface /// [Pure] public IEnumerator GetEnumerator() => Value.GetEnumerator(); /// /// GetEnumerator - IEnumerable interface /// [Pure] IEnumerator IEnumerable.GetEnumerator() => Value.GetEnumerator(); [Pure] public Seq ToSeq() => toSeq(this); /// /// Format the collection as `[a, b, c, ...]` /// The elipsis is used for collections over 50 items /// To get a formatted string with all the items, use `ToFullString` /// or `ToFullArrayString`. /// [Pure] public override string ToString() => CollectionFormat.ToShortArrayString(this, Count); /// /// Format the collection as `a, b, c, ...` /// [Pure] public string ToFullString(string separator = ", ") => CollectionFormat.ToFullString(this, separator); /// /// Format the collection as `[a, b, c, ...]` /// [Pure] public string ToFullArrayString(string separator = ", ") => CollectionFormat.ToFullArrayString(this, separator); [Pure] public Iterable AsIterable() => IterableExtensions.AsIterable(this); /// /// Implicit conversion from an untyped empty list /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator HashSet(SeqEmpty _) => Empty; [Pure] public static HashSet operator +(HashSet lhs, HashSet rhs) => lhs.Combine(rhs); /// /// Choice operator /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static HashSet operator |(HashSet x, K y) => x.Choose(y).As(); /// /// Choice operator /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static HashSet operator |(K x, HashSet y) => x.Choose(y).As(); [Pure] public HashSet Combine(HashSet rhs) => Wrap(Value.Append(rhs.Value)); [Pure] public static HashSet operator -(HashSet lhs, HashSet rhs) => lhs.Subtract(rhs); [Pure] public HashSet Subtract(HashSet rhs) => Wrap(Value.Subtract(rhs.Value)); /// /// Equality /// [Pure] public static bool operator ==(HashSet lhs, HashSet rhs) => lhs.Equals(rhs); /// /// In-equality /// [Pure] public static bool operator !=(HashSet lhs, HashSet rhs) => !(lhs == rhs); /// /// Equality /// [Pure] public bool Equals(HashSet other) => Value.Equals(other.Value); /// /// Returns True if 'other' is a proper subset of this set /// /// True if 'other' is a proper subset of this set [Pure] public bool IsProperSubsetOf(IEnumerable other) => Value.IsProperSubsetOf(other); /// /// Returns True if 'other' is a proper superset of this set /// /// True if 'other' is a proper superset of this set [Pure] public bool IsProperSupersetOf(IEnumerable other) => Value.IsProperSupersetOf(other); /// /// Returns True if 'other' is a superset of this set /// /// True if 'other' is a superset of this set [Pure] public bool IsSubsetOf(IEnumerable other) => Value.IsSubsetOf(other); /// /// Returns True if other overlaps this set /// [Pure] public bool Overlaps(IEnumerable other) => Value.Overlaps(other); /// /// Returns True if 'other' is a superset of this set /// /// True if 'other' is a superset of this set [Pure] public bool IsSupersetOf(IEnumerable rhs) => Value.IsSupersetOf(rhs); /// /// Returns the elements that are in both this and other /// [Pure] public HashSet Intersect(IEnumerable rhs) => Wrap(Value.Intersect(rhs)); /// /// Returns this - other. Only the items in this that are not in /// other will be returned. /// [Pure] public HashSet Except(IEnumerable rhs) => Wrap(Value.Except(rhs)); /// /// Only items that are in one set or the other will be returned. /// If an item is in both, it is dropped. /// [Pure] public HashSet SymmetricExcept(HashSet rhs) => Wrap(Value.SymmetricExcept(rhs.Value)); /// /// Only items that are in one set or the other will be returned. /// If an item is in both, it is dropped. /// [Pure] public HashSet SymmetricExcept(IEnumerable rhs) => Wrap(Value.SymmetricExcept(rhs)); /// /// Finds the union of two sets and produces a new set with /// the results /// /// Other set to union with /// A set which contains all items from both sets [Pure] public HashSet Union(IEnumerable rhs) => this.TryAddRange(rhs); /// /// Copy the items from the set into the specified array /// /// Array to copy to /// Index into the array to start public Unit CopyTo(A[] array, int index) { var max = array.Length; using var iter = GetEnumerator(); for (var i = index; i < max && iter.MoveNext(); i++) { array[i] = iter.Current; } return default; } /// /// Copy the items from the set into the specified array /// /// Array to copy to /// Index into the array to start public Unit CopyTo(Array array, int index) { var max = array.Length; using var iter = GetEnumerator(); for (var i = index; i < max && iter.MoveNext(); i++) { array.SetValue(iter.Current, i); } return default; } [Pure] public override bool Equals(object? obj) => obj is HashSet hs && Equals(hs); [Pure] public override int GetHashCode() => Value.GetHashCode(); [Pure] public HashSet Bind(Func> f) { var self = this; IEnumerable Yield() { foreach (var x in self.AsIterable()) { foreach (var y in f(x)) { yield return y; } } } return new HashSet(Yield(), true); } [Pure] public HashSet SelectMany(Func> bind, Func project) { var self = this; IEnumerable Yield() { foreach (var x in self.AsIterable()) { foreach (var y in bind(x)) { yield return project(x, y); } } } return new HashSet(Yield(), true); } public static HashSet AdditiveIdentity => Empty; } ================================================ FILE: LanguageExt.Core/Immutable Collections/HashSet/Operators/HashSet.Operators.cs ================================================ using LanguageExt.Traits; namespace LanguageExt; public static partial class HashSetExtensions { extension(K _) { /// /// Downcast operator /// public static HashSet operator +(K ma) => (HashSet)ma; } } ================================================ FILE: LanguageExt.Core/Immutable Collections/HashSet/Prelude/HashSet.Prelude.mapapply.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public static partial class Prelude { /// /// Functor map operation /// /// /// Unwraps the value within the functor, passes it to the map function `f` provided, and /// then takes the mapped value and wraps it back up into a new functor. /// /// Functor to map /// Mapping function /// Mapped functor public static HashSet map(Func f, K ma) => Functor.map(f, ma).As(); /// /// Applicative action: runs the first applicative, ignores the result, and returns the second applicative /// public static HashSet action(K ma, K mb) => Applicative.action(ma, mb).As(); /// /// Applicative functor apply operation /// /// /// Unwraps the value within the `ma` applicative-functor, passes it to the unwrapped function(s) within `mf`, and /// then takes the resulting value and wraps it back up into a new applicative-functor. /// /// Value(s) applicative functor /// Mapping function(s) /// Mapped applicative functor public static HashSet apply(K> mf, K ma) => Applicative.apply(mf, ma).As(); } ================================================ FILE: LanguageExt.Core/Immutable Collections/HashSet/Trait/HashSet.TraitImpl.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using LanguageExt.Common; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; public partial class HashSet : Monad, MonoidK, Alternative, Traversable { static K Monad.Recur(A value, Func>> f) => createRange(Monad.enumerableRecur(value, x =>f(x).As().AsEnumerable())); static K Monad.Bind(K ma, Func> f) { return new HashSet(Go()); IEnumerable Go() { foreach (var x in ma.As()) { foreach (var y in f(x).As()) { yield return y; } } } } static K Functor.Map(Func f, K ma) { return new HashSet(Go()); IEnumerable Go() { foreach (var x in ma.As()) { yield return f(x); } } } static K Applicative.Pure(A value) => singleton(value); static K Applicative.Apply(K> mf, K ma) { var ff = mf.As(); if(ff.IsEmpty) return HashSet.Empty; return new HashSet(Go()); IEnumerable Go() { foreach (var f in mf.As()) { foreach (var a in ma.As()) { yield return f(a); } } } } static K Applicative.Apply(K> mf, Memo ma) { var ff = mf.As(); if(ff.IsEmpty) return HashSet.Empty; return new HashSet(Go()); IEnumerable Go() { foreach (var f in mf.As()) { foreach (var a in ma.Value.As()) { yield return f(a); } } } } static K MonoidK.Empty() => HashSet.Empty; static K Alternative.Empty() => HashSet.Empty; static K SemigroupK.Combine(K ma, K mb) => ma.As() + mb.As(); static K Choice.Choose(K ma, K mb) => ma.IsEmpty ? mb : ma; static K Choice.Choose(K ma, Memo mb) => ma.IsEmpty ? mb.Value : ma; static bool Foldable.Contains(A value, K ta) => ta.As().Contains(value); static int Foldable.Count(K ta) => ta.As().Count; static bool Foldable.IsEmpty(K ta) => ta.As().IsEmpty; static K> Traversable.Traverse(Func> f, K ta) { return F.Map, K>( ks => ks, Foldable.fold(acc, F.Pure(empty()), ta)); K> acc(K> ys, A x) => Applicative.lift((bs, b) => bs.Add(b), ys, f(x)); } static K> Traversable.TraverseM(Func> f, K ta) { return F.Map, K>( ks => ks, Foldable.fold(acc, F.Pure(empty()), ta)); K> acc(K> fys, A x) => fys.Bind(ys => f(x).Map(ys.Add)); } static Fold Foldable.FoldStep(K ta, S initialState) { // ReSharper disable once GenericEnumeratorNotDisposed var iter = ta.As().GetEnumerator(); return go(initialState); Fold go(S state) => iter.MoveNext() ? Fold.Loop(state, iter.Current, go) : Fold.Done(state); } static Fold Foldable.FoldStepBack(K ta, S initialState) => // Order is undefined in a HashSet, so reversing the order makes no sense, // so let's take the most efficient option: ta.FoldStep(initialState); } ================================================ FILE: LanguageExt.Core/Immutable Collections/Iterable/DSL/Iterable.Add.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; namespace LanguageExt; sealed class IterableAdd(SeqStrict Prefix, Iterable Source, SeqStrict Postfix) : Iterable { internal override bool IsAsync => Source.IsAsync; public override IO CountIO() => Source.CountIO().Map(c => Prefix.Count + c + Postfix.Count); public override Iterable Add(A item) => new IterableAdd(Prefix, Source, (SeqStrict)Postfix.Add(item)); public override Iterable Cons(A item) => new IterableAdd((SeqStrict)Prefix.Cons(item), Source, Postfix); public override IO> AsEnumerableIO() { return IO.lift(go); IEnumerable go(EnvIO env) { foreach (var x in Prefix) { if (env.Token.IsCancellationRequested) throw new OperationCanceledException(); yield return x; } foreach (var x in Source.AsEnumerable(env.Token)) { if(env.Token.IsCancellationRequested) throw new OperationCanceledException(); yield return x; } foreach (var x in Postfix) { if (env.Token.IsCancellationRequested) throw new OperationCanceledException(); yield return x; } } } public override IO> AsAsyncEnumerableIO() { return IO.lift(go); async IAsyncEnumerable go(EnvIO env) { foreach (var x in Prefix) { if (env.Token.IsCancellationRequested) throw new OperationCanceledException(); yield return x; } await foreach (var x in Source.AsAsyncEnumerable(env.Token)) { yield return x; } foreach (var x in Postfix) { if (env.Token.IsCancellationRequested) throw new OperationCanceledException(); yield return x; } } } public override Iterable Reverse() => new IterableAdd(Postfix.Rev(), Source.Rev(), Prefix.Rev()); public override Iterable Map(Func f) => new IterableAdd(Prefix.Map(f), Source.Map(f), Postfix.Map(f)); public override Iterable Filter(Func f) => new IterableAdd(Prefix.Filter(f), Source.Filter(f), Postfix.Filter(f)); public override IO FoldWhileIO( Func> f, Func<(S State, A Value), bool> predicate, S initialState) => Source.IsAsync ? IO.liftVAsync(async env => { var s = initialState; foreach (var x in Prefix) { if (!predicate((s, x))) { return s; } s = f(x)(s); } await foreach (var x in Source.AsAsyncEnumerable(env.Token)) { if (!predicate((s, x))) { return s; } s = f(x)(s); } foreach (var x in Postfix) { if (!predicate((s, x))) { return s; } s = f(x)(s); } return s; }) : IO.lift(env => { var s = initialState; foreach (var x in Prefix) { if (!predicate((s, x))) { return s; } s = f(x)(s); } foreach (var x in Source.AsEnumerable(env.Token)) { if (!predicate((s, x))) { return s; } s = f(x)(s); } foreach (var x in Postfix) { if (!predicate((s, x))) { return s; } s = f(x)(s); } return s; }); public override IO FoldWhileIO(Func f, Func<(S State, A Value), bool> predicate, S initialState) => Source.IsAsync ? IO.liftVAsync(async env => { var s = initialState; foreach (var x in Prefix) { if (!predicate((s, x))) { return s; } s = f(s, x); } await foreach (var x in Source.AsAsyncEnumerable(env.Token)) { if (!predicate((s, x))) { return s; } s = f(s, x); } foreach (var x in Postfix) { if (!predicate((s, x))) { return s; } s = f(s, x); } return s; }) : IO.lift(env => { var s = initialState; foreach (var x in Prefix) { if (!predicate((s, x))) { return s; } s = f(s, x); } foreach (var x in Source.AsEnumerable(env.Token)) { if (!predicate((s, x))) { return s; } s = f(s, x); } foreach (var x in Postfix) { if (!predicate((s, x))) { return s; } s = f(s, x); } return s; }); public override IO FoldUntilIO( Func> f, Func<(S State, A Value), bool> predicate, S initialState) => Source.IsAsync ? IO.liftVAsync(async env => { var s = initialState; foreach (var x in Prefix) { s = f(x)(s); if (predicate((s, x))) { return s; } } await foreach (var x in Source.AsAsyncEnumerable(env.Token)) { s = f(x)(s); if (predicate((s, x))) { return s; } } foreach (var x in Postfix) { s = f(x)(s); if (predicate((s, x))) { return s; } } return s; }) : IO.lift(env => { var s = initialState; foreach (var x in Prefix) { s = f(x)(s); if (predicate((s, x))) { return s; } } foreach (var x in Source.AsEnumerable(env.Token)) { s = f(x)(s); if (predicate((s, x))) { return s; } } foreach (var x in Postfix) { s = f(x)(s); if (predicate((s, x))) { return s; } } return s; }); public override IO FoldUntilIO(Func f, Func<(S State, A Value), bool> predicate, S initialState) => Source.IsAsync ? IO.liftVAsync(async env => { var s = initialState; foreach (var x in Prefix) { s = f(s, x); if (predicate((s, x))) { return s; } } await foreach (var x in Source.AsAsyncEnumerable(env.Token)) { s = f(s, x); if (predicate((s, x))) { return s; } } foreach (var x in Postfix) { s = f(s, x); if (predicate((s, x))) { return s; } } return s; }) : IO.lift(env => { var s = initialState; foreach (var x in Prefix) { s = f(s, x); if (predicate((s, x))) { return s; } } foreach (var x in Source.AsEnumerable(env.Token)) { s = f(s, x); if (predicate((s, x))) { return s; } } foreach (var x in Postfix) { s = f(s, x); if (predicate((s, x))) { return s; } } return s; }); public override Iterable Choose(Iterable rhs) { if (!Prefix.IsEmpty) return this; return new IterableAsyncEnumerable( IO.liftVAsync(async env => { var ls = AsAsyncEnumerable(env.Token); var iter = ls.GetIteratorAsync(); if (await iter.IsEmpty) { return rhs.AsAsyncEnumerable(env.Token); } else { // This has already been evaluated by `IsEmpty` var head = await iter.Head; var tail = (await iter.Tail).Split().AsEnumerable(env.Token); return tail.Prepend(head); } })); } public override Iterable Choose(Memo rhs) { if (!Prefix.IsEmpty) return this; return new IterableAsyncEnumerable( IO.liftVAsync(async env => { var ls = AsAsyncEnumerable(env.Token); var iter = ls.GetIteratorAsync(); if (await iter.IsEmpty) { return rhs.Value.As().AsAsyncEnumerable(env.Token); } else { // This has already been evaluated by `IsEmpty` var head = await iter.Head; var tail = (await iter.Tail).Split().AsEnumerable(env.Token); return tail.Prepend(head); } })); } } ================================================ FILE: LanguageExt.Core/Immutable Collections/Iterable/DSL/Iterable.AsyncEnumerable.cs ================================================ using System; using System.Linq; using System.Collections.Generic; using System.Diagnostics.Contracts; using System.Threading; using System.Threading.Tasks; using LanguageExt.Traits; namespace LanguageExt; sealed class IterableAsyncEnumerable(IO> runEnumerable) : Iterable { internal override bool IsAsync => true; public override IO CountIO() => IO.liftVAsync(async env => await (await runEnumerable.RunAsync(env)).CountAsync(env.Token)); public override IO> AsEnumerableIO() { return IO.lift(env => go(env, runEnumerable)); static IEnumerable go(EnvIO env, IO> run) { var xs = run.Run(env); foreach (var x in xs.ToBlockingEnumerable(env.Token)) { if (env.Token.IsCancellationRequested) throw new OperationCanceledException(); yield return x; } } } public override IO> AsAsyncEnumerableIO() { return IO.lift(env => go(env, runEnumerable)); static async IAsyncEnumerable go(EnvIO env, IO> run) { var xs = await run.RunAsync(env); await foreach (var x in xs.WithCancellation(env.Token)) { yield return x; } } } public override Iterable Reverse() => new IterableAsyncEnumerable(AsAsyncEnumerableIO().Map(xs => xs.Reverse())); public override Iterable Map(Func f) => new IterableAsyncEnumerable(AsAsyncEnumerableIO().Map(xs => xs.Select(f))); public override Iterable Filter(Func f) => new IterableAsyncEnumerable(AsAsyncEnumerableIO().Map(xs => xs.Where(f))); public override IO FoldWhileIO( Func> f, Func<(S State, A Value), bool> predicate, S initialState) => IO.liftVAsync(async env => { var s = initialState; var xs = await runEnumerable.RunAsync(env); await foreach (var x in xs.WithCancellation(env.Token)) { if (!predicate((s, x))) { return s; } s = f(x)(s); } return s; }); public override IO FoldWhileIO(Func f, Func<(S State, A Value), bool> predicate, S initialState) => IO.liftVAsync(async env => { var s = initialState; var xs = await runEnumerable.RunAsync(env); await foreach (var x in xs.WithCancellation(env.Token)) { if (!predicate((s, x))) { return s; } s = f(s, x); } return s; }); public override IO FoldUntilIO( Func> f, Func<(S State, A Value), bool> predicate, S initialState) => IO.liftVAsync(async env => { var s = initialState; var xs = await runEnumerable.RunAsync(env); await foreach (var x in xs.WithCancellation(env.Token)) { s = f(x)(s); if (predicate((s, x))) { return s; } } return s; }); public override IO FoldUntilIO(Func f, Func<(S State, A Value), bool> predicate, S initialState) => IO.liftVAsync(async env => { var s = initialState; var xs = await runEnumerable.RunAsync(env); await foreach (var x in xs.WithCancellation(env.Token)) { s = f(s, x); if (predicate((s, x))) { return s; } } return s; }); public override Iterable Choose(Iterable rhs) => new IterableAsyncEnumerable( IO.liftVAsync(async env => { var ls = AsAsyncEnumerable(env.Token); var iter = ls.GetIteratorAsync(); if (await iter.IsEmpty) { return rhs.AsAsyncEnumerable(env.Token); } else { // This has already been evaluated by `IsEmpty` var head = await iter.Head; var tail = (await iter.Tail).Split().AsEnumerable(env.Token); return tail.Prepend(head); } })); /// /// If this sequence is empty, return the other sequence, otherwise return this sequence. /// /// Right hand side of the operator /// A choice between two sequences based [Pure] public override Iterable Choose(Memo rhs) => new IterableAsyncEnumerable( IO.liftVAsync(async env => { var ls = AsAsyncEnumerable(env.Token); var iter = ls.GetIteratorAsync(); if (await iter.IsEmpty) { return rhs.Value.As().AsAsyncEnumerable(env.Token); } else { // This has already been evaluated by `IsEmpty` var head = await iter.Head; var tail = (await iter.Tail).Split().AsEnumerable(env.Token); return tail.Prepend(head); } })); } ================================================ FILE: LanguageExt.Core/Immutable Collections/Iterable/DSL/Iterable.Cast.cs ================================================ using System; using System.Collections.Generic; using System.Linq; namespace LanguageExt; sealed class IterableCast(Iterable Items) : Iterable { internal override bool IsAsync => Items.IsAsync; public override IO CountIO() => Items.CountIO(); public override IO> AsEnumerableIO() { return Items.AsEnumerableIO() .Map(xs => xs as IEnumerable ?? go(xs)); IEnumerable go(IEnumerable xs) { foreach (object? x in xs) { if (x is A y) yield return y; } } } public override IO> AsAsyncEnumerableIO() { return Items.AsAsyncEnumerableIO() .Map(xs => xs as IAsyncEnumerable ?? go(xs)); async IAsyncEnumerable go(IAsyncEnumerable xs) { await foreach (object? x in xs) { if (x is A y) yield return y; } } } public override Iterable Reverse() => new IterableCast(Items.Reverse()); public override Iterable Map(Func f) => Make().Map(f); public override Iterable Filter(Func f) => Make().Filter(f); public override IO FoldWhileIO(Func> f, Func<(S State, A Value), bool> predicate, S initialState) => Make().FoldWhileIO(f, predicate, initialState); public override IO FoldWhileIO(Func f, Func<(S State, A Value), bool> predicate, S initialState) => Make().FoldWhileIO(f, predicate, initialState); public override IO FoldUntilIO(Func> f, Func<(S State, A Value), bool> predicate, S initialState) => Make().FoldUntilIO(f, predicate, initialState); public override IO FoldUntilIO(Func f, Func<(S State, A Value), bool> predicate, S initialState) => Make().FoldUntilIO(f, predicate, initialState); public override Iterable Choose(Iterable rhs) => new IterableAsyncEnumerable( IO.liftVAsync(async env => { var ls = AsAsyncEnumerable(env.Token); var iter = ls.GetIteratorAsync(); if (await iter.IsEmpty) { return rhs.AsAsyncEnumerable(env.Token); } else { // This has already been evaluated by `IsEmpty` var head = await iter.Head; var tail = (await iter.Tail).Split().AsEnumerable(env.Token); return tail.Prepend(head); } })); public override Iterable Choose(Memo rhs) => new IterableAsyncEnumerable( IO.liftVAsync(async env => { var ls = AsAsyncEnumerable(env.Token); var iter = ls.GetIteratorAsync(); if (await iter.IsEmpty) { return rhs.Value.As().AsAsyncEnumerable(env.Token); } else { // This has already been evaluated by `IsEmpty` var head = await iter.Head; var tail = (await iter.Tail).Split().AsEnumerable(env.Token); return tail.Prepend(head); } })); Iterable Make() => IsAsync ? new IterableEnumerable(AsEnumerableIO()) : new IterableAsyncEnumerable(AsAsyncEnumerableIO()); } ================================================ FILE: LanguageExt.Core/Immutable Collections/Iterable/DSL/Iterable.Concat.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using LanguageExt.Traits; namespace LanguageExt; sealed class IterableConcat(Seq> Items) : Iterable { public Seq> Items { get; } = Items; internal override bool IsAsync => Items.Exists(x => x.IsAsync); public override IO CountIO() => +Items.FoldM(0, (s, iter) => iter.CountIO().Map(c => c + s)); public override IO> AsEnumerableIO() { return +go(Items); K> go(Seq> items) => items switch { [] => IO.pure(Enumerable.Empty()), var (h, t) => +h.AsEnumerableIO() >> (xs => xs.Concat * go(t)) }; } public override IO> AsAsyncEnumerableIO() { return go(Items); IO> go(Seq> items) => items switch { [] => IO.pure(AsyncEnumerable.Empty()), var (h, t) => from xs in h.AsAsyncEnumerableIO() from ys in go(t) select xs.Concat(ys) }; } public override Iterable Reverse() => new IterableConcat(Items.Map(xs => xs.Reverse()).Rev()); public override Iterable Map(Func f) => new IterableConcat(Items.Map(xs => xs.Map(f))); public override Iterable Filter(Func f) => new IterableConcat(Items.Map(xs => xs.Filter(f))); public override IO FoldWhileIO(Func> f, Func<(S State, A Value), bool> predicate, S initialState) => IO.liftVAsync(async env => { var s = initialState; foreach (var xxs in Items) { if (xxs.IsAsync) { await foreach (var x in xxs.AsAsyncEnumerable(env.Token)) { if (!predicate((s, x))) { return s; } s = f(x)(s); } } else { foreach (var x in xxs.AsEnumerable(env.Token)) { if (!predicate((s, x))) { return s; } s = f(x)(s); } } } return s; }); public override IO FoldWhileIO(Func f, Func<(S State, A Value), bool> predicate, S initialState) => IO.liftVAsync(async env => { var s = initialState; foreach (var xxs in Items) { if (xxs.IsAsync) { await foreach (var x in xxs.AsAsyncEnumerable(env.Token)) { if (!predicate((s, x))) { return s; } s = f(s, x); } } else { foreach (var x in xxs.AsEnumerable(env.Token)) { if (!predicate((s, x))) { return s; } s = f(s, x); } } } return s; }); public override IO FoldUntilIO(Func> f, Func<(S State, A Value), bool> predicate, S initialState) => IO.liftVAsync(async env => { var s = initialState; foreach (var xxs in Items) { if (xxs.IsAsync) { await foreach (var x in xxs.AsAsyncEnumerable(env.Token)) { s = f(x)(s); if (predicate((s, x))) { return s; } } } else { foreach (var x in xxs.AsEnumerable(env.Token)) { s = f(x)(s); if (predicate((s, x))) { return s; } } } } return s; }); public override IO FoldUntilIO(Func f, Func<(S State, A Value), bool> predicate, S initialState) => IO.liftVAsync(async env => { var s = initialState; foreach (var xxs in Items) { if (xxs.IsAsync) { await foreach (var x in xxs.AsAsyncEnumerable(env.Token)) { s = f(s, x); if (predicate((s, x))) { return s; } } } else { foreach (var x in xxs.AsEnumerable(env.Token)) { s = f(s, x); if (predicate((s, x))) { return s; } } } } return s; }); public override Iterable Choose(Iterable rhs) => new IterableAsyncEnumerable( IO.liftVAsync(async env => { var ls = AsAsyncEnumerable(env.Token); var iter = ls.GetIteratorAsync(); if (await iter.IsEmpty) { return rhs.AsAsyncEnumerable(env.Token); } else { // This has already been evaluated by `IsEmpty` var head = await iter.Head; var tail = (await iter.Tail).Split().AsEnumerable(env.Token); return tail.Prepend(head); } })); public override Iterable Choose(Memo rhs) => new IterableAsyncEnumerable( IO.liftVAsync(async env => { var ls = AsAsyncEnumerable(env.Token); var iter = ls.GetIteratorAsync(); if (await iter.IsEmpty) { return rhs.Value.As().AsAsyncEnumerable(env.Token); } else { // This has already been evaluated by `IsEmpty` var head = await iter.Head; var tail = (await iter.Tail).Split().AsEnumerable(env.Token); return tail.Prepend(head); } })); } ================================================ FILE: LanguageExt.Core/Immutable Collections/Iterable/DSL/Iterable.Enumerable.cs ================================================ #pragma warning disable CS1998 using System; using System.Linq; using System.Collections.Generic; using System.Threading.Tasks; namespace LanguageExt; sealed class IterableEnumerable(IO> runEnumerable) : Iterable { internal override bool IsAsync => false; public override IO CountIO() => AsEnumerableIO().Map(xs => xs.Count()); public override IO> AsEnumerableIO() { // This makes a regular IEnumerable cancellable. return IO.lift(env => go(env, runEnumerable)); static IEnumerable go(EnvIO env, IO> run) { var xs = run.Run(env); foreach (var x in xs) { if (env.Token.IsCancellationRequested) throw new OperationCanceledException(); yield return x; } } } public override IO> AsAsyncEnumerableIO() => AsEnumerableIO().Map(xs => xs.ToAsyncEnumerable()); public override Iterable Reverse() => new IterableEnumerable(AsEnumerableIO().Map(xs => xs.Reverse())); public override Iterable Map(Func f) => new IterableEnumerable(AsEnumerableIO().Map(xs => xs.Select(f))); public override Iterable Filter(Func f) => new IterableEnumerable(AsEnumerableIO().Map(xs => xs.Where(f))); public override IO FoldWhileIO(Func> f, Func<(S State, A Value), bool> predicate, S initialState) => IO.liftVAsync(async env => { var s = initialState; var xs = await runEnumerable.RunAsync(env); foreach (var x in xs) { if(env.Token.IsCancellationRequested) throw new OperationCanceledException(); if (!predicate((s, x))) { return s; } s = f(x)(s); } return s; }); public override IO FoldWhileIO(Func f, Func<(S State, A Value), bool> predicate, S initialState) => IO.liftVAsync(async env => { var s = initialState; var xs = await runEnumerable.RunAsync(env); foreach (var x in xs) { if(env.Token.IsCancellationRequested) throw new OperationCanceledException(); if (!predicate((s, x))) { return s; } s = f(s, x); } return s; }); public override IO FoldUntilIO(Func> f, Func<(S State, A Value), bool> predicate, S initialState) => IO.liftVAsync(async env => { var s = initialState; var xs = await runEnumerable.RunAsync(env); foreach (var x in xs) { if(env.Token.IsCancellationRequested) throw new OperationCanceledException(); s = f(x)(s); if (predicate((s, x))) { return s; } } return s; }); public override IO FoldUntilIO(Func f, Func<(S State, A Value), bool> predicate, S initialState) => IO.liftVAsync(async env => { var s = initialState; var xs = await runEnumerable.RunAsync(env); foreach (var x in xs) { if (env.Token.IsCancellationRequested) throw new OperationCanceledException(); s = f(s, x); if (predicate((s, x))) { return s; } } return s; }); public override Iterable Choose(Iterable rhs) => rhs.IsAsync ? new IterableAsyncEnumerable( IO.liftVAsync(async env => { var ls = AsAsyncEnumerable(env.Token); var iter = ls.GetIteratorAsync(); if (await iter.IsEmpty) { return rhs.AsAsyncEnumerable(env.Token); } else { // This has already been evaluated by `IsEmpty` var head = await iter.Head; var tail = (await iter.Tail).Split().AsEnumerable(env.Token); return tail.Prepend(head); } })) : new IterableEnumerable( IO.lift(env => { var ls = AsEnumerable(env.Token); var iter = ls.GetIterator(); if (iter.IsEmpty) { return rhs.AsEnumerable(env.Token); } else { // This has already been evaluated by `IsEmpty` var head = iter.Head; var tail = iter.Tail.Split().AsEnumerable(); return tail.Prepend(head); } })); public override Iterable Choose(Memo rhs) => new IterableAsyncEnumerable( IO.liftVAsync(async env => { var ls = AsAsyncEnumerable(env.Token); var iter = ls.GetIteratorAsync(); if (await iter.IsEmpty) { return rhs.Value.As().AsAsyncEnumerable(env.Token); } else { // This has already been evaluated by `IsEmpty` var head = await iter.Head; var tail = (await iter.Tail).Split().AsEnumerable(env.Token); return tail.Prepend(head); } })); } ================================================ FILE: LanguageExt.Core/Immutable Collections/Iterable/DSL/Iterable.Nil.cs ================================================ using System; using System.Collections.Generic; using System.Linq; namespace LanguageExt; sealed class IterableNil : Iterable { public static readonly Iterable Default = new IterableNil(); internal override bool IsAsync => false; public override IO CountIO() => IO.pure(0); public override IO> AsEnumerableIO() => IO.pure(Enumerable.Empty()); public override IO> AsAsyncEnumerableIO() => IO.pure(AsyncEnumerable.Empty()); public override Iterable Reverse() => this; public override Iterable Map(Func f) => IterableNil.Default; public override Iterable Filter(Func f) => this; public override IO FoldWhileIO(Func> f, Func<(S State, A Value), bool> predicate, S initialState) => IO.pure(initialState); public override IO FoldWhileIO(Func f, Func<(S State, A Value), bool> predicate, S initialState) => IO.pure(initialState); public override IO FoldUntilIO(Func> f, Func<(S State, A Value), bool> predicate, S initialState) => IO.pure(initialState); public override IO FoldUntilIO(Func f, Func<(S State, A Value), bool> predicate, S initialState) => IO.pure(initialState); public override Iterable Choose(Iterable rhs) => rhs; public override Iterable Choose(Memo rhs) => rhs.Value.As(); } ================================================ FILE: LanguageExt.Core/Immutable Collections/Iterable/DSL/Iterable.Singleton.cs ================================================ #pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously using System; using System.Collections.Generic; namespace LanguageExt; sealed class IterableSingleton(A Value) : Iterable { internal override bool IsAsync => false; public override IO CountIO() => IO.pure(1); public override IO> AsEnumerableIO() => IO.pure>([Value]); public override IO> AsAsyncEnumerableIO() => IO.pure(One(Value)); static async IAsyncEnumerable One(A value) { yield return value; } public override Iterable Reverse() => this; public override Iterable Map(Func f) => new IterableSingleton(f(Value)); public override Iterable Filter(Func f) => f(Value) ? this : Empty; public override IO FoldWhileIO(Func> f, Func<(S State, A Value), bool> predicate, S initialState) => IO.lift(_ => predicate((initialState, Value)) ? f(Value)(initialState) : initialState); public override IO FoldWhileIO(Func f, Func<(S State, A Value), bool> predicate, S initialState) => IO.lift(_ => predicate((initialState, Value)) ? f(initialState, Value) : initialState); public override IO FoldUntilIO(Func> f, Func<(S State, A Value), bool> predicate, S initialState) => IO.lift(_ => f(Value)(initialState)); public override IO FoldUntilIO(Func f, Func<(S State, A Value), bool> predicate, S initialState) => IO.lift(_ => f(initialState, Value)); public override Iterable Choose(Iterable rhs) => this; public override Iterable Choose(Memo rhs) => this; } ================================================ FILE: LanguageExt.Core/Immutable Collections/Iterable/DSL/Iterable.SingletonIO.cs ================================================ #pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously using System; using System.Collections.Generic; using System.Linq; namespace LanguageExt; sealed class IterableSingletonIO(IO Value) : Iterable { internal override bool IsAsync => false; public override IO CountIO() => IO.pure(1); public override IO> AsEnumerableIO() => Value.Map(One); public override IO> AsAsyncEnumerableIO() => Value.Map(OneAsync); static IEnumerable One(A value) { yield return value; } static async IAsyncEnumerable OneAsync(A value) { yield return value; } public override Iterable Reverse() => this; public override Iterable Map(Func f) => new IterableSingletonIO(Value.Map(f)); public override Iterable Filter(Func f) => new IterableEnumerable(AsEnumerableIO().Map(xs => xs.Where(f))); public override IO FoldWhileIO(Func> f, Func<(S State, A Value), bool> predicate, S initialState) => Value.Map(x => predicate((initialState, x)) ? f(x)(initialState) : initialState); public override IO FoldWhileIO(Func f, Func<(S State, A Value), bool> predicate, S initialState) => Value.Map(x => predicate((initialState, x)) ? f(initialState, x) : initialState); public override IO FoldUntilIO(Func> f, Func<(S State, A Value), bool> predicate, S initialState) => Value.Map(x => f(x)(initialState)); public override IO FoldUntilIO(Func f, Func<(S State, A Value), bool> predicate, S initialState) => Value.Map(x => f(initialState, x)); public override Iterable Choose(Iterable rhs) => this; public override Iterable Choose(Memo rhs) => this; } ================================================ FILE: LanguageExt.Core/Immutable Collections/Iterable/DSL/Iterable.Strict.cs ================================================ #pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously using System; using System.Collections.Generic; using System.Threading.Tasks; namespace LanguageExt; sealed class IterableStrict(SeqStrict Items) : Iterable { internal override bool IsAsync => false; public override IO CountIO() => IO.pure(Items.Count); public override Iterable Add(A item) => new IterableStrict((SeqStrict)Items.Add(item)); public override Iterable Cons(A item) => new IterableStrict((SeqStrict)Items.Cons(item)); public override IO> AsEnumerableIO() { return IO.lift(go); IEnumerable go(EnvIO env) { foreach (var x in Items) { if (env.Token.IsCancellationRequested) throw new OperationCanceledException(); yield return x; } } } public override IO> AsAsyncEnumerableIO() { return IO.lift(go); async IAsyncEnumerable go(EnvIO env) { foreach (var x in Items) { if (env.Token.IsCancellationRequested) throw new OperationCanceledException(); yield return x; } } } public override Iterable Reverse() => new IterableStrict(Items.Rev()); public override Iterable Map(Func f) => new IterableStrict(Items.Map(f)); public override Iterable Filter(Func f) => new IterableStrict(Items.Filter(f)); public override IO FoldWhileIO(Func> f, Func<(S State, A Value), bool> predicate, S initialState) => IO.liftVAsync(async env => { var s = initialState; foreach (var x in Items) { if(env.Token.IsCancellationRequested) throw new OperationCanceledException(); if (!predicate((s, x))) { return s; } s = f(x)(s); } return s; }); public override IO FoldWhileIO(Func f, Func<(S State, A Value), bool> predicate, S initialState) => IO.liftVAsync(async env => { var s = initialState; foreach (var x in Items) { if(env.Token.IsCancellationRequested) throw new OperationCanceledException(); if (!predicate((s, x))) { return s; } s = f(s, x); } return s; }); public override IO FoldUntilIO(Func> f, Func<(S State, A Value), bool> predicate, S initialState) => IO.liftVAsync(async env => { var s = initialState; foreach (var x in Items) { if(env.Token.IsCancellationRequested) throw new OperationCanceledException(); s = f(x)(s); if (predicate((s, x))) { return s; } } return s; }); public override IO FoldUntilIO(Func f, Func<(S State, A Value), bool> predicate, S initialState) => IO.liftVAsync(async env => { var s = initialState; foreach (var x in Items) { if(env.Token.IsCancellationRequested) throw new OperationCanceledException(); s = f(s, x); if (predicate((s, x))) { return s; } } return s; }); public override Iterable Choose(Iterable rhs) => Items.IsEmpty ? rhs : this; public override Iterable Choose(Memo rhs) => Items.IsEmpty ? rhs.Value.As() : this; } ================================================ FILE: LanguageExt.Core/Immutable Collections/Iterable/DSL/Iterable.Zip.cs ================================================ using System; using System.Collections.Generic; using System.Linq; namespace LanguageExt; sealed class IterableZip(Iterable First, Iterable Second) : Iterable<(A First, B Second)> { internal override bool IsAsync => First.IsAsync || Second.IsAsync; public override IO CountIO() => IsAsync ? IO.liftVAsync(async env => await (await AsAsyncEnumerableIO().RunAsync(env)).CountAsync(env.Token)) : IO.lift(env => AsEnumerableIO().Run(env).Count()); public override IO> AsEnumerableIO() { return IO.lift(e => go(e, First, Second)); static IEnumerable<(A, B)> go(EnvIO env, Iterable fst, Iterable snd) { using var iterA = fst.AsEnumerable(env.Token).GetEnumerator(); using var iterB = snd.AsEnumerable(env.Token).GetEnumerator(); while (iterA.MoveNext() && iterB.MoveNext()) { yield return (iterA.Current, iterB.Current); } } } public override IO> AsAsyncEnumerableIO() { return (First.IsAsync, Second.IsAsync) switch { (true, true) => IO.lift(e => AsyncAsync(e, First, Second)), (true, false) => IO.lift(e => AsyncSync(e, First, Second)), (false, true) => IO.lift(e => SyncAsync(e, First, Second)), (false, false) => IO.lift(e => SyncSync(e, First, Second).ToAsyncEnumerable()), }; static async IAsyncEnumerable<(A, B)> AsyncAsync(EnvIO env, Iterable fst, Iterable snd) { var iterA = fst.AsAsyncEnumerable(env.Token).GetAsyncEnumerator(env.Token); var iterB = snd.AsAsyncEnumerable(env.Token).GetAsyncEnumerator(env.Token); while (await iterA.MoveNextAsync() && await iterB.MoveNextAsync()) { yield return (iterA.Current, iterB.Current); } } static async IAsyncEnumerable<(A, B)> AsyncSync(EnvIO env, Iterable fst, Iterable snd) { var iterA = fst.AsAsyncEnumerable(env.Token).GetAsyncEnumerator(env.Token); using var iterB = snd.AsEnumerable(env.Token).GetEnumerator(); while (await iterA.MoveNextAsync() && iterB.MoveNext()) { yield return (iterA.Current, iterB.Current); } } static async IAsyncEnumerable<(A, B)> SyncAsync(EnvIO env, Iterable fst, Iterable snd) { using var iterA = fst.AsEnumerable(env.Token).GetEnumerator(); var iterB = snd.AsAsyncEnumerable(env.Token).GetAsyncEnumerator(env.Token); while (iterA.MoveNext() && await iterB.MoveNextAsync()) { yield return (iterA.Current, iterB.Current); } } static IEnumerable<(A, B)> SyncSync(EnvIO env, Iterable fst, Iterable snd) { using var iterA = fst.AsEnumerable(env.Token).GetEnumerator(); using var iterB = snd.AsEnumerable(env.Token).GetEnumerator(); while (iterA.MoveNext() && iterB.MoveNext()) { yield return (iterA.Current, iterB.Current); } } } public override Iterable<(A First, B Second)> Reverse() => Make().Reverse(); public override Iterable Map(Func<(A First, B Second), C> f) => Make().Map(f); public override Iterable<(A First, B Second)> Filter(Func<(A First, B Second), bool> f) => Make().Filter(f); public override IO FoldWhileIO(Func<(A First, B Second), Func> f, Func<(S State, (A First, B Second) Value), bool> predicate, S initialState) => Make().FoldWhileIO(f, predicate, initialState); public override IO FoldWhileIO(Func f, Func<(S State, (A First, B Second) Value), bool> predicate, S initialState) => Make().FoldWhileIO(f, predicate, initialState); public override IO FoldUntilIO(Func<(A First, B Second), Func> f, Func<(S State, (A First, B Second) Value), bool> predicate, S initialState) => Make().FoldUntilIO(f, predicate, initialState); public override IO FoldUntilIO(Func f, Func<(S State, (A First, B Second) Value), bool> predicate, S initialState) => Make().FoldUntilIO(f, predicate, initialState); public override Iterable<(A First, B Second)> Choose(Iterable<(A First, B Second)> rhs) => new IterableAsyncEnumerable<(A First, B Second)>( IO.liftVAsync(async env => { var ls = AsAsyncEnumerable(env.Token); var iter = ls.GetIteratorAsync(); if (await iter.IsEmpty) { return rhs.AsAsyncEnumerable(env.Token); } else { // This has already been evaluated by `IsEmpty` var head = await iter.Head; var tail = (await iter.Tail).Split().AsEnumerable(env.Token); return tail.Prepend(head); } })); public override Iterable<(A First, B Second)> Choose(Memo rhs) => new IterableAsyncEnumerable<(A First, B Second)>( IO.liftVAsync(async env => { var ls = AsAsyncEnumerable(env.Token); var iter = ls.GetIteratorAsync(); if (await iter.IsEmpty) { return rhs.Value.As().AsAsyncEnumerable(env.Token); } else { // This has already been evaluated by `IsEmpty` var head = await iter.Head; var tail = (await iter.Tail).Split().AsEnumerable(env.Token); return tail.Prepend(head); } })); Iterable<(A First, B Second)> Make() => IsAsync ? new IterableAsyncEnumerable<(A First, B Second)>(AsAsyncEnumerableIO()) : new IterableEnumerable<(A First, B Second)>(AsEnumerableIO()); } ================================================ FILE: LanguageExt.Core/Immutable Collections/Iterable/Extensions/Iterable.Extensions.MapApply.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public static partial class IterableExtensions { /// /// Functor map operation /// /// /// Unwraps the value within the functor, passes it to the map function `f` provided, and /// then takes the mapped value and wraps it back up into a new functor. /// /// Functor to map /// Mapping function /// Mapped functor public static Iterable Map(this Func f, K ma) => Functor.map(f, ma).As(); /// /// Functor map operation /// /// /// Unwraps the value within the functor, passes it to the map function `f` provided, and /// then takes the mapped value and wraps it back up into a new functor. /// /// Functor to map /// Mapping function /// Mapped functor public static Iterable Map(this Func f, Iterable ma) => Functor.map(f, ma).As(); /// /// Applicative action: runs the first applicative, ignores the result, and returns the second applicative /// public static Iterable Action(this K ma, K mb) => Applicative.action(ma, mb).As(); /// /// Applicative action: runs the first applicative, ignores the result, and returns the second applicative /// public static Iterable Action(this Iterable ma, K mb) => Applicative.action(ma, mb).As(); /// /// Applicative functor apply operation /// /// /// Unwraps the value within the `ma` applicative-functor, passes it to the unwrapped function(s) within `mf`, and /// then takes the resulting value and wraps it back up into a new applicative-functor. /// /// Value(s) applicative functor /// Mapping function(s) /// Mapped applicative functor public static Iterable Apply(this Iterable> mf, K ma) => Applicative.apply(mf, ma).As(); /// /// Applicative functor apply operation /// /// /// Unwraps the value within the `ma` applicative-functor, passes it to the unwrapped function(s) within `mf`, and /// then takes the resulting value and wraps it back up into a new applicative-functor. /// /// Value(s) applicative functor /// Mapping function(s) /// Mapped applicative functor public static Iterable Apply(this K> mf, K ma) => Applicative.apply(mf, ma).As(); } ================================================ FILE: LanguageExt.Core/Immutable Collections/Iterable/Extensions/Iterable.Extensions.cs ================================================ #pragma warning disable CS0693 // Type parameter has the same name as the type parameter from outer type using System; using System.Collections.Generic; using LanguageExt.Traits; using System.Diagnostics.Contracts; using static LanguageExt.Prelude; namespace LanguageExt; public static partial class IterableExtensions { public static Iterable As(this K xs) => (Iterable)xs; public static Iterable AsIterable(this IEnumerable xs) => new IterableEnumerable(IO.pure(xs)); public static Iterable AsIterable(this IAsyncEnumerable xs) => new IterableAsyncEnumerable(IO.pure(xs)); public static Iterable Flatten(this Iterable> ma) => ma.Bind(identity); /// sequence /// sequence item type extension(Iterable list) { /// /// Applies the given function 'selector' to each element of the sequence. Returns the sequence /// comprised of the results for each element where the function returns Some(f(x)). /// /// Selector function /// Mapped and filtered sequence [Pure] public Iterable Choose(Func> selector) => Iterable.choose(list, selector); [Pure] public Iterable Rev() => Iterable.rev(list); } /// sequence /// sequence item type extension(Iterable list) where A : Monoid { /// /// Given a structure with elements whose type is a `Monoid`, combine them /// via the monoid's `Append` operator. This fold is right-associative and /// lazy in the accumulator. When you need a strict left-associative fold, /// use 'foldMap'' instead, with 'id' as the map. /// public A Fold() => list.FoldIO().Run(); /// /// Given a structure with elements whose type is a `Monoid`, combine them /// via the monoid's `Append` operator. This fold is right-associative and /// lazy in the accumulator. When you need a strict left-associative fold, /// use 'foldMap'' instead, with 'id' as the map. /// public IO FoldIO() => list.FoldMapIO(identity); /// /// Given a structure with elements whose type is a `Monoid`, combine them /// via the monoid's `Append` operator. This fold is right-associative and /// lazy in the accumulator. When you need a strict left-associative fold, /// use 'foldMap'' instead, with 'id' as the map. /// public A FoldWhile(Func<(A State, A Value), bool> predicate) => list.FoldWhileIO(predicate).Run(); /// /// Given a structure with elements whose type is a `Monoid`, combine them /// via the monoid's `Append` operator. This fold is right-associative and /// lazy in the accumulator. When you need a strict left-associative fold, /// use 'foldMap'' instead, with 'id' as the map. /// public IO FoldWhileIO(Func<(A State, A Value), bool> predicate) => list.FoldMapWhileIO(identity, predicate); /// /// Given a structure with elements whose type is a `Monoid`, combine them /// via the monoid's `Append` operator. This fold is right-associative and /// lazy in the accumulator. When you need a strict left-associative fold, /// use 'foldMap'' instead, with 'id' as the map. /// public A FoldUntil(Func<(A State, A Value), bool> predicate) => list.FoldUntilIO(predicate).Run(); /// /// Given a structure with elements whose type is a `Monoid`, combine them /// via the monoid's `Append` operator. This fold is right-associative and /// lazy in the accumulator. When you need a strict left-associative fold, /// use 'foldMap'' instead, with 'id' as the map. /// public IO FoldUntilIO(Func<(A State, A Value), bool> predicate) => list.FoldMapUntilIO(identity, predicate); } } ================================================ FILE: LanguageExt.Core/Immutable Collections/Iterable/Iterable.Module.cs ================================================ using System; using LanguageExt.Traits; using System.Collections.Generic; using static LanguageExt.Prelude; using System.Diagnostics.Contracts; using System.Runtime.CompilerServices; namespace LanguageExt; /// /// Cons sequence module /// Represents a sequence of values in a similar way to IEnumerable, but without the /// issues of multiple evaluation for key LINQ operators like Skip, Count, etc. /// /// Type of the values in the sequence public partial class Iterable { /// /// Monadic join /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Iterable flatten(Iterable> ma) => ma.Bind(identity); /// /// Create an empty sequence /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Iterable empty() => Iterable.Empty; /// /// Create an empty sequence /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Iterable singleton(A value) => new IterableSingleton(value); /// /// Create a new empty sequence /// /// sequence [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Iterable create() => Iterable.Empty; /// /// Create a sequence from an initial set of items /// /// Items /// sequence [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Iterable create(params A[] items) { if (items.Length == 0) return Iterable.Empty; var nitems = new A[items.Length]; System.Array.Copy(items, nitems, items.Length); return Iterable.FromSpan(items); } /// /// Create a sequence from an initial set of items /// /// Items /// sequence [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Iterable createRange(ReadOnlySpan items) => items.Length == 0 ? Iterable.Empty : Iterable.FromSpan(items); /// /// Create a sequence from an initial set of items /// /// Items /// sequence [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Iterable createRange(IEnumerable items) => new IterableEnumerable(IO.pure(items)); /// /// Create a sequence from an initial set of items /// /// Items /// sequence [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Iterable createRange(IAsyncEnumerable items) => new IterableAsyncEnumerable(IO.pure(items)); /// /// Create a sequence from an initial set of items /// /// Items /// sequence [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Iterable createRange(IO> items) => new IterableAsyncEnumerable(items); /// /// Generates a sequence of A using the provided delegate to initialise /// each item. /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Iterable generate(int count, Func generator) => IterableExtensions.AsIterable(Range(0, count)).Map(generator); /// /// Generates a sequence that contains one repeated value. /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Iterable repeat(A item, int count) => IterableExtensions.AsIterable(Range(0, count)).Map(_ => item); /// /// Get the item at the head (first) of the sequence or None if the sequence is empty /// /// sequence /// Optional head item [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Option head(Iterable list) => list.Head; /// /// Applies the given function 'selector' to each element of the sequence. Returns the sequence /// of results for each element where the function returns Some(f(x)). /// /// sequence item type /// sequence /// Selector function /// Mapped and filtered sequence [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Iterable choose(Iterable list, Func> selector) => list.Map(selector).Filter(t => t.IsSome).Map(t => t.Value!); /// /// Reverses the sequence (Reverse in LINQ) /// /// sequence item type /// sequence to reverse /// Reversed sequence [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Iterable rev(Iterable list) => list.Reverse(); /// /// Joins two sequences together either into a single sequence using the join /// function provided /// /// First sequence to join /// Second sequence to join /// Join function /// Joined sequence [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Iterable zip(Iterable list, Iterable other, Func zipper) => list.Zip(other, zipper); /// /// Joins two sequences together either into an sequence of tuples /// /// First sequence to join /// Second sequence to join /// Join function /// Joined sequence of tuples [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Iterable<(A First, B Second)> zip(Iterable list, Iterable other) => list.Zip(other, (t, u) => (t, u)); /// /// Return a new sequence with all duplicate values removed /// /// sequence item type /// sequence /// A new sequence with all duplicate values removed [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Iterable distinct(Iterable list) => list.Distinct(); /// /// Return a new sequence with all duplicate values removed /// /// sequence item type /// sequence /// A new sequence with all duplicate values removed [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Iterable distinct(Iterable list) where EqA : Eq => list.Distinct(); /// /// Returns a new sequence with the first 'count' items from the sequence provided /// /// sequence item type /// sequence /// Number of items to take /// A new sequence with the first 'count' items from the sequence provided [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Iterable take(Iterable list, int count) => list.Take(count); /// /// Iterate the sequence, yielding items if they match the predicate provided, and stopping /// as soon as one doesn't /// /// sequence item type /// sequence /// Number of items to take /// A new sequence with the first items that match the predicate [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Iterable takeWhile(Iterable list, Func pred) => list.TakeWhile(pred); /// /// Iterate the sequence, yielding items if they match the predicate provided, and stopping /// as soon as one doesn't. An index value is also provided to the predicate function. /// /// sequence item type /// sequence /// Number of items to take /// A new sequence with the first items that match the predicate [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Iterable takeWhile(Iterable list, Func pred) => list.TakeWhile(pred); } ================================================ FILE: LanguageExt.Core/Immutable Collections/Iterable/Iterable.cs ================================================ using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics.Contracts; using System.Linq; using System.Numerics; using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; using LanguageExt.ClassInstances; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; /// /// Lazy sequence /// /// /// This is a lightweight wrapper around `IEnumerable` that also implements traits /// that make it play nice with other types in this library: Monad, Traversable, etc. /// /// Type of the values in the sequence [CollectionBuilder(typeof(Iterable), nameof(Iterable.createRange))] public abstract class Iterable : IEnumerable, IAsyncEnumerable, Monoid>, IComparable>, IAdditiveIdentity, Iterable>, IComparisonOperators, Iterable, bool>, IAdditionOperators, Iterable, Iterable>, K { int? hashCode; /// /// True if this iterable or any component part of the structure has asynchonicity. /// internal abstract bool IsAsync { get; } /// /// Create an iterable from a span /// public static Iterable FromSpan(ReadOnlySpan ma) => new IterableEnumerable(IO.pure>(ma.ToArray())); /// /// Number of items in the sequence. /// /// /// NOTE: This will force evaluation of the sequence /// [Pure] public int Count() => CountIO().Run(); /// /// Number of items in the sequence. /// [Pure] public abstract IO CountIO(); /// /// Stream as an enumerable /// [Pure] public IEnumerable AsEnumerable(CancellationToken token = default) { using var env = EnvIO.New(token: token); var xs = AsEnumerableIO().Run(); foreach (var x in xs) { if(env.Token.IsCancellationRequested) throw new OperationCanceledException(); yield return x; } } /// /// Stream as an enumerable /// [Pure] public abstract IO> AsEnumerableIO(); /// /// Stream as an enumerable /// [Pure] public async IAsyncEnumerable AsAsyncEnumerable([EnumeratorCancellation] CancellationToken token = default) { using var env = EnvIO.New(token: token); var xs = await AsAsyncEnumerableIO().RunAsync(env); await foreach (var x in xs.WithCancellation(token)) { yield return x; } } /// /// Stream as an enumerable /// [Pure] public abstract IO> AsAsyncEnumerableIO(); /// /// Reverse the sequence /// [Pure] public abstract Iterable Reverse(); /// /// Add an item to the end of the sequence /// /// /// This does not force evaluation of the whole lazy sequence, nor does it cause /// exponential iteration issues when repeated adds occur. /// [Pure] public virtual Iterable Add(A item) => new IterableAdd( new SeqStrict(new A[8], 8, 0, 0, 0), this, new SeqStrict([item, default!, default!, default!, default!, default!, default!, default!], 0, 1, 0, 0)); /// /// Add an item to the beginning of the sequence /// /// /// This does not force evaluation of the whole lazy sequence, nor does it cause /// exponential iteration issues when repeated cons occur. /// [Pure] public virtual Iterable Cons(A item) => new IterableAdd( new SeqStrict([default!, default!, default!, default!, default!, default!, default!, item], 7, 1, 0, 0), this, new SeqStrict(new A[8], 0, 0, 0, 0)); /// /// Impure iteration of the bound values in the structure /// /// /// Returns the original unmodified structure /// public Unit Iter(Action f) => IterIO(f).Run(); /// /// Pure iteration of the bound values in the structure /// /// /// Returns the original unmodified structure /// [Pure] public IO IterIO(Action f) => IsAsync ? IO.liftVAsync(async env => { await foreach (var x in AsAsyncEnumerable(env.Token)) { f(x); } return unit; }) : IO.lift(env => { foreach (var x in AsEnumerable(env.Token)) { f(x); } return unit; }); /// /// Impure iteration of the bound values in the structure /// /// /// Returns the original unmodified structure /// public Unit Iter(Action f) => IterIO(f).Run(); /// /// Pure iteration of the bound values in the structure /// /// /// Returns the original unmodified structure /// [Pure] public IO IterIO(Action f, int offset = 0) => IsAsync ? IO.liftVAsync(async env => { var ix = offset; await foreach (var x in AsAsyncEnumerable(env.Token)) { f(x, ix++); } return unit; }) : IO.lift(env => { var ix = offset; foreach (var x in AsEnumerable(env.Token)) { f(x, ix++); } return unit; }); /// /// Map the sequence using the function provided /// /// /// Mapping function /// Mapped sequence [Pure] public abstract Iterable Map(Func f); /// /// Map the sequence using the function provided /// /// /// Mapping function /// Mapped sequence [Pure] public Iterable Map(Func f, int offset = 0) => Zip(Iterable.createRange(Enumerable.InfiniteSequence(offset, 1))) .Map(p => f(p.First, p.Second)); /// /// Filter the items in the sequence /// /// Predicate to apply to the items /// Filtered sequence [Pure] public abstract Iterable Filter(Func f); /// /// Equality test /// [Pure] public IO EqualsIO(Iterable? other) where EqA : Eq => other is null ? IO.pure(false) : ReferenceEquals(this, other) ? IO.pure(true) : (IsAsync, IsAsync) switch { (true, true) => IO.liftVAsync(async env => await AsAsyncEnumerable(env.Token).SequenceEqualAsync(other.AsAsyncEnumerable(env.Token), Eq.Comparer())), (true, false) => IO.liftVAsync(async env => await AsAsyncEnumerable(env.Token).SequenceEqualAsync(other.AsAsyncEnumerable(env.Token), Eq.Comparer())), (false, true) => IO.liftVAsync(async env => await AsAsyncEnumerable(env.Token).SequenceEqualAsync(other.AsAsyncEnumerable(env.Token), Eq.Comparer())), (false, false) => IO.lift(env => AsEnumerable(env.Token).SequenceEqual(other.AsEnumerable(env.Token), Eq.Comparer())) }; /// /// Equality test /// [Pure] public IO EqualsIO(Iterable? other) => EqualsIO>(other); /// /// Equality test /// [Pure] public bool Equals(Iterable? other) where EqA : Eq => EqualsIO(other).Run(); /// /// Equality test /// [Pure] public bool Equals(Iterable? other) => EqualsIO>(other).Run(); [Pure] public IAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken = default) => AsAsyncEnumerable(cancellationToken).GetAsyncEnumerator(cancellationToken); [Pure] public override bool Equals(object? obj) => obj is Iterable rhs && Equals(rhs); [Pure] public static bool operator ==(Iterable? lhs, Iterable? rhs) => (lhs, rhs) switch { (null, null) => true, (null, _) => false, (_, null) => false, _ => lhs.Equals(rhs) }; [Pure] public static bool operator !=(Iterable? lhs, Iterable? rhs) => !(lhs == rhs); /// /// Semigroup combine two iterables (concatenate) /// [Pure] public Iterable Combine(Iterable y) => Concat(y); /// /// Add a range of items to the end of the sequence /// [Pure] public Iterable Concat(IEnumerable items) => Concat(new IterableEnumerable(IO.pure(items))); /// /// Add a range of items to the end of the sequence /// [Pure] public Iterable Concat(IAsyncEnumerable items) => Concat(new IterableAsyncEnumerable(IO.pure(items))); /// /// Add a range of items to the end of the sequence /// [Pure] public Iterable Concat(Iterable items) => (this, items) switch { (IterableConcat l, IterableConcat r) => new IterableConcat(l.Items + r.Items), (IterableConcat l, var r) => new IterableConcat(l.Items.Add(r)), (var l, IterableConcat r) => new IterableConcat(l.Cons(r.Items)), var (l, r) => new IterableConcat(Seq(l, r)) }; /// /// Return a new sequence with all duplicate values removed /// /// sequence item type /// A new sequence with all duplicate values removed [Pure] public Iterable Distinct() where EqA : Eq => IsAsync ? new IterableAsyncEnumerable(AsAsyncEnumerableIO().Map(xs => xs.Distinct(Eq.Comparer()))) : new IterableEnumerable(AsEnumerableIO().Map(xs => xs.Distinct(Eq.Comparer()))); /// /// Return a new sequence with all duplicate values removed /// /// sequence item type /// A new sequence with all duplicate values removed [Pure] public Iterable Distinct() => Distinct>(); /// /// Map each element of a structure to an action, evaluate these actions from /// left to right, and collect the results. /// /// /// NOTE: This method will eagerly evaluate the iterable. If you're working with /// an asynchronous sequence, then it is advised to use `TraverseIO`. /// /// /// Traversable structure /// Applicative functor trait /// Bound value (output) [Pure] public K> Traverse(Func> f) where F : Applicative { return FoldIO(add, F.Pure(Iterable.Empty)).Run(); Func>, K>> add(A value) => state => Applicative.lift((bs, b) => bs.Add(b), state, f(value)); } /// /// Map each element of a structure to an action, evaluate these actions from /// left to right, and collect the results. /// /// /// NOTE: This method will eagerly evaluate the iterable. If you're working with /// an asynchronous sequence, then it is advised to use `TraverseIO`. /// /// /// Traversable structure /// Monad trait /// Bound value (output) [Pure] public K> TraverseM(Func> f) where M : Monad { return FoldIO(add, M.Pure(Iterable.Empty)).Run(); Func>, K>> add(A value) => state => state.Bind(bs => f(value).Map(bs.Add)); } /// /// Map each element of a structure to an action, evaluate these actions from /// left to right, and collect the results. /// /// /// Traversable structure /// Monad trait /// Bound value (output) [Pure] public K> TraverseIO(Func> f, K kta) where F : MonadIO { var ta = +kta; return F.LiftIO(ta.FoldIO(add, F.Pure(Iterable.Empty))).Flatten(); Func>, K>> add(A value) => state => state.Bind( bs => f(value).Bind( b => F.Pure(bs.Add(b)))); } /// /// Monadic bind (flatmap) of the sequence /// /// Bound return value type /// Bind function /// Flat-mapped sequence [Pure] public Iterable Bind(Func> f) { return IsAsync ? new IterableAsyncEnumerable(IO.lift(async)) : new IterableAsyncEnumerable(IO.lift(sync)); async IAsyncEnumerable async(EnvIO env) { await foreach (var a in AsAsyncEnumerable(env.Token)) { if (env.Token.IsCancellationRequested) throw new OperationCanceledException(); var mb = +f(a); if (mb.IsAsync) { await foreach (var b in mb.AsAsyncEnumerable(env.Token)) { if (env.Token.IsCancellationRequested) throw new OperationCanceledException(); yield return b; } } else { foreach (var b in mb.AsEnumerable(env.Token)) { if (env.Token.IsCancellationRequested) throw new OperationCanceledException(); yield return b; } } } } async IAsyncEnumerable sync(EnvIO env) { foreach (var a in AsEnumerable(env.Token)) { if (env.Token.IsCancellationRequested) throw new OperationCanceledException(); var mb = +f(a); if (mb.IsAsync) { await foreach (var b in mb.AsAsyncEnumerable(env.Token)) { if (env.Token.IsCancellationRequested) throw new OperationCanceledException(); yield return b; } } else { foreach (var b in mb.AsEnumerable(env.Token)) { if (env.Token.IsCancellationRequested) throw new OperationCanceledException(); yield return b; } } } } } /// /// Monadic bind (flatmap) of the sequence /// /// Bound return value type /// Bind function /// Flat-mapped sequence [Pure] public Iterable Bind(Func> f) { return IsAsync ? new IterableAsyncEnumerable(IO.lift(async)) : new IterableAsyncEnumerable(IO.lift(sync)); async IAsyncEnumerable async(EnvIO env) { await foreach (var a in AsAsyncEnumerable(env.Token)) { if (env.Token.IsCancellationRequested) throw new OperationCanceledException(); var mb = f(a); if (mb.IsAsync) { await foreach (var b in mb.AsAsyncEnumerable(env.Token)) { if (env.Token.IsCancellationRequested) throw new OperationCanceledException(); yield return b; } } else { foreach (var b in mb.AsEnumerable(env.Token)) { if (env.Token.IsCancellationRequested) throw new OperationCanceledException(); yield return b; } } } } async IAsyncEnumerable sync(EnvIO env) { foreach (var a in AsEnumerable(env.Token)) { if (env.Token.IsCancellationRequested) throw new OperationCanceledException(); var mb = f(a); if (mb.IsAsync) { await foreach (var b in mb.AsAsyncEnumerable(env.Token)) { if (env.Token.IsCancellationRequested) throw new OperationCanceledException(); yield return b; } } else { foreach (var b in mb.AsEnumerable(env.Token)) { if (env.Token.IsCancellationRequested) throw new OperationCanceledException(); yield return b; } } } } } /// /// Fold over the sequence from the left, accumulating state in `f` /// /// Fold function to apply to each item in the sequence /// Continue while the predicate returns true for any pair of value and state. /// This is tested before the value is processed and the state is updated. So, use `FoldWhile*` for pre-assertions. /// /// Initial state value /// State value type /// Resulting state public S FoldWhile( Func> f, Func<(S State, A Value), bool> predicate, S initialState) => FoldWhileIO(f, predicate, initialState).Run(); /// /// Fold over the sequence from the left, accumulating state in `f` /// /// Fold function to apply to each item in the sequence /// Continue while the predicate returns true for any pair of value and state. /// This is tested before the value is processed and the state is updated. So, use `FoldWhile*` for pre-assertions. /// /// Initial state value /// State value type /// Resulting state public S FoldWhile( Func f, Func<(S State, A Value), bool> predicate, S initialState) => FoldWhileIO(f, predicate, initialState).Run(); /// /// Fold over the sequence from the left, accumulating state in `f` /// /// Fold function to apply to each item in the sequence /// Continue while the predicate returns true for any pair of value and state. /// This is tested before the value is processed and the state is updated. So, use `FoldWhile*` for pre-assertions. /// /// Initial state value /// State value type /// Resulting state public abstract IO FoldWhileIO( Func> f, Func<(S State, A Value), bool> predicate, S initialState); /// /// Fold over the sequence from the left, accumulating state in `f` /// /// Fold function to apply to each item in the sequence /// Continue while the predicate returns true for any pair of value and state. /// This is tested before the value is processed and the state is updated. So, use `FoldWhile*` for pre-assertions. /// /// Initial state value /// State value type /// Resulting state public abstract IO FoldWhileIO( Func f, Func<(S State, A Value), bool> predicate, S initialState); /// /// Fold over the sequence from the left, accumulating state in `f` /// /// Fold function to apply to each item in the sequence /// Continue while the predicate returns true for any pair of value and state. /// This is tested before the value is processed and the state is updated. So, use `FoldWhile*` for pre-assertions. /// /// Initial state value /// State value type /// Resulting state public S FoldUntil( Func> f, Func<(S State, A Value), bool> predicate, S initialState) => FoldUntilIO(f, predicate, initialState).Run(); /// /// Fold over the sequence from the left, accumulating state in `f` /// /// Fold function to apply to each item in the sequence /// Continue while the predicate returns true for any pair of value and state. /// This is tested before the value is processed and the state is updated. So, use `FoldWhile*` for pre-assertions. /// /// Initial state value /// State value type /// Resulting state public S FoldUntil( Func f, Func<(S State, A Value), bool> predicate, S initialState) => FoldUntilIO(f, predicate, initialState).Run(); /// /// Fold over the sequence from the left, accumulating state in `f` /// /// Fold function to apply to each item in the sequence /// Continue while the predicate returns true for any pair of value and state. /// This is tested before the value is processed and the state is updated. So, use `FoldWhile*` for pre-assertions. /// /// Initial state value /// State value type /// Resulting state public abstract IO FoldUntilIO( Func> f, Func<(S State, A Value), bool> predicate, S initialState); /// /// Fold over the sequence from the left, accumulating state in `f` /// /// Fold function to apply to each item in the sequence /// Continue while the predicate returns true for any pair of value and state. /// This is tested before the value is processed and the state is updated. So, use `FoldWhile*` for pre-assertions. /// /// Initial state value /// State value type /// Resulting state public abstract IO FoldUntilIO( Func f, Func<(S State, A Value), bool> predicate, S initialState); /// /// Fold until the `Option` returns `None` /// /// Fold function /// Initial state for the fold /// Foldable structure /// State type /// Aggregated value public S FoldMaybe( Func>> f, S initialState) => FoldMaybeIO(f, initialState).Run(); /// /// Fold until the `Option` returns `None` /// /// Fold function /// Initial state for the fold /// Foldable structure /// State type /// Aggregated value public S FoldMaybe( Func> f, S initialState) => FoldMaybeIO(f, initialState).Run(); /// /// Fold until the `Option` returns `None` /// /// Fold function /// Initial state for the fold /// Foldable structure /// State type /// Aggregated value public IO FoldMaybeIO( Func>> f, S initialState) => FoldWhileIO<(bool IsSome, S Value)>( a => s => f(s.Value)(a) switch { { IsSome: true, Case: S value } => (true, value), _ => (false, s.Value) }, s => s.State.IsSome, (true, initialState)) .Map(s => s.Value); /// /// Fold until the `Option` returns `None` /// /// Fold function /// Initial state for the fold /// Foldable structure /// State type /// Aggregated value public IO FoldMaybeIO( Func> f, S initialState) => FoldWhileIO<(bool IsSome, S Value)>( (s, a) => f(s.Value, a) switch { { IsSome: true, Case: S value } => (true, value), _ => (false, s.Value) }, s => s.State.IsSome, (true, initialState)) .Map(s => s.Value); /// /// Same behaviour as `Fold` but the fold operation returns a monadic type and allows /// early exit of the operation once the predicate function becomes `false` for the /// state/value pair /// public K FoldWhileM( Func>> f, Func predicate, S initialState) where M : MonadIO { return FoldWhileIO(acc, s => predicate(s.Value), Monad.pure) .Bind(f1 => f1(initialState)); Func>, Func>> acc(A value) => bind => state => Monad.bind(f(value)(state), bind); } /// /// Same behaviour as `Fold` but the fold operation returns a monadic type and allows /// early exit of the operation once the predicate function becomes `false` for the /// state/value pair /// public K FoldWhileM( Func> f, Func predicate, S initialState) where M : MonadIO { return FoldWhileIO(acc, s => predicate(s.Value), Monad.pure) .Bind(f1 => f1(initialState)); Func>, Func>> acc(A value) => bind => state => Monad.bind(f(state, value), bind); } /// /// Same behaviour as `Fold` but the fold operation returns a monadic type and allows /// early exit of the operation once the predicate function becomes `false` for the /// state/value pair /// public K FoldUntilM( Func>> f, Func predicate, S initialState) where M : MonadIO { return FoldUntilIO(acc, s => predicate(s.Value), Monad.pure).Bind(f1 => f1(initialState)); Func>, Func>> acc(A value) => bind => state => Monad.bind(f(value)(state), bind); } /// /// Same behaviour as `Fold` but the fold operation returns a monadic type and allows /// early exit of the operation once the predicate function becomes `false` for the /// state/value pair /// public K FoldUntilM( Func> f, Func predicate, S initialState) where M : MonadIO { return FoldUntilIO(acc, s => predicate(s.Value), Monad.pure).Bind(f1 => f1(initialState)); Func>, Func>> acc(A value) => bind => state => Monad.bind(f(state, value), bind); } /// /// Right-associative fold of a structure, lazy in the accumulator. /// /// In the case of lists, 'Fold', when applied to a binary operator, a /// starting value (typically the right-identity of the operator), and a /// list, reduces the list using the binary operator, from right to left. /// public S Fold(Func> f, S initialState) => FoldIO(f, initialState).Run(); /// /// Right-associative fold of a structure, lazy in the accumulator. /// /// In the case of lists, 'Fold', when applied to a binary operator, a /// starting value (typically the right-identity of the operator), and a /// list, reduces the list using the binary operator, from right to left. /// public S Fold(Func f, S initialState) => FoldIO(f, initialState).Run(); /// /// Right-associative fold of a structure, lazy in the accumulator. /// /// In the case of lists, 'Fold', when applied to a binary operator, a /// starting value (typically the right-identity of the operator), and a /// list, reduces the list using the binary operator, from right to left. /// public IO FoldIO(Func> f, S initialState) => FoldWhileIO(f, _ => true, initialState); /// /// Right-associative fold of a structure, lazy in the accumulator. /// /// In the case of lists, 'Fold', when applied to a binary operator, a /// starting value (typically the right-identity of the operator), and a /// list, reduces the list using the binary operator, from right to left. /// public IO FoldIO(Func f, S initialState) => FoldWhileIO(f, _ => true, initialState); /// /// Right-associative fold of a structure, lazy in the accumulator. /// /// In the case of lists, 'Fold', when applied to a binary operator, a /// starting value (typically the right-identity of the operator), and a /// list, reduces the list using the binary operator, from right to left. /// public K FoldM( Func>> f, S initialState) where M : MonadIO { return FoldIO(acc, Monad.pure).Bind(f => f(initialState)); Func>, Func>> acc(A value) => bind => state => Monad.bind(f(value)(state), bind); } /// /// Right-associative fold of a structure, lazy in the accumulator. /// /// In the case of lists, 'Fold', when applied to a binary operator, a /// starting value (typically the right-identity of the operator), and a /// list, reduces the list using the binary operator, from right to left. /// public K FoldM( Func> f, S initialState) where M : MonadIO { return FoldIO(acc, Monad.pure).Bind(f => f(initialState)); Func>, Func>> acc(A value) => bind => state => Monad.bind(f(state, value), bind); } /// /// Map each element of the structure into a monoid, and combine the /// results with `Append`. This fold is right-associative and lazy in the /// accumulator. For strict left-associative folds consider `FoldMapBack` /// instead. /// public B FoldMap(Func f) where B : Monoid => FoldMapIO(f).Run(); /// /// Map each element of the structure into a monoid, and combine the /// results with `Append`. This fold is right-associative and lazy in the /// accumulator. For strict left-associative folds consider `FoldMapBack` /// instead. /// public IO FoldMapIO(Func f) where B : Monoid => FoldIO(x => a => f(x).Combine(a), B.Empty); /// /// Map each element of the structure into a monoid, and combine the /// results with `Append`. This fold is right-associative and lazy in the /// accumulator. For strict left-associative folds consider `FoldMapBack` /// instead. /// public B FoldMapWhile(Func f, Func<(B State, A Value), bool> predicate) where B : Monoid => FoldMapWhileIO(f, predicate).Run(); /// /// Map each element of the structure into a monoid, and combine the /// results with `Append`. This fold is right-associative and lazy in the /// accumulator. For strict left-associative folds consider `FoldMapBack` /// instead. /// public IO FoldMapWhileIO(Func f, Func<(B State, A Value), bool> predicate) where B : Monoid => FoldWhileIO(x => a => f(x).Combine(a), predicate, B.Empty); /// /// Map each element of the structure into a monoid, and combine the /// results with `Append`. This fold is right-associative and lazy in the /// accumulator. For strict left-associative folds consider `FoldMapBack` /// instead. /// public B FoldMapUntil(Func f, Func<(B State, A Value), bool> predicate) where B : Monoid => FoldMapUntilIO(f, predicate).Run(); /// /// Map each element of the structure into a monoid, and combine the /// results with `Append`. This fold is right-associative and lazy in the /// accumulator. For strict left-associative folds consider `FoldMapBack` /// instead. /// public IO FoldMapUntilIO(Func f, Func<(B State, A Value), bool> predicate) where B : Monoid => FoldUntilIO(x => a => f(x).Combine(a), predicate, B.Empty); /// /// Returns true if the sequence has items in it /// /// True if the sequence has items in it [Pure] public bool Any() => this.Exists(_ => true); /// /// Inject a value in between each item in the sequence /// /// Sequence to inject values into /// Item to inject /// Bound type /// A sequence with the values injected [Pure] public Iterable Intersperse(A value) { return IsAsync ? new IterableAsyncEnumerable(IO.lift(goAsync)) : new IterableEnumerable(IO.lift(goSync)); IEnumerable goSync(EnvIO env) { var isFirst = true; foreach(var item in AsEnumerable(env.Token)) { if (!isFirst) { yield return value; } yield return item; isFirst = false; } } async IAsyncEnumerable goAsync(EnvIO env) { { var isFirst = true; await foreach(var item in AsAsyncEnumerable(env.Token)) { if (!isFirst) { yield return value; } yield return item; isFirst = false; } } } } [Pure] public IO CompareToIO(object? obj) => obj is Iterable rhs ? CompareToIO(rhs) : IO.pure(1); [Pure] public int CompareTo(object? obj) => obj is Iterable rhs ? CompareTo(rhs) : 1; [Pure] public IO CompareToIO(Iterable? other) => CompareToIO>(other); [Pure] public int CompareTo(Iterable? other) => CompareTo>(other); /// /// Compare to another sequence /// [Pure] public IO CompareToIO(Iterable? rhs) where OrdA : Ord { if (rhs is null) return IO.pure(1); return IsAsync || rhs.IsAsync ? IO.liftVAsync(async env => { var iterA = GetAsyncEnumerator(env.Token); var iterB = rhs.GetAsyncEnumerator(env.Token); while (await iterA.MoveNextAsync()) { if (await iterB.MoveNextAsync()) { var cmp = OrdA.Compare(iterA.Current, iterB.Current); if (cmp != 0) return cmp; } else { return 1; } } if (await iterB.MoveNextAsync()) { return -1; } return 0; }) : IO.lift(env => { using var iterA = AsEnumerable(env.Token).GetEnumerator(); using var iterB = rhs.AsEnumerable(env.Token).GetEnumerator(); while (iterA.MoveNext()) { if (iterB.MoveNext()) { var cmp = OrdA.Compare(iterA.Current, iterB.Current); if (cmp != 0) return cmp; } else { return 1; } } if (iterB.MoveNext()) { return -1; } return 0; }); } /// /// Compare to another sequence /// [Pure] public int CompareTo(Iterable? rhs) where OrdA : Ord => CompareToIO(rhs).Run(); /// /// Format the collection as `[a, b, c, ...]` /// The ellipsis is used for collections over 50 items /// To get a formatted string with all the items, use `ToFullString` /// or `ToFullArrayString`. /// [Pure] public override string ToString() => ToStringIO().Run(); /// /// Format the collection as `[a, b, c, ...]` /// The ellipsis is used for collections over 50 items /// To get a formatted string with all the items, use `ToFullString` /// or `ToFullArrayString`. /// [Pure] public virtual IO ToStringIO() => // TODO: Replace with FoldIO AsEnumerableIO().Map(xs => CollectionFormat.ToShortArrayString(xs)); /// /// Format the collection as `a, b, c, ...` /// [Pure] public string ToFullString(string separator = ", ") => ToFullStringIO(separator).Run(); /// /// Format the collection as `a, b, c, ...` /// [Pure] public virtual IO ToFullStringIO(string separator = ", ") => // TODO: Replace with FoldIO AsEnumerableIO().Map(xs => CollectionFormat.ToFullString(xs, separator)); /// /// Format the collection as `[a, b, c, ...]` /// [Pure] public string ToFullArrayString(string separator = ", ") => ToFullArrayStringIO(separator).Run(); /// /// Format the collection as `[a, b, c, ...]` /// [Pure] public virtual IO ToFullArrayStringIO(string separator = ", ") => // TODO: Replace with FoldIO AsEnumerableIO().Map(xs => CollectionFormat.ToFullArrayString(AsEnumerable(), separator)); /// /// Tail of the iterable /// public Iterable Tail => Skip(1); /// /// Skip count items /// [Pure] public Iterable Skip(int amount) => amount < 1 ? this : IsAsync ? new IterableEnumerable(AsEnumerableIO().Map(xs => xs.Skip(amount))) : new IterableAsyncEnumerable(AsAsyncEnumerableIO().Map(xs => xs.Skip(amount))); /// /// Take count items /// [Pure] public Iterable Take(int amount) => amount < 1 ? Empty : IsAsync ? new IterableEnumerable(AsEnumerableIO().Map(xs => xs.Take(amount))) : new IterableAsyncEnumerable(AsAsyncEnumerableIO().Map(xs => xs.Take(amount))); /// /// Iterate the sequence, yielding items if they match the predicate /// provided, and stopping as soon as one doesn't /// /// A new sequence with the first items that match the /// predicate [Pure] public Iterable TakeWhile(Func pred) { return IsAsync ? new IterableAsyncEnumerable(IO.lift(async)) : new IterableEnumerable(IO.lift(sync)); IEnumerable sync(EnvIO env) { foreach (var x in AsEnumerable(env.Token)) { if (!pred(x)) break; yield return x; } } async IAsyncEnumerable async(EnvIO env) { await foreach (var x in AsAsyncEnumerable(env.Token)) { if (!pred(x)) break; yield return x; } } } /// /// Iterate the sequence, yielding items if they match the predicate /// provided; and stopping as soon as one doesn't. An index value is /// also provided to the predicate function. /// /// A new sequence with the first items that match the /// predicate [Pure] public Iterable TakeWhile(Func pred) { return IsAsync ? new IterableAsyncEnumerable(IO.lift(async)) : new IterableEnumerable(IO.lift(sync)); IEnumerable sync(EnvIO env) { var ix = 0; foreach (var x in AsEnumerable(env.Token)) { if (!pred(x, ix++)) break; yield return x; } } async IAsyncEnumerable async(EnvIO env) { var ix = 0; await foreach (var x in AsAsyncEnumerable(env.Token)) { if (!pred(x, ix++)) break; yield return x; } } } /// /// Partition a list into two based on a predicate /// /// True if the item goes in the first list, false for the second list /// Pair of lists [Pure] public IO<(Iterable First, Iterable Second)> PartitionIO(Func predicate) { return IsAsync ? IO.liftVAsync(async) : IO.lift(sync); async ValueTask<(Iterable First, Iterable Second)> async(EnvIO env) { Iterable f = new IterableStrict(SeqStrict.Empty); Iterable s = new IterableStrict(SeqStrict.Empty); await foreach (var item in AsAsyncEnumerable(env.Token)) { if (predicate(item)) { f = f.Add(item); } else { s = s.Add(item); } } return (f, s); } (Iterable First, Iterable Second) sync(EnvIO env) { Iterable f = new IterableStrict(SeqStrict.Empty); Iterable s = new IterableStrict(SeqStrict.Empty); foreach (var item in AsEnumerable(env.Token)) { if (predicate(item)) { f = f.Add(item); } else { s = s.Add(item); } } return (f, s); } } /// /// Partition a list into two based on a predicate /// /// True if the item goes in the first list, false for the second list /// Pair of lists [Pure] public (Iterable First, Iterable Second) Partition(Func predicate) => PartitionIO(predicate).Run(); /// /// Cast items to another type /// /// /// Any item in the sequence that can't be cast to a `B` will be dropped from the result /// [Pure] public Iterable Cast() => new IterableCast(this); /// /// Zip two iterables into pairs /// [Pure] public Iterable<(A First, B Second)> Zip(Iterable rhs) => new IterableZip(this, rhs); /// /// Zip two iterables into pairs /// [Pure] public Iterable Zip(Iterable rhs, Func zipper) => Zip(rhs).Map(pair => zipper(pair.First, pair.Second)); /// /// Empty sequence /// [Pure] public static Iterable Empty => IterableNil.Default; /// /// Append operator /// [Pure] public static Iterable operator +(Iterable x, Iterable y) => x.Concat(y); /// /// Append operator /// [Pure] public static Iterable operator +(A x, Iterable y) => x.Cons(y); /// /// Append operator /// [Pure] public static Iterable operator +(Iterable x, A y) => x.Add(y); /// /// If this sequence is empty, return the other sequence, otherwise return this sequence. /// /// Right hand side of the operator /// A choice between two sequences based [Pure] public abstract Iterable Choose(Iterable rhs); /// /// If this sequence is empty, return the other sequence, otherwise return this sequence. /// /// Right hand side of the operator /// A choice between two sequences based [Pure] public abstract Iterable Choose(Memo rhs); /// /// Choice operator /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Iterable operator |(Iterable x, K y) => x.Choose(+y); /// /// Choice operator /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Iterable operator |(K x, Iterable y) => x.As().Choose(y); /// /// Choice operator /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Iterable operator |(Iterable x, Memo y) => x.Choose(y); /// /// Ordering operator /// [Pure] public static bool operator >(Iterable x, Iterable y) => x.CompareTo(y) > 0; /// /// Ordering operator /// [Pure] public static bool operator >=(Iterable x, Iterable y) => x.CompareTo(y) >= 0; /// /// Ordering operator /// [Pure] public static bool operator <(Iterable x, Iterable y) => x.CompareTo(y) < 0; /// /// Ordering operator /// [Pure] public static bool operator <=(Iterable x, Iterable y) => x.CompareTo(y) <= 0; /// /// Implicit conversion from an untyped empty list /// [Pure] public static implicit operator Iterable(SeqEmpty _) => Empty; /// /// Map the sequence using the function provided /// /// /// Mapping function /// Mapped sequence [Pure] public Iterable Select(Func f) => Map(f); /// /// Map the sequence using the function provided /// /// /// Mapping function /// Mapped sequence [Pure] public Iterable Select(Func f) => Map(f); /// /// Filter the items in the sequence /// /// Predicate to apply to the items /// Filtered sequence [Pure] public Iterable Where(Func f) => Filter(f); /// /// Monadic bind (flatmap) of the sequence /// /// Bound return value type /// Bind function /// Flat-mapped sequence [Pure] public Iterable SelectMany(Func> bind) => Bind(bind); /// /// Monadic bind (flatmap) of the sequence /// /// Bound return value type /// Bind function /// Flat-mapped sequence [Pure] public Iterable SelectMany(Func> bind, Func project) => Bind(x => bind(x).Map(y => project(x, y))); /// /// Monadic bind (flatmap) of the sequence /// /// Bound return value type /// Bind function /// Flat-mapped sequence [Pure] public Iterable SelectMany(Func> bind) => Bind(x => bind(x).AsIterable()); /// /// Monadic bind (flatmap) of the sequence /// /// Bound return value type /// Bind function /// Flat-mapped sequence [Pure] public Iterable SelectMany(Func> bind, Func project) => Bind(x => bind(x).AsIterable().Map(y => project(x, y))); [Pure] public IEnumerator GetEnumerator() => // ReSharper disable once NotDisposedResourceIsReturned AsEnumerable().GetEnumerator(); /// /// Get the hash code for all the items in the sequence, or 0 if empty /// /// [Pure] public override int GetHashCode() => hashCode is null ? (hashCode = hash(AsEnumerable())).Value : hashCode.Value; /// /// Get the additive-identity, i.e. the monoid-zero. Which is the empty sequence/ /// public static Iterable AdditiveIdentity => Empty; IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } ================================================ FILE: LanguageExt.Core/Immutable Collections/Iterable/Operators/Iterable.Operators.cs ================================================ using LanguageExt.Traits; namespace LanguageExt; public static partial class IterableExtensions { extension(K _) { /// /// Downcast operator /// public static Iterable operator +(K ma) => (Iterable)ma; public static Iterable operator >> (K ma, Lower lower) => (Iterable)ma; } } ================================================ FILE: LanguageExt.Core/Immutable Collections/Iterable/Prelude/Iterable.Prelude.mapapply.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public static partial class Prelude { /// /// Functor map operation /// /// /// Unwraps the value within the functor, passes it to the map function `f` provided, and /// then takes the mapped value and wraps it back up into a new functor. /// /// Functor to map /// Mapping function /// Mapped functor public static Iterable map(Func f, K ma) => Functor.map(f, ma).As(); /// /// Applicative action: runs the first applicative, ignores the result, and returns the second applicative /// public static Iterable action(K ma, K mb) => Applicative.action(ma, mb).As(); /// /// Applicative functor apply operation /// /// /// Unwraps the value within the `ma` applicative-functor, passes it to the unwrapped function(s) within `mf`, and /// then takes the resulting value and wraps it back up into a new applicative-functor. /// /// Value(s) applicative functor /// Mapping function(s) /// Mapped applicative functor public static Iterable apply(K> mf, K ma) => Applicative.apply(mf, ma).As(); } ================================================ FILE: LanguageExt.Core/Immutable Collections/Iterable/Trait/Iterable.TraitImpl.cs ================================================ using System; using System.Collections.Generic; using LanguageExt.Common; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; public partial class Iterable : Monad, MonoidK, Alternative, Traversable, NaturalEpi, NaturalMono, NaturalMono, NaturalMono, NaturalMono, NaturalMono { static K Monad.Recur(A value, Func>> f) => Monad.iterableRecur(value, f); static K Monad.Bind(K ma, Func> f) => ma.As().Bind(f); static K Functor.Map(Func f, K ma) => ma.As().Map(f); static K Applicative.Pure(A value) => singleton(value); static K Applicative.Apply(K> mf, K ma) => mf >> ma.Map; static K Applicative.Apply(K> mf, Memo ma) => mf >> ma.Map; static K MonoidK.Empty() => Iterable.Empty; static K Alternative.Empty() => Iterable.Empty; static K SemigroupK.Combine(K ma, K mb) => ma.As().Concat(+mb); static K Choice.Choose(K ma, K mb) => ma.As().Choose(+mb); static K Choice.Choose(K ma, Memo mb) => ma.As().Choose(mb); static K> Traversable.Traverse(Func> f, K ta) => ta.As().Traverse(f).Map(mb => mb.Kind()); static K> Traversable.TraverseM(Func> f, K ta) => ta.As().TraverseM(f).Map(mb => mb.Kind()); static S Foldable.FoldWhile( Func> f, Func<(S State, A Value), bool> predicate, S state, K ta) => ta.As().FoldWhile(f, predicate, state); static S Foldable.FoldBackWhile( Func> f, Func<(S State, A Value), bool> predicate, S state, K ta) => ta.As().Reverse().FoldWhile((a, s) => f(a)(s), predicate, state); static Arr Foldable.ToArr(K ta) => new(ta.As()); static Lst Foldable.ToLst(K ta) => new(ta.As()); static Iterable Foldable.ToIterable(K ta) => ta.As(); static Seq Foldable.ToSeq(K ta) => new(ta.As()); static K Natural.Transform(K fa) => toSeq(fa.As()); static K Natural.Transform(K fa) => toArray(fa.As()); static K Natural.Transform(K fa) => toList(fa.As()); static K Natural.Transform(K fa) => toSet(fa.As()); static K Natural.Transform(K fa) => toHashSet(fa.As()); static K Natural.Transform(K fa) => fa; static K CoNatural.CoTransform(K fa) => fa; static Fold Foldable.FoldStep(K ta, S initialState) { // ReSharper disable once GenericEnumeratorNotDisposed var iter = ta.As().GetEnumerator(); return go(initialState); Fold go(S state) => iter.MoveNext() ? Fold.Loop(state, iter.Current, go) : Fold.Done(state); } static Fold Foldable.FoldStepBack(K ta, S initialState) { // ReSharper disable once GenericEnumeratorNotDisposed var iter = ta.As().Reverse().GetEnumerator(); return go(initialState); Fold go(S state) => iter.MoveNext() ? Fold.Loop(state, iter.Current, go) : Fold.Done(state); } } ================================================ FILE: LanguageExt.Core/Immutable Collections/IterableNE/Extensions/IterableNE.Extensions.MapApply.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public static partial class IterableNEExtensions { /// Mapping function extension(Func f) { /// /// Functor map operation /// /// /// Unwraps the value within the functor, passes it to the map function `f` provided, and /// then takes the mapped value and wraps it back up into a new functor. /// /// Functor to map /// Mapped functor public IterableNE Map(K ma) => Functor.map(f, ma).As(); /// /// Functor map operation /// /// /// Unwraps the value within the functor, passes it to the map function `f` provided, and /// then takes the mapped value and wraps it back up into a new functor. /// /// Functor to map /// Mapped functor public IterableNE Map(IterableNE ma) => Functor.map(f, ma).As(); } extension(K ma) { /// /// Applicative action: runs the first applicative, ignores the result, and returns the second applicative /// public IterableNE Action(K mb) => Applicative.action(ma, mb).As(); } extension(IterableNE ma) { /// /// Applicative action: runs the first applicative, ignores the result, and returns the second applicative /// public IterableNE Action(K mb) => Applicative.action(ma, mb).As(); } /// Mapping function(s) extension(IterableNE> mf) { /// /// Applicative functor apply operation /// /// /// Unwraps the value within the `ma` applicative-functor, passes it to the unwrapped function(s) within `mf`, and /// then takes the resulting value and wraps it back up into a new applicative-functor. /// /// Value(s) applicative functor /// Mapped applicative functor public IterableNE Apply(K ma) => Applicative.apply(mf, ma).As(); } /// Mapping function(s) extension(K> mf) { /// /// Applicative functor apply operation /// /// /// Unwraps the value within the `ma` applicative-functor, passes it to the unwrapped function(s) within `mf`, and /// then takes the resulting value and wraps it back up into a new applicative-functor. /// /// Value(s) applicative functor /// Mapped applicative functor public IterableNE Apply(K ma) => Applicative.apply(mf, ma).As(); } } ================================================ FILE: LanguageExt.Core/Immutable Collections/IterableNE/Extensions/IterableNE.Extensions.cs ================================================ #pragma warning disable CS0693 // Type parameter has the same name as the type parameter from outer type using System; using LanguageExt.Traits; using System.Collections.Generic; using System.Diagnostics.Contracts; using static LanguageExt.Prelude; namespace LanguageExt; public static partial class IterableNEExtensions { [Pure] public static IterableNE As(this K xs) => (IterableNE)xs; [Pure] public static Option> AsIterableNE(this IEnumerable xs) => IterableNE.createRange(xs); [Pure] public static IO> AsIterableNE(this IAsyncEnumerable xs) => IterableNE.createRange(xs); [Pure] public static Option> AsIterableNE(this Seq xs) => xs.Head.Map(h => new IterableNE(h, xs.Tail.AsIterable())); [Pure] public static Option> AsIterableNE(this Arr xs) => xs.IsEmpty ? None : new IterableNE(xs[0], xs.Splice(1).AsIterable()); [Pure] public static Option> AsIterableNE(this Lst xs) => xs.IsEmpty ? None : new IterableNE(xs[0], xs.Skip(1)); /// /// Monadic join /// [Pure] public static Option> Flatten(this IterableNE> ma) => ma.Bind(identity); /// sequence /// sequence item type extension(IterableNE list) { /// /// Applies the given function 'selector' to each element of the sequence. Returns the sequence /// comprised of the results for each element where the function returns Some(f(x)). /// /// Selector function /// Mapped and filtered sequence [Pure] public Iterable Choose(Func> selector) => IterableNE.choose(list, selector); } } ================================================ FILE: LanguageExt.Core/Immutable Collections/IterableNE/IterableNE.Module.cs ================================================ using System; using System.Linq; using System.Collections.Generic; using static LanguageExt.Prelude; using System.Diagnostics.Contracts; using System.Runtime.CompilerServices; using System.Threading.Tasks; namespace LanguageExt; /// /// Cons sequence module /// Represents a sequence of values in a similar way to IEnumerable, but without the /// issues of multiple evaluation for key LINQ operators like Skip, Count, etc. /// /// Type of the values in the sequence public partial class IterableNE { /// /// Monadic join /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Option> flatten(IterableNE> ma) => ma.Bind(identity); /// /// Create an empty sequence /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static IterableNE singleton(A value) => IterableNE.FromSpan([value]); /// /// Create a sequence from an initial set of items /// /// Items /// sequence [Pure] public static IterableNE create(A head, params ReadOnlySpan tail) => new (head, Iterable.FromSpan(tail)); /// /// Create a sequence from an initial set of items /// /// Items /// sequence [Pure] public static IterableNE create(A head, IEnumerable tail) => new (head, Iterable.createRange(tail)); /// /// Create a sequence from an initial set of items /// /// Items /// sequence [Pure] public static IterableNE create(A head, IAsyncEnumerable tail) => new (head, Iterable.createRange(tail)); /// /// Create a sequence from an initial set of items /// /// Items /// sequence [Pure] public static IterableNE create(A head, Iterable tail) => new (head, tail); /// /// Create a sequence from an initial set of items /// /// Items /// sequence [Pure] public static IterableNE create(A head, IterableNE tail) => new (head, tail.AsIterable()); /// /// Create a sequence from an initial set of items /// /// Items /// sequence [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Option> createRange(ReadOnlySpan items) => items.Length == 0 ? None : IterableNE.FromSpan(items); /// /// Create a sequence from an initial set of items /// /// Items /// sequence [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Option> createRange(IEnumerable items) { var iter = Iterator.from(items); if (iter.IsEmpty) return None; var head = iter.Head; var tail = iter.Tail.AsIterable(); return new IterableNE(head, tail); } /// /// Create a sequence from an initial set of items /// /// Items /// sequence [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static IO> createRange(IAsyncEnumerable items) => IO.liftVAsync(async _ => { var iter = IteratorAsync.from(items); if (await iter.IsEmpty) throw new ArgumentException("Can't create an IterableNE from an empty sequence"); var head = await iter.Head; var tail = (await iter.Tail).AsIterable(); return new IterableNE(head, tail); }); /// /// Create a sequence from an initial set of items /// /// Items /// sequence [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static IO> createRange(Iterable items) { return IO.liftVAsync(_ => items.IsAsync ? async() : sync()); async ValueTask> async() { var iter = IteratorAsync.from(items); if (await iter.IsEmpty) throw new ArgumentException("Can't create an IterableNE from an empty sequence"); var head = await iter.Head; var tail = (await iter.Tail).AsIterable(); return new IterableNE(head, tail); } ValueTask> sync() { var iter = Iterator.from(items); if (iter.IsEmpty) throw new ArgumentException("Can't create an IterableNE from an empty sequence"); var head = iter.Head; var tail = iter.Tail.AsIterable(); return ValueTask.FromResult(new IterableNE(head, tail)); } } /// /// Generates a sequence of A using the provided delegate to initialise /// each item. /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Option> generate(int count, Func generator) => count < 1 ? None : createRange(Range(0, count).Select(generator)); /// /// Generates a sequence that contains one repeated value. /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Option> repeat(A item, int count) => count < 1 ? None : createRange(Range(0, count).Select(_ => item)); /// /// Get the item at the head (first) of the sequence /// /// sequence /// Head item [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static A head(IterableNE list) => list.Head; /// /// Applies the given function 'selector' to each element of the sequence. Returns the sequence /// of results for each element where the function returns Some(f(x)). /// /// sequence item type /// sequence /// Selector function /// Mapped and filtered sequence [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Iterable choose(IterableNE list, Func> selector) => list.Map(selector) .Filter(t => t.IsSome) .Map(t => t.Value!); /// /// Joins two sequences together either into a single sequence using the join /// function provided /// /// First sequence to join /// Second sequence to join /// Join function /// Joined sequence [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static IterableNE zip(IterableNE list, IterableNE other, Func zipper) => list.Zip(other, zipper); /// /// Joins two sequences together either into an sequence of tuples /// /// First sequence to join /// Second sequence to join /// Join function /// Joined sequence of tuples [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static IterableNE<(A First, B Second)> zip(IterableNE list, IterableNE other) => list.Zip(other, (t, u) => (t, u)); /// /// Returns a new sequence with the first 'count' items from the sequence provided /// /// sequence item type /// sequence /// Number of items to take /// A new sequence with the first 'count' items from the sequence provided [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Iterable take(IterableNE list, int count) => list.Take(count); /// /// Iterate the sequence, yielding items if they match the predicate provided, and stopping /// as soon as one doesn't /// /// sequence item type /// sequence /// Number of items to take /// A new sequence with the first items that match the predicate [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Iterable takeWhile(IterableNE list, Func pred) => list.TakeWhile(pred); /// /// Iterate the sequence, yielding items if they match the predicate provided, and stopping /// as soon as one doesn't. An index value is also provided to the predicate function. /// /// sequence item type /// sequence /// Number of items to take /// A new sequence with the first items that match the predicate [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Iterable takeWhile(IterableNE list, Func pred) => list.TakeWhile(pred); } ================================================ FILE: LanguageExt.Core/Immutable Collections/IterableNE/IterableNE.cs ================================================ using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics.Contracts; using System.Linq; using System.Numerics; using System.Threading; using LanguageExt.ClassInstances; using LanguageExt.Traits; using static LanguageExt.Prelude; // ReSharper disable NonReadonlyMemberInGetHashCode namespace LanguageExt; /// /// Non-empty lazy-sequence /// /// /// This always has a Head value and a Tail of length 0 to `n`. /// /// Type of the values in the sequence public record IterableNE(A Head, Iterable Tail) : IEnumerable, Semigroup>, IComparable>, IComparisonOperators, IterableNE, bool>, IAdditionOperators, IterableNE, IterableNE>, K { int? hashCode; public static IterableNE FromSpan(ReadOnlySpan ma) { if (ma.IsEmpty) throw new ArgumentException("Cannot create an IterableNE from an empty span"); return new IterableNE(ma[0], Iterable.FromSpan(ma.Slice(1))); } [Pure] internal bool IsAsync => Tail.IsAsync; /// /// Number of items in the sequence. /// /// /// NOTE: This will force evaluation of the sequence /// [Pure] public int Count() => CountIO().Run(); /// /// Number of items in the sequence. /// /// /// NOTE: This will force evaluation of the sequence /// [Pure] public IO CountIO() => Tail.CountIO().Map(c => c + 1); /// /// Stream as an enumerable /// [Pure] public IO> AsEnumerableIO() => Tail.AsEnumerableIO().Map(xs => xs.Prepend(Head)); /// /// Stream as an enumerable /// [Pure] public IEnumerable AsEnumerable(CancellationToken token = default) { using var env = EnvIO.New(token: token); return AsEnumerableIO().Run(env); } /// /// Stream as an enumerable /// [Pure] public IO> AsAsyncEnumerableIO() => Tail.AsAsyncEnumerableIO().Map(xs => xs.Prepend(Head)); /// /// Stream as an enumerable /// [Pure] public IAsyncEnumerable AsAsyncEnumerable(CancellationToken token = default) { using var env = EnvIO.New(token: token); return AsAsyncEnumerableIO().Run(env); } /// /// Stream as an enumerable /// [Pure] public Iterable AsIterable() => Head.Cons(Tail); /// /// Add an item to the end of the sequence /// /// /// This does not force evaluation of the whole lazy sequence, nor does it cause /// exponential iteration issues when repeated adds occur. /// [Pure] public IterableNE Add(A item) => new(Head, Tail.Add(item)); /// /// Add an item to the beginning of the sequence /// /// /// This does not force evaluation of the whole lazy sequence, nor does it cause /// exponential iteration issues when repeated cons occur. /// [Pure] public IterableNE Cons(A item) => new(item, Head.Cons(Tail)); /// /// Impure iteration of the bound values in the structure /// /// /// Returns the original unmodified structure /// public IO IterIO(Action f) => IO.lift(() => f(Head)) >> Tail.IterIO(f); /// /// Impure iteration of the bound values in the structure /// /// /// Returns the original unmodified structure /// public Unit Iter(Action f) => IterIO(f).Run(); /// /// Impure iteration of the bound values in the structure /// /// /// Returns the original unmodified structure /// public IO IterIO(Action f) => IO.lift(() => f(Head, 0)) >> Tail.IterIO(f, 1); /// /// Impure iteration of the bound values in the structure /// /// /// Returns the original unmodified structure /// public Unit Iter(Action f) => IterIO(f).Run(); /// /// Map the sequence using the function provided /// /// /// Mapping function /// Mapped sequence [Pure] public IterableNE Map(Func f) => new(f(Head), Tail.Map(f)); /// /// Map the sequence using the function provided /// /// /// Mapping function /// Mapped sequence [Pure] public IterableNE Map(Func f) => new(f(Head, 0), Tail.Map(f, 1)); /// /// Monadic bind (flatmap) of the sequence /// /// Bound return value type /// Bind function /// Flat-mapped sequence [Pure] public IterableNE Bind(Func> f) { var head = f(Head); var tail = Tail.Bind(a => f(a).AsIterable()); return head + tail; } /// /// Filter the items in the sequence /// /// Predicate to apply to the items /// Filtered sequence [Pure] public Iterable Filter(Func f) => f(Head) ? Head.Cons(Tail.Filter(f)) : Tail.Filter(f); /// /// Fold over the sequence from the left, accumulating state in `f` /// /// Fold function to apply to each item in the sequence /// Continue while the predicate returns true for any pair of value and state. /// This is tested before the value is processed and the state is updated. So, use `FoldWhile*` for pre-assertions. /// /// Initial state value /// State value type /// Resulting state public S FoldWhile( Func> f, Func<(S State, A Value), bool> predicate, S initialState) => FoldWhileIO(f, predicate, initialState).Run(); /// /// Fold over the sequence from the left, accumulating state in `f` /// /// Fold function to apply to each item in the sequence /// Continue while the predicate returns true for any pair of value and state. /// This is tested before the value is processed and the state is updated. So, use `FoldWhile*` for pre-assertions. /// /// Initial state value /// State value type /// Resulting state public S FoldWhile( Func f, Func<(S State, A Value), bool> predicate, S initialState) => FoldWhileIO(f, predicate, initialState).Run(); /// /// Fold over the sequence from the left, accumulating state in `f` /// /// Fold function to apply to each item in the sequence /// Continue while the predicate returns true for any pair of value and state. /// This is tested before the value is processed and the state is updated. So, use `FoldWhile*` for pre-assertions. /// /// Initial state value /// State value type /// Resulting state public IO FoldWhileIO( Func> f, Func<(S State, A Value), bool> predicate, S initialState) => IO.liftVAsync(async env => predicate((initialState, Head)) ? initialState : await Tail.FoldUntilIO(f, predicate, f(Head)(initialState)).RunAsync(env)); /// /// Fold over the sequence from the left, accumulating state in `f` /// /// Fold function to apply to each item in the sequence /// Continue while the predicate returns true for any pair of value and state. /// This is tested before the value is processed and the state is updated. So, use `FoldWhile*` for pre-assertions. /// /// Initial state value /// State value type /// Resulting state public IO FoldWhileIO( Func f, Func<(S State, A Value), bool> predicate, S initialState) => IO.liftVAsync(async env => predicate((initialState, Head)) ? initialState : await Tail.FoldUntilIO(f, predicate, f(initialState, Head)).RunAsync(env)); /// /// Fold over the sequence from the left, accumulating state in `f` /// /// Fold function to apply to each item in the sequence /// Continue while the predicate returns true for any pair of value and state. /// This is tested before the value is processed and the state is updated. So, use `FoldWhile*` for pre-assertions. /// /// Initial state value /// State value type /// Resulting state public S FoldUntil( Func> f, Func<(S State, A Value), bool> predicate, S initialState) => FoldUntilIO(f, predicate, initialState).Run(); /// /// Fold over the sequence from the left, accumulating state in `f` /// /// Fold function to apply to each item in the sequence /// Continue while the predicate returns true for any pair of value and state. /// This is tested before the value is processed and the state is updated. So, use `FoldWhile*` for pre-assertions. /// /// Initial state value /// State value type /// Resulting state public S FoldUntil( Func f, Func<(S State, A Value), bool> predicate, S initialState) => FoldUntilIO(f, predicate, initialState).Run(); /// /// Fold over the sequence from the left, accumulating state in `f` /// /// Fold function to apply to each item in the sequence /// Continue while the predicate returns true for any pair of value and state. /// This is tested before the value is processed and the state is updated. So, use `FoldWhile*` for pre-assertions. /// /// Initial state value /// State value type /// Resulting state public IO FoldUntilIO( Func> f, Func<(S State, A Value), bool> predicate, S initialState) => IO.liftVAsync(async env => { var s = f(Head)(initialState); if (predicate((s, Head))) return s; return await Tail.FoldUntilIO(f, predicate, s).RunAsync(env); }); /// /// Fold over the sequence from the left, accumulating state in `f` /// /// Fold function to apply to each item in the sequence /// Continue while the predicate returns true for any pair of value and state. /// This is tested before the value is processed and the state is updated. So, use `FoldWhile*` for pre-assertions. /// /// Initial state value /// State value type /// Resulting state public IO FoldUntilIO( Func f, Func<(S State, A Value), bool> predicate, S initialState) => IO.liftVAsync(async env => { var s = f(initialState, Head); if (predicate((s, Head))) return s; return await Tail.FoldUntilIO(f, predicate, s).RunAsync(env); }); /// /// Fold until the `Option` returns `None` /// /// Fold function /// Initial state for the fold /// Foldable structure /// State type /// Aggregated value public S FoldMaybe( Func>> f, S initialState) => FoldMaybeIO(f, initialState).Run(); /// /// Fold until the `Option` returns `None` /// /// Fold function /// Initial state for the fold /// Foldable structure /// State type /// Aggregated value public S FoldMaybe( Func> f, S initialState) => FoldMaybeIO(f, initialState).Run(); /// /// Fold until the `Option` returns `None` /// /// Fold function /// Initial state for the fold /// Foldable structure /// State type /// Aggregated value public IO FoldMaybeIO( Func>> f, S initialState) => FoldWhileIO<(bool IsSome, S Value)>( a => s => f(s.Value)(a) switch { { IsSome: true, Case: S value } => (true, value), _ => (false, s.Value) }, s => s.State.IsSome, (true, initialState)) .Map(s => s.Value); /// /// Fold until the `Option` returns `None` /// /// Fold function /// Initial state for the fold /// Foldable structure /// State type /// Aggregated value public IO FoldMaybeIO( Func> f, S initialState) => FoldWhileIO<(bool IsSome, S Value)>( (s, a) => f(s.Value, a) switch { { IsSome: true, Case: S value } => (true, value), _ => (false, s.Value) }, s => s.State.IsSome, (true, initialState)) .Map(s => s.Value); /// /// Same behaviour as `Fold` but the fold operation returns a monadic type and allows /// early exit of the operation once the predicate function becomes `false` for the /// state/value pair /// public K FoldWhileM( Func>> f, Func predicate, S initialState) where M : MonadIO { return FoldWhileIO(acc, s => predicate(s.Value), Monad.pure) .Bind(f1 => f1(initialState)); Func>, Func>> acc(A value) => bind => state => Monad.bind(f(value)(state), bind); } /// /// Same behaviour as `Fold` but the fold operation returns a monadic type and allows /// early exit of the operation once the predicate function becomes `false` for the /// state/value pair /// public K FoldWhileM( Func> f, Func predicate, S initialState) where M : MonadIO { return FoldWhileIO(acc, s => predicate(s.Value), Monad.pure) .Bind(f1 => f1(initialState)); Func>, Func>> acc(A value) => bind => state => Monad.bind(f(state, value), bind); } /// /// Same behaviour as `Fold` but the fold operation returns a monadic type and allows /// early exit of the operation once the predicate function becomes `false` for the /// state/value pair /// public K FoldUntilM( Func>> f, Func predicate, S initialState) where M : MonadIO { return FoldUntilIO(acc, s => predicate(s.Value), Monad.pure).Bind(f1 => f1(initialState)); Func>, Func>> acc(A value) => bind => state => Monad.bind(f(value)(state), bind); } /// /// Same behaviour as `Fold` but the fold operation returns a monadic type and allows /// early exit of the operation once the predicate function becomes `false` for the /// state/value pair /// public K FoldUntilM( Func> f, Func predicate, S initialState) where M : MonadIO { return FoldUntilIO(acc, s => predicate(s.Value), Monad.pure).Bind(f1 => f1(initialState)); Func>, Func>> acc(A value) => bind => state => Monad.bind(f(state, value), bind); } /// /// Right-associative fold of a structure, lazy in the accumulator. /// /// In the case of lists, 'Fold', when applied to a binary operator, a /// starting value (typically the right-identity of the operator), and a /// list, reduces the list using the binary operator, from right to left. /// public S Fold(Func> f, S initialState) => FoldIO(f, initialState).Run(); /// /// Right-associative fold of a structure, lazy in the accumulator. /// /// In the case of lists, 'Fold', when applied to a binary operator, a /// starting value (typically the right-identity of the operator), and a /// list, reduces the list using the binary operator, from right to left. /// public S Fold(Func f, S initialState) => FoldIO(f, initialState).Run(); /// /// Right-associative fold of a structure, lazy in the accumulator. /// /// In the case of lists, 'Fold', when applied to a binary operator, a /// starting value (typically the right-identity of the operator), and a /// list, reduces the list using the binary operator, from right to left. /// public IO FoldIO(Func> f, S initialState) => FoldWhileIO(f, _ => true, initialState); /// /// Right-associative fold of a structure, lazy in the accumulator. /// /// In the case of lists, 'Fold', when applied to a binary operator, a /// starting value (typically the right-identity of the operator), and a /// list, reduces the list using the binary operator, from right to left. /// public IO FoldIO(Func f, S initialState) => FoldWhileIO(f, _ => true, initialState); /// /// Right-associative fold of a structure, lazy in the accumulator. /// /// In the case of lists, 'Fold', when applied to a binary operator, a /// starting value (typically the right-identity of the operator), and a /// list, reduces the list using the binary operator, from right to left. /// public K FoldM( Func>> f, S initialState) where M : MonadIO { return FoldIO(acc, Monad.pure).Bind(f => f(initialState)); Func>, Func>> acc(A value) => bind => state => Monad.bind(f(value)(state), bind); } /// /// Right-associative fold of a structure, lazy in the accumulator. /// /// In the case of lists, 'Fold', when applied to a binary operator, a /// starting value (typically the right-identity of the operator), and a /// list, reduces the list using the binary operator, from right to left. /// public K FoldM( Func> f, S initialState) where M : MonadIO { return FoldIO(acc, Monad.pure).Bind(f => f(initialState)); Func>, Func>> acc(A value) => bind => state => Monad.bind(f(state, value), bind); } /// /// Map each element of the structure into a monoid, and combine the /// results with `Append`. This fold is right-associative and lazy in the /// accumulator. For strict left-associative folds consider `FoldMapBack` /// instead. /// public B FoldMap(Func f) where B : Monoid => FoldMapIO(f).Run(); /// /// Map each element of the structure into a monoid, and combine the /// results with `Append`. This fold is right-associative and lazy in the /// accumulator. For strict left-associative folds consider `FoldMapBack` /// instead. /// public IO FoldMapIO(Func f) where B : Monoid => FoldIO(x => a => f(x).Combine(a), B.Empty); /// /// Map each element of the structure into a monoid, and combine the /// results with `Append`. This fold is right-associative and lazy in the /// accumulator. For strict left-associative folds consider `FoldMapBack` /// instead. /// public B FoldMapWhile(Func f, Func<(B State, A Value), bool> predicate) where B : Monoid => FoldMapWhileIO(f, predicate).Run(); /// /// Map each element of the structure into a monoid, and combine the /// results with `Append`. This fold is right-associative and lazy in the /// accumulator. For strict left-associative folds consider `FoldMapBack` /// instead. /// public IO FoldMapWhileIO(Func f, Func<(B State, A Value), bool> predicate) where B : Monoid => FoldWhileIO(x => a => f(x).Combine(a), predicate, B.Empty); /// /// Map each element of the structure into a monoid, and combine the /// results with `Append`. This fold is right-associative and lazy in the /// accumulator. For strict left-associative folds consider `FoldMapBack` /// instead. /// public B FoldMapUntil(Func f, Func<(B State, A Value), bool> predicate) where B : Monoid => FoldMapUntilIO(f, predicate).Run(); /// /// Map each element of the structure into a monoid, and combine the /// results with `Append`. This fold is right-associative and lazy in the /// accumulator. For strict left-associative folds consider `FoldMapBack` /// instead. /// public IO FoldMapUntilIO(Func f, Func<(B State, A Value), bool> predicate) where B : Monoid => FoldUntilIO(x => a => f(x).Combine(a), predicate, B.Empty); /// /// Semigroup combine two iterables (concatenate) /// [Pure] public IterableNE Combine(IterableNE y) => new(Head, Tail + y.Head.Cons(y.Tail)); /// /// Add a range of items to the end of the sequence /// [Pure] public IterableNE Concat(IEnumerable items) => new(Head, Tail.Concat(items)); /// /// Add a range of items to the end of the sequence /// [Pure] public IterableNE Concat(Iterable items) => new(Head, Tail + items); /// /// Add a range of items to the end of the sequence /// [Pure] public IterableNE Concat(IterableNE items) => new(Head, Tail + items.Head.Cons(items.Tail)); /// /// Monadic bind (flatmap) of the sequence /// /// Bound return value type /// Bind function /// Flat-mapped sequence [Pure] public IterableNE Bind(Func> f) => Bind(a => f(a).As()); /// /// Returns true if the sequence has items in it /// /// True if the sequence has items in it [Pure] public bool Any() => true; [Pure] public int CompareTo(object? obj) => obj is IterableNE rhs ? CompareTo(rhs) : 1; [Pure] public int CompareTo(IterableNE? other) => CompareTo>(other); [Pure] public IO CompareToIO(IterableNE? other) => CompareToIO>(other); /// /// Compare to another sequence /// [Pure] public int CompareTo(IterableNE? rhs) where OrdA : Ord => CompareToIO(rhs).Run(); /// /// Compare to another sequence /// [Pure] public IO CompareToIO(IterableNE? rhs) where OrdA : Ord => IO.liftVAsync(async e => { if (rhs is null) return 1; var cmp = OrdA.Compare(Head, rhs.Head); if (cmp != 0) return cmp; return await Tail.CompareToIO(rhs.Tail).RunAsync(e); }); /// /// Format the collection as `[a, b, c, ...]` /// The ellipsis is used for collections over 50 items /// To get a formatted string with all the items, use `ToFullString` /// or `ToFullArrayString`. /// [Pure] public override string ToString() => CollectionFormat.ToShortArrayString(AsEnumerable()); /// /// Format the collection as `[a, b, c, ...]` /// The ellipsis is used for collections over 50 items /// To get a formatted string with all the items, use `ToFullString` /// or `ToFullArrayString`. /// [Pure] public IO ToStringIO() => AsEnumerableIO().Map(xs => CollectionFormat.ToShortArrayString(xs)); /// /// Format the collection as `a, b, c, ...` /// [Pure] public string ToFullString(string separator = ", ") => CollectionFormat.ToFullString(AsEnumerable(), separator); /// /// Format the collection as `a, b, c, ...` /// [Pure] public IO ToFullStringIO(string separator = ", ") => AsEnumerableIO().Map(xs => CollectionFormat.ToFullString(xs, separator)); /// /// Format the collection as `[a, b, c, ...]` /// [Pure] public string ToFullArrayString(string separator = ", ") => CollectionFormat.ToFullArrayString(AsEnumerable(), separator); /// /// Format the collection as `[a, b, c, ...]` /// [Pure] public IO ToFullArrayStringIO(string separator = ", ") => AsEnumerableIO().Map(xs => CollectionFormat.ToFullArrayString(xs, separator)); /// /// Skip count items /// [Pure] public Iterable Skip(int amount) => amount switch { 0 => AsIterable(), 1 => Tail, _ => Tail.Skip(amount - 1) }; /// /// Take count items /// [Pure] public Iterable Take(int amount) => amount switch { 0 => [], 1 => [Head], _ => Tail.Take(amount - 1) }; /// /// Iterate the sequence, yielding items if they match the predicate /// provided, and stopping as soon as one doesn't /// /// A new sequence with the first items that match the /// predicate [Pure] public Iterable TakeWhile(Func pred) => AsIterable().TakeWhile(pred); /// /// Iterate the sequence, yielding items if they match the predicate /// provided, and stopping as soon as one doesn't. An index value is /// also provided to the predicate function. /// /// A new sequence with the first items that match the /// predicate [Pure] public Iterable TakeWhile(Func pred) => AsIterable().TakeWhile(pred); [Pure] /// /// Partition a list into two based on a predicate /// /// True if the item goes in the first list, false for the second list /// Pair of lists public (Iterable First, Iterable Second) Partition(Func predicate) { var (f, s) = Tail.Partition(predicate); return predicate(Head) ? (Head.Cons(f), s) : (f, Head.Cons(s)); } /// /// Zip two iterables into pairs /// [Pure] public IterableNE<(A First, B Second)> Zip(IterableNE rhs) => new ((Head, rhs.Head), Tail.Zip(rhs.Tail)); /// /// Zip two iterables into pairs /// [Pure] public IterableNE Zip(IterableNE rhs, Func zipper) => new (zipper(Head, rhs.Head), Tail.Zip(rhs.Tail, zipper)); /// /// Append operator /// [Pure] public static IterableNE operator +(IterableNE x, IterableNE y) => x.Concat(y); /// /// Append operator /// [Pure] public static IterableNE operator +(IterableNE x, Iterable y) => x.Concat(y); /// /// Ordering operator /// [Pure] public static bool operator >(IterableNE x, IterableNE y) => x.CompareTo(y) > 0; /// /// Ordering operator /// [Pure] public static bool operator >=(IterableNE x, IterableNE y) => x.CompareTo(y) >= 0; /// /// Ordering operator /// [Pure] public static bool operator <(IterableNE x, IterableNE y) => x.CompareTo(y) < 0; /// /// Ordering operator /// [Pure] public static bool operator <=(IterableNE x, IterableNE y) => x.CompareTo(y) <= 0; /// /// Map the sequence using the function provided /// /// /// Mapping function /// Mapped sequence [Pure] public IterableNE Select(Func f) => Map(f); /// /// Map the sequence using the function provided /// /// /// Mapping function /// Mapped sequence [Pure] public IterableNE Select(Func f) => Map(f); /// /// Monadic bind (flatmap) of the sequence /// /// Bound return value type /// Bind function /// Flat-mapped sequence [Pure] public IterableNE SelectMany(Func> bind) => Bind(bind); /// /// Monadic bind (flatmap) of the sequence /// /// Bound return value type /// Bind function /// Flat-mapped sequence [Pure] public IterableNE SelectMany(Func> bind, Func project) => Bind(x => bind(x).Map(y => project(x, y))); [Pure] public IEnumerator GetEnumerator() => // ReSharper disable once NotDisposedResourceIsReturned AsEnumerable().GetEnumerator(); /// /// Get the hash code for all the items in the sequence, or 0 if empty /// /// [Pure] public override int GetHashCode() => hashCode.HasValue ? hashCode.Value : (hashCode = hash(AsEnumerable())).Value; IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } ================================================ FILE: LanguageExt.Core/Immutable Collections/IterableNE/Operators/IterableNE.Operators.cs ================================================ using LanguageExt.Traits; namespace LanguageExt; public static partial class IterableNEExtensions { extension(K _) { /// /// Downcast operator /// public static IterableNE operator +(K ma) => (IterableNE)ma; public static IterableNE operator >> (K ma, Lower lower) => (IterableNE)ma; } } ================================================ FILE: LanguageExt.Core/Immutable Collections/IterableNE/Trait/IterableNE.TraitImpl.cs ================================================ using System; using System.Linq; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; public partial class IterableNE : Monad, SemigroupK, Foldable, NaturalMono, NaturalMono, NaturalMono, NaturalMono, NaturalMono, NaturalMono { static K Monad.Recur(A value, Func>> f) => // TODO: We need a way of lifting an IO into IterableNE (knowing it's non-empty) // Create a sub-type DSL for IterableNE, like Iterable Monad.unsafeRecur(value, f); static K Monad.Bind(K ma, Func> f) => ma.As().Bind(f); static K Functor.Map(Func f, K ma) => ma.As().Map(f); static K Applicative.Pure(A value) => singleton(value); static K Applicative.Apply(K> mf, K ma) => mf >> ma.Map; static K Applicative.Apply(K> mf, Memo ma) => mf >> ma.Map; static K SemigroupK.Combine(K ma, K mb) => ma.As().Concat(mb.As()); static S Foldable.FoldWhile( Func> f, Func<(S State, A Value), bool> predicate, S state, K ta) => ta.As().FoldWhile(f, predicate, state); static S Foldable.FoldBackWhile( Func> f, Func<(S State, A Value), bool> predicate, S state, K ta) => throw new NotSupportedException("FoldBack* is currently not supported for IterableNE"); static Arr Foldable.ToArr(K ta) => new(ta.As()); static Lst Foldable.ToLst(K ta) => new(ta.As()); static Iterable Foldable.ToIterable(K ta) => ta.As().AsIterable(); static Seq Foldable.ToSeq(K ta) => new(ta.As()); static K Natural.Transform(K fa) => toSeq(fa.As()); static K Natural.Transform(K fa) => toArray(fa.As()); static K Natural.Transform(K fa) => toList(fa.As()); static K Natural.Transform(K fa) => toSet(fa.As()); static K Natural.Transform(K fa) => fa.As().AsIterable(); static K Natural.Transform(K fa) => toHashSet(fa.As()); static Fold Foldable.FoldStep(K ta, S initialState) { // ReSharper disable once GenericEnumeratorNotDisposed var iter = ta.As().GetEnumerator(); return go(initialState); Fold go(S state) => iter.MoveNext() ? Fold.Loop(state, iter.Current, go) : Fold.Done(state); } static Fold Foldable.FoldStepBack(K ta, S initialState) { // ReSharper disable once GenericEnumeratorNotDisposed var iter = ta.As().Reverse().GetEnumerator(); return go(initialState); Fold go(S state) => iter.MoveNext() ? Fold.Loop(state, iter.Current, go) : Fold.Done(state); } } ================================================ FILE: LanguageExt.Core/Immutable Collections/Iterator/DSL/Iterator.Cons.cs ================================================ using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics.Contracts; using System.Linq; using System.Threading; using LanguageExt.ClassInstances; using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt; public abstract partial class Iterator { /// /// Cons iterator case. /// /// Contains a head value and a tail that represents the rest of the sequence. /// public abstract class Cons : Iterator { public new void Deconstruct(out A head, out Iterator tail) { head = Head; tail = Tail; } } } ================================================ FILE: LanguageExt.Core/Immutable Collections/Iterator/DSL/Iterator.ConsFirst.cs ================================================ using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics.Contracts; using System.Linq; using System.Threading; using LanguageExt.ClassInstances; using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt; public abstract partial class Iterator { internal sealed class ConsFirst : Cons { IEnumerable enumerable; int firstAcquired; Iterator? firstValue; public override string ToString() => "Iterator"; internal ConsFirst(IEnumerable enumerable) => this.enumerable = enumerable; public override A Head => First.Head; public override Iterator Tail => First.Tail; public new void Deconstruct(out A head, out Iterator tail) { head = Head; tail = Tail; } Iterator First { get { if (firstAcquired == 2) return firstValue!; SpinWait sw = default; while (firstAcquired < 2) { if (Interlocked.CompareExchange(ref firstAcquired, 1, 0) == 0) { try { var enumerator = enumerable.GetEnumerator(); if (enumerator.MoveNext()) { firstValue = new ConsValueEnum(enumerator.Current, enumerator); } else { enumerator.Dispose(); firstValue = Nil.Default; } firstAcquired = 2; } catch (Exception) { firstAcquired = 0; throw; } } else { sw.SpinOnce(); } } return firstValue!; } } /// /// Return true if there are no elements in the sequence. /// public override bool IsEmpty => First.IsEmpty; /// /// Clone the iterator so that we can consume it without having the head item referenced. /// This will stop any GC pressure. /// public override Iterator Clone() => new ConsFirst(enumerable); /// /// When iterating a sequence, it is possible (before evaluation of the `Tail`) to Terminate the current /// iterator and to take a new iterator that continues on from the current location. The reasons for doing /// this are to break the linked-list chain so that there isn't a big linked-list of objects in memory that /// can't be garbage collected. /// /// New iterator that starts from the current iterator position public override Iterator Split() => Clone(); /// /// Return the number of items in the sequence. /// /// /// Requires all items to be evaluated, this will happen only once however. /// [Pure] public override long Count => First.Count; public override void Dispose() { if (Interlocked.CompareExchange(ref firstAcquired, 1, 2) == 2) { firstValue?.Dispose(); firstValue = null; firstAcquired = 0; } } } } ================================================ FILE: LanguageExt.Core/Immutable Collections/Iterator/DSL/Iterator.ConsValue.cs ================================================ using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics.Contracts; using System.Linq; using System.Threading; using LanguageExt.ClassInstances; using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt; public abstract partial class Iterator { internal sealed class ConsValue : Cons { A head; Iterator tail; public override string ToString() => "Iterator"; public ConsValue(A head, Iterator tail) { this.head = head; this.tail = tail; Count = -1; } public new void Deconstruct(out A head, out Iterator tail) { head = Head; tail = Tail; } /// /// Head element /// public override A Head => head; /// /// Tail of the sequence /// public override Iterator Tail => tail; /// /// Return true if there are no elements in the sequence. /// public override bool IsEmpty => false; /// /// Clone the iterator so that we can consume it without having the head item referenced. /// This will stop any GC pressure. /// public override Iterator Clone() => new ConsValue(Head, Tail.Clone()); /// /// When iterating a sequence, it is possible (before evaluation of the `Tail`) to Terminate the current /// iterator and to take a new iterator that continues on from the current location. The reasons for doing /// this are to break the linked-list chain so that there isn't a big linked-list of objects in memory that /// can't be garbage collected. /// /// New iterator that starts from the current iterator position public override Iterator Split() => this; /// /// Return the number of items in the sequence. /// /// /// Requires all items to be evaluated, this will happen only once however. /// [Pure] public override long Count { get { if (field == -1) { field = 1 + Tail.Count; } return field; } } public override void Dispose() => Tail.Dispose(); } } ================================================ FILE: LanguageExt.Core/Immutable Collections/Iterator/DSL/Iterator.ConsValueEnum.cs ================================================ using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics.Contracts; using System.Linq; using System.Threading; using LanguageExt.ClassInstances; using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt; public abstract partial class Iterator { internal sealed class ConsValueEnum : Cons { Exception? exception; IEnumerator? enumerator; int tailAcquired; Iterator? tailValue; long count = -1; public override string ToString() => "Iterator"; internal ConsValueEnum(A head, IEnumerator enumerator) { Head = head; this.enumerator = enumerator; } public new void Deconstruct(out A head, out Iterator tail) { head = Head; tail = Tail; } /// /// Head element /// public override A Head { get; } /// /// Tail of the sequence /// public override Iterator Tail { get { if(tailAcquired == 2) return tailValue!; if(tailAcquired == 3) exception!.Rethrow(); SpinWait sw = default; while (tailAcquired < 2) { if (Interlocked.CompareExchange(ref tailAcquired, 1, 0) == 0) { try { if (enumerator!.MoveNext()) { tailValue = new ConsValueEnum(enumerator.Current, enumerator); } else { enumerator?.Dispose(); enumerator = null; tailValue = Nil.Default; } tailAcquired = 2; } catch (Exception e) { exception = e; tailAcquired = 3; throw; } } else { sw.SpinOnce(); } } if(tailAcquired == 3) exception!.Rethrow(); return tailValue!; } } /// /// Return true if there are no elements in the sequence. /// public override bool IsEmpty => false; /// /// Clone the iterator so that we can consume it without having the head item referenced. /// This will stop any GC pressure. /// public override Iterator Clone() => this; /// /// When iterating a sequence, it is possible (before evaluation of the `Tail`) to Terminate the current /// iterator and to take a new iterator that continues on from the current location. The reasons for doing /// this are to break the linked-list chain so that there isn't a big linked-list of objects in memory that /// can't be garbage collected. /// /// New iterator that starts from the current iterator position public override Iterator Split() { if (Interlocked.CompareExchange(ref tailAcquired, 1, 0) == 0) { tailValue = Nil.Default; tailAcquired = 2; return new ConsValueEnum(Head, enumerator!); } else { return this; } } /// /// Return the number of items in the sequence. /// /// /// Requires all items to be evaluated, this will happen only once however. /// [Pure] public override long Count { get { if (count == -1) { count = 1 + Tail.Count; } return count; } } public override void Dispose() { enumerator?.Dispose(); enumerator = null; } } } ================================================ FILE: LanguageExt.Core/Immutable Collections/Iterator/DSL/Iterator.ConsValueLazy.cs ================================================ using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics.Contracts; using System.Linq; using System.Threading; using LanguageExt.ClassInstances; using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt; public abstract partial class Iterator { internal sealed class ConsValueLazy : Cons { long count; A head; Exception? error; Iterator? tail; Func>? tailF; int tailAcquired; public override string ToString() => "Iterator"; public ConsValueLazy(A head, Func> tailF) { this.head = head; this.tailF = tailF; tail = null; count = -1; } public new void Deconstruct(out A h, out Iterator t) { h = Head; t = Tail; } /// /// Head element /// public override A Head => head; /// /// Tail of the sequence /// public override Iterator Tail => TailLazy(); Iterator TailLazy() { if (tailAcquired == 2) return tail!; if (tailAcquired == 3) error!.Rethrow(); SpinWait sw = default; while (tailAcquired < 2) { if (Interlocked.CompareExchange(ref tailAcquired, 1, 0) == 0) { try { tail = tailF!(); tailF = null; tailAcquired = 2; } catch(Exception e) { error = e; tailF = null; tailAcquired = 3; throw; } } else { sw.SpinOnce(); } } return tail!; } /// /// Return true if there are no elements in the sequence. /// public override bool IsEmpty => false; /// /// Clone the iterator so that we can consume it without having the head item referenced. /// This will stop any GC pressure. /// public override Iterator Clone() => this; /// /// When iterating a sequence, it is possible (before evaluation of the `Tail`) to Terminate the current /// iterator and to take a new iterator that continues on from the current location. The reasons for doing /// this are to break the linked-list chain so that there isn't a big linked-list of objects in memory that /// can't be garbage collected. /// /// New iterator that starts from the current iterator position public override Iterator Split() { var h = head; var t = tailF; return tailAcquired == 0 ? new ConsValueLazy(h, t!) : this; } /// /// Return the number of items in the sequence. /// /// /// Requires all items to be evaluated, this will happen only once however. /// [Pure] public override long Count { get { if (count == -1) { count = 1 + Tail.Count; } return count; } } public override void Dispose() { if (tailAcquired == 2) { Tail.Dispose(); } } } } ================================================ FILE: LanguageExt.Core/Immutable Collections/Iterator/DSL/Iterator.Nil.cs ================================================ using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics.Contracts; using System.Linq; using System.Threading; using LanguageExt.ClassInstances; using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt; public abstract partial class Iterator : IEnumerable, IEquatable>, IDisposable, K { /// /// Nil iterator case /// /// The end of the sequence. /// public sealed class Nil : Iterator { public static readonly Iterator Default = new Nil(); public override string ToString() => "Nil"; /// /// Head element /// public override A Head => throw new InvalidOperationException("Nil iterator has no head"); /// /// Tail of the sequence /// public override Iterator Tail => this; /// /// Return true if there are no elements in the sequence. /// public override bool IsEmpty => true; /// /// Clone the iterator so that we can consume it without having the head item referenced. /// This will stop any GC pressure. /// public override Iterator Clone() => this; /// /// When iterating a sequence, it is possible (before evaluation of the `Tail`) to Terminate the current /// iterator and to take a new iterator that continues on from the current location. The reasons for doing /// this are to break the linked-list chain so that there isn't a big linked-list of objects in memory that /// can't be garbage collected. /// /// New iterator that starts from the current iterator position public override Iterator Split() => this; /// /// Return the number of items in the sequence. /// /// /// Requires all items to be evaluated, this will happen only once however. /// [Pure] public override long Count => 0; public override void Dispose() { } } } ================================================ FILE: LanguageExt.Core/Immutable Collections/Iterator/Extensions/Iterator.Extensions.cs ================================================ using System.Collections.Generic; using LanguageExt.Traits; namespace LanguageExt; public static partial class IteratorExtensions { /// /// Downcast operator /// public static Iterator As(this K ma) => (Iterator)ma; /// /// Get an iterator for any `IEnumerable` /// public static Iterator GetIterator(this IEnumerable enumerable) => Iterator.from(enumerable); } ================================================ FILE: LanguageExt.Core/Immutable Collections/Iterator/Iterator.Module.cs ================================================ using System; using System.Collections.Generic; namespace LanguageExt; public partial class Iterator { /// /// Create an iterator from an `IEnumerable` /// /// /// /// public static Iterator from(IEnumerable enumerable) => new Iterator.ConsFirst(enumerable); /// /// Construct a singleton sequence /// /// Head item /// Bound value type /// Iterator public static Iterator singleton(A head) => new Iterator.ConsValue(head, Iterator.Nil.Default); /// /// Construct a sequence from a head item and a tail sequence /// /// Head item /// Tail sequences /// Bound value type /// Iterator public static Iterator Cons(A head, Iterator tail) => new Iterator.ConsValue(head, tail); /// /// Construct a sequence from a head item and a tail sequence /// /// Head item /// Tail sequences /// Bound value type /// Iterator public static Iterator Cons(A head, Func> tail) => new Iterator.ConsValueLazy(head, tail); /// /// Empty sequence /// /// Bound value type /// Iterator public static Iterator Nil() => Iterator.Nil.Default; } ================================================ FILE: LanguageExt.Core/Immutable Collections/Iterator/Iterator.cs ================================================ using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics.Contracts; using System.Linq; using LanguageExt.ClassInstances; using LanguageExt.Traits; namespace LanguageExt; /// /// Wrapper for `IEnumerator` that makes it work like an immutable sequence. /// /// It is thread-safe and impossible for any item in the sequence to be enumerated more than once. /// /// /// `IEnumerator` from the .NET BCL has several problems: /// /// * It's very imperative /// * It's not thread-safe, two enumerators can't be shared /// /// The lack of support for sharing of enumerators means that it's problematic using it internally /// in types like `StreamT`, or anything that needs to keep an `IEnumerator` alive for any period /// of time. /// /// NOTE: There is a per-item allocation to hold the state of the iterator. These are discarded as /// you enumerate the sequence. However, technically it is possible to hold the initial `Iterator` /// value and subsequently gain a cached sequence of every item encountered in the enumerator. /// /// That may well be valuable for circumstances where re-evaluation would be expensive. However, /// for infinite-streams this would be extremely problematic. So, make sure you discard any /// previous `Iterator` values as you walk the sequence. /// /// Item value type public abstract partial class Iterator : IEnumerable, IEquatable>, IDisposable, K { /// /// Empty iterator /// public static readonly Iterator Empty = new Nil(); /// /// Head element /// [Pure] public abstract A Head { get; } /// /// Tail of the sequence /// [Pure] public abstract Iterator Tail { get; } /// /// Return true if there are no elements in the sequence. /// [Pure] public abstract bool IsEmpty { get; } /// /// Return the number of items in the sequence. /// /// /// Requires all items to be evaluated, this will happen only once however. /// [Pure] public abstract long Count { get; } /// /// Clone the iterator so that we can consume it without having the head item referenced. /// This will stop any GC pressure when processing large or infinite sequences. /// [Pure] public abstract Iterator Clone(); /// /// When iterating a sequence, it is possible (before evaluation of the `Tail`) to Terminate the current /// iterator and to take a new iterator that continues on from the current location. The reasons for doing /// this are to break the linked-list chain so that there isn't a big linked-list of objects in memory that /// can't be garbage collected. /// /// /// Any other iterator references that came before this one will terminate at this point. Splitting the /// previous and subsequent iterators here. /// /// New iterator that starts from the current iterator position. public abstract Iterator Split(); /// /// Create an `IEnumerable` from an `Iterator` /// [Pure] public IEnumerable AsEnumerable() { for (var ma = Clone(); !ma.IsEmpty; ma = ma.Tail) { yield return ma.Head; } } /// /// Create an `Iterable` from an `Iterator` /// [Pure] public Iterable AsIterable() => Iterable.createRange(AsEnumerable()); /// /// Deconstructor /// public void Deconstruct(out A head, out Iterator tail) { head = Head; tail = Tail; } /// /// Functor map /// [Pure] public Iterator Select(Func f) => Map(f); /// /// Functor map /// [Pure] public Iterator Map(Func f) { return Go(this, f).GetIterator(); static IEnumerable Go(Iterator ma, Func f) { for (var a = ma.Clone(); !a.IsEmpty; a = a.Tail) { yield return f(a.Head); } } } /// /// Monad bind /// [Pure] public Iterator Bind(Func> f) { return Go(this, f).GetIterator(); static IEnumerable Go(Iterator ma, Func> f) { for (var a = ma.Clone(); !a.IsEmpty; a = a.Tail) { for (var b = f(a.Head); !b.IsEmpty; b = b.Tail) { yield return b.Head; } } } } /// /// Monad bind /// [Pure] public Iterator SelectMany(Func> bind, Func project) { return Go(this, bind, project).GetIterator(); static IEnumerable Go(Iterator ma, Func> bind, Func project) { for (var a = ma.Clone(); !a.IsEmpty; a = a.Tail) { for (var b = bind(a.Head); !b.IsEmpty; b = b.Tail) { yield return project(a.Head, b.Head); } } } } /// /// Applicative apply /// [Pure] public Iterator Apply(Iterator> ff, Iterator fa) { return Go(ff, fa).GetIterator(); static IEnumerable Go(Iterator> ff, Iterator fa) { for (var f = ff.Clone(); !f.IsEmpty; f = f.Tail) { for (var a = fa.Clone(); !a.IsEmpty; a = a.Tail) { yield return f.Head(a.Head); } } } } /// /// Concatenate two iterators /// [Pure] public Iterator Concat(Iterator other) { return Go(this, other).GetIterator(); static IEnumerable Go(Iterator ma, Iterator mb) { for (var a = ma.Clone(); !a.IsEmpty; a = a.Tail) { yield return ma.Head; } for (var b = mb.Clone(); !b.IsEmpty; b = b.Tail) { yield return mb.Head; } } } /// /// Fold the sequence while there are more items remaining /// [Pure] public S Fold( S state, Func> f) { for(var xs = Clone(); !xs.IsEmpty; xs = xs.Tail) { state = f(xs.Head)(state); } return state; } /// /// Fold the sequence while there are more items remaining /// [Pure] public S Fold( S state, Func f) { for(var xs = Clone(); !xs.IsEmpty; xs = xs.Tail) { state = f(state, xs.Head); } return state; } /// /// Fold the sequence in reverse while there are more items remaining /// [Pure] public S FoldBack( S state, Func> f) { foreach(var x in Clone().AsEnumerable().Reverse()) { state = f(x)(state); } return state; } /// /// Fold the sequence in reverse while there are more items remaining /// [Pure] public S FoldBack( S state, Func f) { foreach(var x in Clone().AsEnumerable().Reverse()) { state = f(state, x); } return state; } /// /// Fold the sequence while the predicate returns `true` and there are more items remaining /// [Pure] public S FoldWhile( S state, Func> f, Func<(S State, A Value), bool> predicate) { for(var xs = Clone(); !xs.IsEmpty; xs = xs.Tail) { if (!predicate((state, xs.Head))) return state; state = f(xs.Head)(state); } return state; } /// /// Fold the sequence while the predicate returns `true` and there are more items remaining /// [Pure] public S FoldWhile( S state, Func f, Func<(S State, A Value), bool> predicate) { for(var xs = Clone(); !xs.IsEmpty; xs = xs.Tail) { if (!predicate((state, xs.Head))) return state; state = f(state, xs.Head); } return state; } /// /// Fold the sequence in reverse while the predicate returns `true` and there are more items remaining /// [Pure] public S FoldBackWhile( S state, Func> f, Func<(S State, A Value), bool> predicate) { foreach(var x in Clone().AsEnumerable().Reverse()) { if (!predicate((state, x))) return state; state = f(state)(x); } return state; } /// /// Fold the sequence in reverse while the predicate returns `true` and there are more items remaining /// [Pure] public S FoldBackWhile( S state, Func f, Func<(S State, A Value), bool> predicate) { foreach(var x in Clone().AsEnumerable().Reverse()) { if (!predicate((state, x))) return state; state = f(state, x); } return state; } /// /// Fold the sequence until the predicate returns `true` or the sequence ends /// [Pure] public S FoldUntil( S state, Func> f, Func<(S State, A Value), bool> predicate) { for(var xs = Clone(); !xs.IsEmpty; xs = xs.Tail) { state = f(xs.Head)(state); if (predicate((state, xs.Head))) return state; } return state; } /// /// Fold the sequence until the predicate returns `true` or the sequence ends /// [Pure] public S FoldUntil( S state, Func f, Func<(S State, A Value), bool> predicate) { for(var xs = Clone(); !xs.IsEmpty; xs = xs.Tail) { state = f(state, xs.Head); if (predicate((state, xs.Head))) return state; } return state; } /// /// Fold the sequence in reverse until the predicate returns `true` or the sequence ends /// [Pure] public S FoldBackUntil( S state, Func> f, Func<(S State, A Value), bool> predicate) { foreach(var x in Clone().AsEnumerable().Reverse()) { state = f(state)(x); if (predicate((state, x))) return state; } return state; } /// /// Fold the sequence in reverse until the predicate returns `true` or the sequence ends /// [Pure] public S FoldBackUntil( S state, Func f, Func<(S State, A Value), bool> predicate) { foreach(var x in Clone().AsEnumerable().Reverse()) { state = f(state, x); if (predicate((state, x))) return state; } return state; } /// /// Interleave two iterator sequences together /// /// /// Whilst there are items in both sequences, each is yielded after the other. Once one sequence /// runs out of items, the remaining items of the other sequence is yielded alone. /// [Pure] public Iterator Merge(Iterator other) { return Go(this, other).GetIterator(); static IEnumerable Go(Iterator ma, Iterator mb) { var a = ma.Clone(); var b = mb.Clone(); while (!a.IsEmpty && !b.IsEmpty) { yield return a.Head; yield return b.Head; a = a.Tail; b = b.Tail; } if (a.IsEmpty) { while (!b.IsEmpty) { yield return b.Head; b = b.Tail; } } else { while (!a.IsEmpty) { yield return a.Head; a = a.Tail; } } } } /// /// Zips the items of two sequences together /// /// /// The output sequence will be as long as the shortest input sequence. /// [Pure] public Iterator<(A First , A Second)> Zip(Iterator other) { return Go(this, other).GetIterator(); static IEnumerable<(A First , A Second)> Go(Iterator ma, Iterator mb) { var a = ma.Clone(); var b = mb.Clone(); while (!a.IsEmpty && !b.IsEmpty) { yield return (a.Head, b.Head); a = a.Tail; b = b.Tail; } } } /// /// Combine two sequences /// public static Iterator operator +(Iterator ma, Iterator mb) => ma.Concat(mb); /// /// Dispose /// public abstract void Dispose(); /// /// Equality test /// /// Other iterator to compare against /// True if equal [Pure] public override bool Equals(object? obj) => obj is Iterator other && Equals(other); /// /// Equality test /// /// Other iterator to compare against /// True if equal [Pure] public bool Equals(Iterator? rhs) { if (rhs is null) return false; var iterA = Clone(); var iterB = rhs.Clone(); while (true) { if(iterA.IsEmpty && iterB.IsEmpty) return true; if(iterA.IsEmpty || iterB.IsEmpty) return false; if(!EqDefault.Equals(iterA.Head, iterB.Head)) return false; iterA = iterA.Tail; iterB = iterB.Tail; } } [Pure] public override int GetHashCode() { if(IsEmpty) return 0; var iter = Clone(); var hash = OffsetBasis; while(!iter.IsEmpty) { var itemHash = iter.Head?.GetHashCode() ?? 0; unchecked { hash = (hash ^ itemHash) * Prime; } iter = iter.Tail; } return hash; } /// /// Get enumerator /// /// [Pure] public IEnumerator GetEnumerator() => AsEnumerable().GetEnumerator(); [Pure] IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); [Pure] public override string ToString() => CollectionFormat.ToShortArrayString(AsEnumerable()); /// /// Format the collection as `a, b, c, ...` /// [Pure] public string ToFullString(string separator = ", ") => CollectionFormat.ToFullString(AsEnumerable(), separator); /// /// Format the collection as `[a, b, c, ...]` /// [Pure] public string ToFullArrayString(string separator = ", ") => CollectionFormat.ToFullArrayString(this.AsEnumerable(), separator); const int OffsetBasis = -2128831035; const int Prime = 16777619; } ================================================ FILE: LanguageExt.Core/Immutable Collections/Iterator/Operators/Iterator.Operators.cs ================================================ using LanguageExt.Traits; namespace LanguageExt; public static partial class IteratorExtensions { extension(K _) { /// /// Downcast operator /// public static Iterator operator +(K ma) => (Iterator)ma; public static Iterator operator >> (K ma, Lower lower) => (Iterator)ma; } } ================================================ FILE: LanguageExt.Core/Immutable Collections/Iterator/README.md ================================================ `Iterator` is a functional-wrapper for `IEnumerator`. The abstraction leaks a little, so it's worth understanding how it works by reading the details below. On the whole, it behaves like an immutable stream that caches values as it goes, but there are some footguns that you should be aware of so that they can be avoided. ## Problem: `IEnumerator` * It is mutable which means using it in immutable data-structures is problematic. * If you pass an `IEnumerator` reference to two threads, each thread can call `MoveNext`and it will move the enumeration position for other thread, or even worse, move past the end of the sequence due to race conditions. * Enumerators start before the first item and use a complicated mechanism for accessing and testing the validity of the element value. _Nobody in their right mind would invent an interface like `IEnumerator` today._ ## Solution: `Iterator` `Iterator` still uses `IEnumerator` internally, but it makes it thread-safe and functional. From the outside the type acts and works exactly like any other immutable sequence, but internally it does some quite complex processing to achieve this with an `IEnumerator` reference. > You may say "Why not just drop `IEnumerator`?" - which is a completely valid position to hold. Unfortunately, > `IEnumerable` and `IEnumerator` are baked into the CPS state-machine that is used for `yield return` and > `yield break`. So, we don't get to ignore those types, and instead we need to make them play nice. `IEnumerable` has a method called `GetEnumerator()` which is used to access an `IEnumerator`. A new extension method is available called `GetIterator()`, this will yield an `Iterator`. You can pattern-match an `Iterator` like a functional 'cons' linked-list type: ```c# static A Sum(Iterator iter) where A : INumber => iter switch { Iterator.Nil => A.Zero, Iterator.Cons(var x, var xs) => x + Sum(xs), } ``` Or, use `IsEmpty` and `Head`: ```c# static A Sum(Iterator iter) where A : INumber => iter.IsEmpty ? A.Zero : iter.Head + Sum(iter.Tail); ``` Or, use built-in operators: ```c# static A Sum(Iterator iter) where A : INumber => iter.Fold(A.Zero, (s, x) => s + x); ``` Or, take an imperative approach: ```c# static A Sum(Iterator iter) where A : INumber { var total = A.Zero; while(!iter.IsEmpty) { total += iter.Head; iter = iter.Tail; } return total; } ``` And, ```c# static A Sum(Iterator iter) where A : INumber { for(var total = A.Zero; !iter.IsEmpty; iter = iter.Tail) { total += iter.Head; } return total; } ``` `Iterator` is `abstract` and the first type returned from `GetIterator()` will be a `Iterator.ConsFirst`, this type implements `Iterator`. The internal fields that `ConsFirst` contains are these: ```c# IEnumerable enumerable; int firstAcquired; Iterator? firstValue; ``` So, you can see that it doesn't actually have an `IEnumerator` as this point. The two key properties of `Iterator` are `Head` (for accessing the current item) and `Tail` (for accessing the remaining items), so let's look at those for `ConsFirst`: ```c# public override A Head => First.Head; public override Iterator Tail => First.Tail; ``` They both access `First`, which is: ```c# Iterator First { get { if (firstAcquired == 2) return firstValue!; SpinWait sw = default; while (firstAcquired < 2) { if (Interlocked.CompareExchange(ref firstAcquired, 1, 0) == 0) { try { var enumerator = enumerable.GetEnumerator(); if (enumerator.MoveNext()) { firstValue = new ConsValueEnum(enumerator.Current, enumerator); } else { enumerator.Dispose(); firstValue = Nil.Default; } firstAcquired = 2; } catch (Exception) { firstAcquired = 0; throw; } } else { sw.SpinOnce(); } } return firstValue!; } } ``` This all looks quite complex, but you should be able to see that the `Interlocked.CompareExchange` then-block is where the `IEnumerator` is created. We then either set `firstValue` to a new `ConsValueEnum` with the head-item and the `enumerator` as arguments; or we set it to `Nil`. Upon success, we set `firstAcquired` to `2`. So, subsequent calls to `First` will just return `firstValue`. This locking technique without using locks is a way to efficiently protect the enumerator from race-conditions. So, upon first access to either `Head` or `Tail` we launch the `IEnumerator` and cache the first item in the sequence. All subsequent access goes to `Head` or `Tail` on either `Nil` or `ConsValueEnum`. We never touch the `IEnumerator` again in `ConsFirst`. The `Nil` implementation isn't so surprising: ```c# public override A Head => throw new InvalidOperationException("Nil iterator has no head"); public override Iterator Tail => this; ``` `ConsValueEnum` is where it gets interesting. It has the following internal fields: ```c# Exception? exception; IEnumerator? enumerator; int tailAcquired; Iterator? tailValue; ``` It also has a `Head` property that is set in the constructor: ```c# public override A Head { get; } ``` So, we can access the `Head` value at any time, but the `Tail` value isn't yet set: ```c# public override Iterator Tail { get { if(tailAcquired == 2) return tailValue!; if(tailAcquired == 3) exception!.Rethrow(); SpinWait sw = default; while (tailAcquired < 2) { if (Interlocked.CompareExchange(ref tailAcquired, 1, 0) == 0) { try { if (enumerator!.MoveNext()) { tailValue = new ConsValueEnum(enumerator.Current, enumerator); } else { enumerator?.Dispose(); enumerator = null; tailValue = Nil.Default; } tailAcquired = 2; } catch (Exception e) { exception = e; tailAcquired = 3; throw; } } else { sw.SpinOnce(); } } if(tailAcquired == 3) exception!.Rethrow(); return tailValue!; } } ``` This does a similar thing to `ConsFirst` of protecting a section with `Interlocked.CompareExchange`. So, we can only ever access the 'then' part [of that `if` statement] once. In that block we `MoveNext` the `IEnumerator` which will either return `true` or `false`. If `true` then we create another `ConsValueEnum`, if `false` then we use `Nil`. Whichever is created gets assigned to `tailValue` and `tailAcquired` gets set to `2`. That means subsequent calls to `Tail` will just return `tailValue`. That process continues for each item of the sequence until the `IEnumerator` runs out of items to yield. The end result is a linked-list of `ConsValueEnum` objects that have a `ConsFirst` object at the head of the linked-list. So, `Iterator` effectively caches the sequence as you go. If you hold on to the head of the sequence, then the whole list may end up in memory at once. This could be problematic when working with large lazy sequences or even infinite sequences. This, for example, is fine: ```c# for(var iter = Naturals.GetIterator(); !iter.IsEmpty; iter = iter.Tail) { Console.WriteLine(iter.Head); } ``` Because the `iter` reference keeps getting updated, in-place, meaning that nothing is holding on to the head-item in the sequence, and so the garbage-collector can collect those unreferenced items. Whereas this (below) will cause memory-usage to grow and grow: ```c# var start = Naturals.GetIterator(); for(var iter = start; !iter.IsEmpty; iter = iter.Tail) { Console.WriteLine(iter.Head); } ``` Because `start` is holding a reference to the first item, so it must hold a reference (indirectly) to every subsequent item. Meaning the garbage-collector can't collect. To get around this you can use `Clone`: ```c# var start = Naturals.GetIterator(); for(var iter = start.Clone(); !iter.IsEmpty; iter = iter.Tail) { Console.WriteLine(iter.Head); } ``` This creates a new 'head' for the sequence and so `iter` is the only reference, meaning updates to `iter` make the head elements free for garbage collection. So, `Iterator` is much, much more powerful than `IEnumerator`. It is mostly useful for immutable data-types that need to carry an `IEnumerator`, but can't due to its mutable-limitations. `Iterator` has some limitations of its own, but they are relatively easy to work around, whereas that isn't the case with `IEnumerator` (without writing a type like `Iterator`!). ================================================ FILE: LanguageExt.Core/Immutable Collections/Iterator/Trait/Iterator.TraitImpl.cs ================================================ using System; using System.Linq; using LanguageExt.Common; using LanguageExt.Traits; using G = System.Collections.Generic; using static LanguageExt.Prelude; namespace LanguageExt; public partial class Iterator : Monad, MonoidK, Alternative, Traversable, Natural, Natural, Natural, Natural, Natural, Natural { static K Monad.Recur(A value, Func>> f) => Monad.unsafeRecur(value, f); static K Monad.Bind(K ma, Func> f) => ma.As().Bind(f); static K Functor.Map(Func f, K ma) => ma.As().Map(f); static K Applicative.Pure(A value) => Cons(value, Nil()); static K Applicative.Apply(K> mf, K ma) { return go().GetIterator(); G.IEnumerable go() { for (var f = mf.As().Clone(); !f.IsEmpty; f = f.Tail) { for (var a = ma.As().Clone(); !a.IsEmpty; a = a.Tail) { yield return f.Head(a.Head); } } } } static K Applicative.Apply(K> mf, Memo ma) { return go().GetIterator(); G.IEnumerable go() { for (var f = mf.As().Clone(); !f.IsEmpty; f = f.Tail) { for (var a = ma.Value.As().Clone(); !a.IsEmpty; a = a.Tail) { yield return f.Head(a.Head); } } } } static K MonoidK.Empty() => Iterator.Empty; static K Alternative.Empty() => Iterator.Empty; static K SemigroupK.Combine(K ma, K mb) => ma.As().Concat(mb.As()); static K Choice.Choose(K ma, K mb) => ma.IsEmpty ? mb : ma; static K Choice.Choose(K ma, Memo mb) => ma.IsEmpty ? mb.Value : ma; static K> Traversable.Traverse(Func> f, K ta) { return Foldable.foldBack(add, F.Pure(Iterator.Empty), ta) .Map(bs => bs.Kind()); Func>> add(K> state) => value => Applicative.lift((bs, b) => b.Cons(bs), state, f(value)); } static K> Traversable.TraverseM(Func> f, K ta) { return Foldable.foldBack(add, F.Pure(Iterator.Empty), ta) .Map(bs => bs.Kind()); Func>> add(K> state) => value => state.Bind( bs => f(value).Bind( b => F.Pure(b.Cons(bs)))); } static S Foldable.FoldWhile( Func> f, Func<(S State, A Value), bool> predicate, S state, K ta) => ta.As().FoldWhile(state, f, predicate); static S Foldable.FoldBackWhile( Func> f, Func<(S State, A Value), bool> predicate, S state, K ta) => ta.As().FoldBackWhile(state, f, predicate); static Arr Foldable.ToArr(K ta) => new(ta.As()); static Lst Foldable.ToLst(K ta) => new(ta.As()); static Iterable Foldable.ToIterable(K ta) => Iterable.createRange(ta.As()); static Seq Foldable.ToSeq(K ta) => new(ta.As()); static K Natural.Transform(K fa) => toSeq(fa.As()); static K Natural.Transform(K fa) => toArray(fa.As()); static K Natural.Transform(K fa) => toList(fa.As()); static K Natural.Transform(K fa) => toSet(fa.As()); static K Natural.Transform(K fa) => toHashSet(fa.As()); static K Natural.Transform(K fa) => Iterable.createRange(fa.As()); static Fold Foldable.FoldStep(K ta, S initialState) { // ReSharper disable once GenericEnumeratorNotDisposed var iter = ta.As().GetEnumerator(); return go(initialState); Fold go(S state) => iter.MoveNext() ? Fold.Loop(state, iter.Current, go) : Fold.Done(state); } static Fold Foldable.FoldStepBack(K ta, S initialState) { // ReSharper disable once GenericEnumeratorNotDisposed var iter = ta.As().Reverse().GetEnumerator(); return go(initialState); Fold go(S state) => iter.MoveNext() ? Fold.Loop(state, iter.Current, go) : Fold.Done(state); } } ================================================ FILE: LanguageExt.Core/Immutable Collections/IteratorAsync/DSL/IteratorAsync.Cons.cs ================================================ using System.Threading.Tasks; namespace LanguageExt; public abstract partial class IteratorAsync { /// /// Cons iterator case. /// /// Contains a head value and a tail that represents the rest of the sequence. /// public abstract class Cons : IteratorAsync { public void Deconstruct(out ValueTask head, out ValueTask> tail) { head = Head; tail = Tail; } } } ================================================ FILE: LanguageExt.Core/Immutable Collections/IteratorAsync/DSL/IteratorAsync.ConsFirst.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics.Contracts; using System.Threading; using System.Threading.Tasks; namespace LanguageExt; public abstract partial class IteratorAsync { internal sealed class ConsFirst : Cons { IAsyncEnumerable enumerable; int firstAcquired; IteratorAsync? firstValue; internal ConsFirst(IAsyncEnumerable enumerable) => this.enumerable = enumerable; public override ValueTask Head => First().Bind(f => f.Head); public override ValueTask> Tail => First().Bind(f => f.Tail); public new void Deconstruct(out ValueTask head, out ValueTask> tail) { head = Head; tail = Tail; } async ValueTask> First() { if (firstAcquired == 2) return firstValue!; SpinWait sw = default; while (firstAcquired < 2) { if (Interlocked.CompareExchange(ref firstAcquired, 1, 0) == 0) { try { var enumerator = enumerable.GetAsyncEnumerator(); if (await enumerator.MoveNextAsync()) { firstValue = new ConsValueEnum(new(enumerator.Current), enumerator); } else { await enumerator.DisposeAsync(); firstValue = Nil.Default; } firstAcquired = 2; } catch (Exception) { firstAcquired = 0; throw; } } else { sw.SpinOnce(); } } return firstValue!; } /// /// Return true if there are no elements in the sequence. /// public override ValueTask IsEmpty => First().Bind(f => f.IsEmpty); /// /// Clone the iterator so that we can consume it without having the head item referenced. /// This will stop any GC pressure. /// public override IteratorAsync Clone() => new ConsFirst(enumerable); /// /// When iterating a sequence, it is possible (before evaluation of the `Tail`) to Terminate the current /// iterator and to take a new iterator that continues on from the current location. The reasons for doing /// this are to break the linked-list chain so that there isn't a big linked-list of objects in memory that /// can't be garbage collected. /// /// New iterator that starts from the current iterator position public override IteratorAsync Split() => Clone(); /// /// Return the number of items in the sequence. /// /// /// Requires all items to be evaluated, this will happen only once however. /// [Pure] public override ValueTask Count => First().Bind(f => f.Count); public override async ValueTask DisposeAsync() { if (Interlocked.CompareExchange(ref firstAcquired, 1, 2) == 2) { var fv = firstValue; if(fv != null) await fv.DisposeAsync(); firstValue = null; firstAcquired = 0; } } } } ================================================ FILE: LanguageExt.Core/Immutable Collections/IteratorAsync/DSL/IteratorAsync.ConsValue.cs ================================================ using System.Diagnostics.Contracts; using System.Threading.Tasks; namespace LanguageExt; public abstract partial class IteratorAsync { internal sealed class ConsValue : Cons { readonly A head; readonly IteratorAsync tail; public ConsValue(A head, IteratorAsync tail) { this.head = head; this.tail = tail; } public new void Deconstruct(out ValueTask h, out ValueTask> t) { h = Head; t = Tail; } /// /// Head element /// public override ValueTask Head => new(head); /// /// Tail of the sequence /// public override ValueTask> Tail => new(tail); /// /// Return true if there are no elements in the sequence. /// public override ValueTask IsEmpty => new(false); /// /// Clone the iterator so that we can consume it without having the head item referenced. /// This will stop any GC pressure. /// public override IteratorAsync Clone() => new ConsValue(head, tail.Clone()); /// /// When iterating a sequence, it is possible (before evaluation of the `Tail`) to Terminate the current /// iterator and to take a new iterator that continues on from the current location. The reasons for doing /// this are to break the linked-list chain so that there isn't a big linked-list of objects in memory that /// can't be garbage collected. /// /// New iterator that starts from the current iterator position public override IteratorAsync Split() => this; /// /// Return the number of items in the sequence. /// /// /// Requires all items to be evaluated, this will happen only once however. /// [Pure] public override ValueTask Count => Tail.Bind(t => t.Count.Map(c => c + 1)); public override async ValueTask DisposeAsync() => await (await Tail).DisposeAsync(); } } ================================================ FILE: LanguageExt.Core/Immutable Collections/IteratorAsync/DSL/IteratorAsync.ConsValueEnum.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics.Contracts; using System.Threading; using System.Threading.Tasks; using LanguageExt.Common; namespace LanguageExt; /// /// Wrapper for `IEnumerator` that makes it work like an immutable sequence. /// /// It is thread-safe and impossible for any item in the sequence to be enumerated more than once. /// /// /// `IEnumerator` from the .NET BCL has several problems: /// /// * It's very imperative /// * It's not thread-safe, two enumerators can't be shared /// /// The lack of support for sharing of enumerators means that it's problematic using it internally /// in types like `StreamT`, or anything that needs to keep an `IEnumerator` alive for any period /// of time. /// /// NOTE: There is a per-item allocation to hold the state of the iterator. These are discarded as /// you enumerate the sequence. However, technically it is possible to hold the initial `Iterator` /// value and subsequently gain a cached sequence of every item encountered in the enumerator. /// /// That may well be valuable for circumstances where re-evaluation would be expensive. However, /// for infinite-streams this would be extremely problematic. So, make sure you discard any /// previous `IteratorAsync` values as you walk the sequence. /// /// Item value type public abstract partial class IteratorAsync { internal sealed class ConsValueEnum : Cons { Exception? exception; IAsyncEnumerator? enumerator; int tailAcquired; IteratorAsync? tailValue; internal ConsValueEnum(ValueTask head, IAsyncEnumerator enumerator) { Head = head; this.enumerator = enumerator; } public new void Deconstruct(out ValueTask head, out ValueTask> tail) { head = Head; tail = Tail; } /// /// Head element /// public override ValueTask Head { get; } /// /// Tail of the sequence /// public override ValueTask> Tail { get { if(tailAcquired == 2) return new(tailValue!); if(tailAcquired == 3) exception!.Rethrow(); return TailAsync(); } } /// /// Tail of the sequence /// async ValueTask> TailAsync() { SpinWait sw = default; while (tailAcquired < 2) { if (Interlocked.CompareExchange(ref tailAcquired, 1, 0) == 0) { try { if (await enumerator!.MoveNextAsync()) { tailValue = new ConsValueEnum(new(enumerator.Current), enumerator); } else { var e = enumerator; if(e != null) await e.DisposeAsync(); enumerator = null; tailValue = Nil.Default; } tailAcquired = 2; } catch (Exception e) { exception = e; tailAcquired = 3; throw; } } else { sw.SpinOnce(); } } if(tailAcquired == 3) exception!.Rethrow(); return tailValue!; } /// /// Return true if there are no elements in the sequence. /// public override ValueTask IsEmpty => new(false); /// /// Clone the iterator so that we can consume it without having the head item referenced. /// This will stop any GC pressure. /// public override IteratorAsync Clone() => this; /// /// When iterating a sequence, it is possible (before evaluation of the `Tail`) to Terminate the current /// iterator and to take a new iterator that continues on from the current location. The reasons for doing /// this are to break the linked-list chain so that there isn't a big linked-list of objects in memory that /// can't be garbage collected. /// /// New iterator that starts from the current iterator position public override IteratorAsync Split() { if (Interlocked.CompareExchange(ref tailAcquired, 1, 0) == 0) { tailValue = Nil.Default; tailAcquired = 2; return new ConsValueEnum(Head, enumerator!); } else { return this; } } /// /// Return the number of items in the sequence. /// /// /// Requires all items to be evaluated, this will happen only once however. /// [Pure] public override ValueTask Count => Tail.Bind(t => t.Count.Map(c => c + 1)); public override async ValueTask DisposeAsync() { var e = enumerator; if(e != null) await e.DisposeAsync(); enumerator = null; } } } ================================================ FILE: LanguageExt.Core/Immutable Collections/IteratorAsync/DSL/IteratorAsync.ConsValueLazy.cs ================================================ using System; using System.Diagnostics.Contracts; using System.Threading; using System.Threading.Tasks; using LanguageExt.Common; namespace LanguageExt; public abstract partial class IteratorAsync { internal sealed class ConsValueLazy : Cons { ValueTask head; Exception? error; IteratorAsync? tail; Func>? tailF; int tailAcquired; public ConsValueLazy(ValueTask head, Func> tailF) { this.head = head; this.tailF = tailF; tail = null; } public new void Deconstruct(out ValueTask h, out ValueTask> t) { h = Head; t = Tail; } /// /// Head element /// public override ValueTask Head => head; /// /// Tail of the sequence /// public override ValueTask> Tail { get { if (tailAcquired == 2) return new(tail!); if (tailAcquired == 3) error!.Rethrow(); return TailLazy(); } } ValueTask> TailLazy() { SpinWait sw = default; while (tailAcquired < 2) { if (Interlocked.CompareExchange(ref tailAcquired, 1, 0) == 0) { try { tail = tailF!(); tailF = null; tailAcquired = 2; } catch(Exception e) { error = e; tailF = null; tailAcquired = 3; throw; } } else { sw.SpinOnce(); } } return new(tail!); } /// /// Return true if there are no elements in the sequence. /// public override ValueTask IsEmpty => new(false); /// /// Clone the iterator so that we can consume it without having the head item referenced. /// This will stop any GC pressure. /// public override IteratorAsync Clone() => this; /// /// When iterating a sequence, it is possible (before evaluation of the `Tail`) to Terminate the current /// iterator and to take a new iterator that continues on from the current location. The reasons for doing /// this are to break the linked-list chain so that there isn't a big linked-list of objects in memory that /// can't be garbage collected. /// /// New iterator that starts from the current iterator position public override IteratorAsync Split() { var h = head; var t = tailF; return tailAcquired == 0 ? new ConsValueLazy(h, t!) : this; } /// /// Return the number of items in the sequence. /// /// /// Requires all items to be evaluated, this will happen only once however. /// [Pure] public override ValueTask Count => Tail.Bind(static t => t.Count.Map(static c => c + 1)); public override async ValueTask DisposeAsync() { if (tailAcquired == 2) { await (await Tail).DisposeAsync(); } } } } ================================================ FILE: LanguageExt.Core/Immutable Collections/IteratorAsync/DSL/IteratorAsync.Nil.cs ================================================ using System; using System.Diagnostics.Contracts; using System.Threading.Tasks; namespace LanguageExt; public abstract partial class IteratorAsync { /// /// Nil iterator case /// /// The end of the sequence. /// public sealed class Nil : IteratorAsync { public static readonly IteratorAsync Default = new Nil(); /// /// Head element /// public override ValueTask Head => throw new InvalidOperationException("Nil iterator has no head"); /// /// Tail of the sequence /// public override ValueTask> Tail => new(this); /// /// Return true if there are no elements in the sequence. /// public override ValueTask IsEmpty => new(true); /// /// Clone the iterator so that we can consume it without having the head item referenced. /// This will stop any GC pressure. /// public override IteratorAsync Clone() => this; /// /// When iterating a sequence, it is possible (before evaluation of the `Tail`) to Terminate the current /// iterator and to take a new iterator that continues on from the current location. The reasons for doing /// this are to break the linked-list chain so that there isn't a big linked-list of objects in memory that /// can't be garbage collected. /// /// New iterator that starts from the current iterator position public override IteratorAsync Split() => this; /// /// Return the number of items in the sequence. /// /// /// Requires all items to be evaluated, this will happen only once however. /// [Pure] public override ValueTask Count => new(0); public override ValueTask DisposeAsync() => ValueTask.CompletedTask; } } ================================================ FILE: LanguageExt.Core/Immutable Collections/IteratorAsync/Extensions/IteratorAsync.Extensions.cs ================================================ using System.Collections.Generic; using LanguageExt.Traits; namespace LanguageExt; public static partial class IteratorAsyncExtensions { /// /// Downcast operator /// public static IteratorAsync As(this K ma) => (IteratorAsync)ma; /// /// Get an iterator for any `IAsyncEnumerable` /// public static IteratorAsync GetIteratorAsync(this IAsyncEnumerable enumerable) => IteratorAsync.from(enumerable); } ================================================ FILE: LanguageExt.Core/Immutable Collections/IteratorAsync/IteratorAsync.Module.cs ================================================ using System; using System.Collections.Generic; namespace LanguageExt; public partial class IteratorAsync { /// /// Create an iterator from an `IAsyncEnumerable` /// /// /// /// public static IteratorAsync from(IAsyncEnumerable enumerable) => new IteratorAsync.ConsFirst(enumerable); /// /// Construct a singleton sequence /// /// Head item /// Bound value type /// IteratorAsync public static IteratorAsync singleton(A head) => new IteratorAsync.ConsValue(head, IteratorAsync.Nil.Default); /// /// Construct a sequence from a head item and a tail sequence /// /// Head item /// Tail sequences /// Bound value type /// IteratorAsync public static IteratorAsync Cons(A head, IteratorAsync tail) => new IteratorAsync.ConsValue(head, tail); /// /// Construct a sequence from a head item and a tail sequence /// /// Head item /// Tail sequences /// Bound value type /// IteratorAsync public static IteratorAsync Cons(A head, Func> tail) => new IteratorAsync.ConsValueLazy(new(head), tail); /// /// Empty sequence /// /// Bound value type /// IteratorAsync public static IteratorAsync Nil() => IteratorAsync.Nil.Default; } ================================================ FILE: LanguageExt.Core/Immutable Collections/IteratorAsync/IteratorAsync.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics.Contracts; using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt; /// /// Wrapper for `IEnumerator` that makes it work like an immutable sequence. /// /// It is thread-safe and impossible for any item in the sequence to be enumerated more than once. /// /// /// `IEnumerator` from the .NET BCL has several problems: /// /// * It's very imperative /// * It's not thread-safe, two enumerators can't be shared /// /// The lack of support for sharing of enumerators means that it's problematic using it internally /// in types like `StreamT`, or anything that needs to keep an `IEnumerator` alive for any period /// of time. /// /// NOTE: There is a per-item allocation to hold the state of the iterator. These are discarded as /// you enumerate the sequence. However, technically it is possible to hold the initial `Iterator` /// value and subsequently gain a cached sequence of every item encountered in the enumerator. /// /// That may well be valuable for circumstances where re-evaluation would be expensive. However, /// for infinite-streams this would be extremely problematic. So, make sure you discard any /// previous `IteratorAsync` values as you walk the sequence. /// public abstract partial class IteratorAsync : IAsyncEnumerable, IAsyncDisposable, K { /// /// Empty iterator /// public static readonly IteratorAsync Empty = new Nil(); /// /// Head element /// [Pure] public abstract ValueTask Head { get; } /// /// Tail of the sequence /// [Pure] public abstract ValueTask> Tail { get; } /// /// Return true if there are no elements in the sequence. /// [Pure] public abstract ValueTask IsEmpty { get; } /// /// Return the number of items in the sequence. /// /// /// Requires all items to be evaluated, this will happen only once however. /// [Pure] public abstract ValueTask Count { get; } /// /// Clone the iterator so that we can consume it without having the head item referenced. /// This will stop any GC pressure when processing large or infinite sequences. /// [Pure] public abstract IteratorAsync Clone(); /// /// When iterating a sequence, it is possible (before evaluation of the `Tail`) to Terminate the current /// iterator and to take a new iterator that continues on from the current location. The reasons for doing /// this are to break the linked-list chain so that there isn't a big linked-list of objects in memory that /// can't be garbage collected. /// /// /// Any other iterator references that came before this one will terminate at this point. Splitting the /// previous and subsequent iterators here. /// /// New iterator that starts from the current iterator position. public abstract IteratorAsync Split(); /// /// Create an `IEnumerable` from an `Iterator` /// [Pure] public async IAsyncEnumerable AsEnumerable([EnumeratorCancellation] CancellationToken token) { for (var ma = Clone(); !await ma.IsEmpty; ma = await ma.Tail) { if (token.IsCancellationRequested) throw new OperationCanceledException(); yield return await ma.Head; } } /// /// Functor map /// [Pure] public IteratorAsync Select(Func f) => Map(f); /// /// Functor map /// [Pure] public IteratorAsync Map(Func f) { return Go(this, f).GetIteratorAsync(); static async IAsyncEnumerable Go(IteratorAsync ma, Func f) { for (var a = ma.Clone(); !await a.IsEmpty; a = await a.Tail) { yield return f(await a.Head); } } } /// /// Monad bind /// [Pure] public IteratorAsync Bind(Func> f) { return Go(this, f).GetIteratorAsync(); static async IAsyncEnumerable Go(IteratorAsync ma, Func> f) { for (var a = ma.Clone(); !await a.IsEmpty; a = await a.Tail) { for (var b = f(await a.Head); !await b.IsEmpty; b = await b.Tail) { yield return await b.Head; } } } } /// /// Monad bind /// [Pure] public IteratorAsync SelectMany(Func> bind, Func project) { return Go(this, bind, project).GetIteratorAsync(); static async IAsyncEnumerable Go(IteratorAsync ma, Func> bind, Func project) { for (var a = ma.Clone(); !await a.IsEmpty; a = await a.Tail) { for (var b = bind(await a.Head); !await b.IsEmpty; b = await b.Tail) { yield return project(await a.Head, await b.Head); } } } } /// /// Applicative apply /// [Pure] public IteratorAsync Apply(IteratorAsync> ff, IteratorAsync fa) { return Go(ff, fa).GetIteratorAsync(); static async IAsyncEnumerable Go(IteratorAsync> ff, IteratorAsync fa) { for (var f = ff.Clone(); !await f.IsEmpty; f = await f.Tail) { for (var a = fa.Clone(); !await a.IsEmpty; a = await a.Tail) { yield return (await f.Head)(await a.Head); } } } } /// /// Concatenate two iterators /// [Pure] public IteratorAsync Concat(IteratorAsync other) { return Go(this, other).GetIteratorAsync(); static async IAsyncEnumerable Go(IteratorAsync ma, IteratorAsync mb) { for (var a = ma.Clone(); !await a.IsEmpty; a = await a.Tail) { yield return await ma.Head; } for (var b = mb.Clone(); !await b.IsEmpty; b = await b.Tail) { yield return await mb.Head; } } } /// /// Fold the sequence while there are more items remaining /// [Pure] public async ValueTask Fold( S state, Func> f) { for(var xs = Clone(); !await xs.IsEmpty; xs = await xs.Tail) { state = f(await xs.Head)(state); } return state; } /// /// Fold the sequence while there are more items remaining /// [Pure] public async ValueTask Fold( S state, Func f) { for(var xs = Clone(); !await xs.IsEmpty; xs = await xs.Tail) { state = f(state, await xs.Head); } return state; } /// /// Fold the sequence while the predicate returns `true` and there are more items remaining /// [Pure] public async ValueTask FoldWhile( S state, Func> f, Func<(S State, A Value), bool> predicate) { for(var xs = Clone(); !await xs.IsEmpty; xs = await xs.Tail) { if (!predicate((state, await xs.Head))) return state; state = f(await xs.Head)(state); } return state; } /// /// Fold the sequence while the predicate returns `true` and there are more items remaining /// [Pure] public async ValueTask FoldWhile( S state, Func f, Func<(S State, A Value), bool> predicate) { for(var xs = Clone(); !await xs.IsEmpty; xs = await xs.Tail) { if (!predicate((state, await xs.Head))) return state; state = f(state, await xs.Head); } return state; } /// /// Fold the sequence until the predicate returns `true` or the sequence ends /// [Pure] public async ValueTask FoldUntil( S state, Func> f, Func<(S State, A Value), bool> predicate) { for(var xs = Clone(); !await xs.IsEmpty; xs = await xs.Tail) { state = f(await xs.Head)(state); if (predicate((state, await xs.Head))) return state; } return state; } /// /// Fold the sequence until the predicate returns `true` or the sequence ends /// [Pure] public async ValueTask FoldUntil( S state, Func f, Func<(S State, A Value), bool> predicate) { for(var xs = Clone(); !await xs.IsEmpty; xs = await xs.Tail) { state = f(state, await xs.Head); if (predicate((state, await xs.Head))) return state; } return state; } /// /// Interleave two iterator sequences together /// /// /// Whilst there are items in both sequences, each is yielded after the other. Once one sequence /// runs out of items, the remaining items of the other sequence is yielded alone. /// [Pure] public IteratorAsync Merge(IteratorAsync other) { return Go(this, other).GetIteratorAsync(); static async IAsyncEnumerable Go(IteratorAsync ma, IteratorAsync mb) { var a = ma.Clone(); var b = mb.Clone(); // TODO: Replace this with a WaitAny tasks for each side, replacing the // task as each one yields. while (!await a.IsEmpty && !await b.IsEmpty) { yield return await a.Head; yield return await b.Head; a = await a.Tail; b = await b.Tail; } if (await a.IsEmpty) { while (!await b.IsEmpty) { yield return await b.Head; b = await b.Tail; } } else { while (!await a.IsEmpty) { yield return await a.Head; a = await a.Tail; } } } } /// /// Zips the items of two sequences together /// /// /// The output sequence will be as long as the shortest input sequence. /// [Pure] public IteratorAsync<(A First , A Second)> Zip(IteratorAsync other) { return Go(this, other).GetIteratorAsync(); static async IAsyncEnumerable<(A First , A Second)> Go(IteratorAsync ma, IteratorAsync mb) { var a = ma.Clone(); var b = mb.Clone(); while (!await a.IsEmpty && !await b.IsEmpty) { yield return (await a.Head, await b.Head); a = await a.Tail; b = await b.Tail; } } } /// /// Combine two sequences /// public static IteratorAsync operator +(IteratorAsync ma, IteratorAsync mb) => ma.Concat(mb); /// /// Dispose /// public abstract ValueTask DisposeAsync(); /// /// Get enumerator /// /// [Pure] public IAsyncEnumerator GetAsyncEnumerator(CancellationToken token) => AsEnumerable(token).GetAsyncEnumerator(token); [Pure] public override string ToString() => "async iterator"; } ================================================ FILE: LanguageExt.Core/Immutable Collections/IteratorAsync/Operators/IteratorAsync.Operators.cs ================================================ using LanguageExt.Traits; namespace LanguageExt; public static partial class IteratorAsyncExtensions { extension(K _) { /// /// Downcast operator /// public static IteratorAsync operator +(K ma) => (IteratorAsync)ma; public static IteratorAsync operator >> (K ma, Lower lower) => (IteratorAsync)ma; } } ================================================ FILE: LanguageExt.Core/Immutable Collections/IteratorAsync/README.md ================================================ `IteratorAsync` is a functional-wrapper for `IAsyncEnumerator`. The abstraction leaks a little, so it's worth understanding how it works by reading the details below. On the whole it behaves like an immutable stream that caches values as it goes, but there's some footguns that you should be aware of so that they can be avoided. ## Problem: `IAsyncEnumerator` * It is mutable which means using it in immutable data-structures is problematic. * If you pass an `IAsyncEnumerator` reference to two threads, each thread can call `MoveNext`and it will move the enumeration position for other thread, or even worse, move past the end of the sequence due to race conditions. * Enumerators start before the first item and use a complicated mechanism for accessing and testing the validity of the element value. _Nobody in their right mind would invent an interface like `IAsyncEnumerator` today._ ## Solution: `IteratorAsync` `IteratorAsync` still uses `IAsyncEnumerator` internally, but it makes it thread-safe and functional. From the outside the type acts and works exactly like any other immutable sequence, but internally it does some quite complex processing to achieve this with an `IAsyncEnumerator` reference. > You may say "Why not just drop `IAsyncEnumerator`?" - which is a completely valid position to hold. Unfortunately, > `IAsyncEnumerable` and `IAsyncEnumerator` are baked into the CPS state-machine that is used for `yield return` and > `yield break`. So, we don't get to ignore those types, and instead we need to make them play nice. `IAsyncEnumerable` has a method called `GetAsyncEnumerator()` which is used to access an `IAsyncEnumerator`. A new extension method is available called `GetIteratorAsync()`, this will yield an `IteratorAsync`. ================================================ FILE: LanguageExt.Core/Immutable Collections/IteratorAsync/Trait/IteratorAsync.TraitImpl.cs ================================================ using System; using LanguageExt.Common; using LanguageExt.Traits; using G = System.Collections.Generic; using static LanguageExt.Prelude; namespace LanguageExt; public partial class IteratorAsync : Monad { static K Monad.Recur(A value, Func>> f) => Monad.unsafeRecur(value, f); static K Monad.Bind(K ma, Func> f) => ma.As().Bind(f); static K Functor.Map(Func f, K ma) => ma.As().Map(f); static K Applicative.Pure(A value) => Cons(value, Nil()); static K Applicative.Apply(K> mf, K ma) { return from(go()); async G.IAsyncEnumerable go() { for (var fi = mf.As().Clone(); !await fi.IsEmpty; fi = await fi.Tail) { for (var ai = ma.As().Clone(); !await ai.IsEmpty; ai = await ai.Tail) { yield return (await fi.Head)(await ai.Head); } } } } static K Applicative.Apply( K> mf, Memo ma) { return from(go()); async G.IAsyncEnumerable go() { for (var fi = mf.As().Clone(); !await fi.IsEmpty; fi = await fi.Tail) { for (var ai = ma.Value.As().Clone(); !await ai.IsEmpty; ai = await ai.Tail) { yield return (await fi.Head)(await ai.Head); } } } } } ================================================ FILE: LanguageExt.Core/Immutable Collections/List/Extensions/Lst.Extensions.MapApply.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public static partial class LstExtensions { /// /// Functor map operation /// /// /// Unwraps the value within the functor, passes it to the map function `f` provided, and /// then takes the mapped value and wraps it back up into a new functor. /// /// Functor to map /// Mapping function /// Mapped functor public static Lst Map(this Func f, K ma) => Functor.map(f, ma).As(); /// /// Functor map operation /// /// /// Unwraps the value within the functor, passes it to the map function `f` provided, and /// then takes the mapped value and wraps it back up into a new functor. /// /// Functor to map /// Mapping function /// Mapped functor public static Lst Map(this Func f, Lst ma) => Functor.map(f, ma).As(); /// /// Applicative action: runs the first applicative, ignores the result, and returns the second applicative /// public static Lst Action(this Lst ma, K mb) => Applicative.action(ma, mb).As(); /// /// Applicative action: runs the first applicative, ignores the result, and returns the second applicative /// public static Lst Action(this K ma, K mb) => Applicative.action(ma, mb).As(); /// /// Applicative functor apply operation /// /// /// Unwraps the value within the `ma` applicative-functor, passes it to the unwrapped function(s) within `mf`, and /// then takes the resulting value and wraps it back up into a new applicative-functor. /// /// Value(s) applicative functor /// Mapping function(s) /// Mapped applicative functor public static Lst Apply(this Lst> mf, K ma) => Applicative.apply(mf, ma).As(); /// /// Applicative functor apply operation /// /// /// Unwraps the value within the `ma` applicative-functor, passes it to the unwrapped function(s) within `mf`, and /// then takes the resulting value and wraps it back up into a new applicative-functor. /// /// Value(s) applicative functor /// Mapping function(s) /// Mapped applicative functor public static Lst Apply(this K> mf, K ma) => Applicative.apply(mf, ma).As(); } ================================================ FILE: LanguageExt.Core/Immutable Collections/List/Extensions/Lst.Extensions.cs ================================================ #pragma warning disable CS0693 // Type parameter has the same name as the type parameter from outer type using System; using System.Linq; using System.Collections.Generic; using static LanguageExt.Prelude; using System.Diagnostics.Contracts; using System.Text; using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt; public static partial class LstExtensions { public static Lst As(this K xs) => (Lst)xs; /// /// Accesses the head of an enumerable and yields the remainder without multiple-enumerations /// /// Throws if sequence is empty public static (A Head, IEnumerable Tail) HeadAndTail(this IEnumerable ma) => ma.HeadAndTailSafe() .IfNone(() => throw Exceptions.SequenceEmpty); /// /// Accesses the head of an enumerable and yields the remainder without multiple-enumerations /// public static Option<(A Head, IEnumerable Tail)> HeadAndTailSafe(this IEnumerable ma) { var iter = ma.GetEnumerator(); A head; if (iter.MoveNext()) { head = iter.Current; } else { iter.Dispose(); return None; } return Some((head, tail(iter))); static IEnumerable tail(IEnumerator rest) { try { while (rest.MoveNext()) { yield return rest.Current; } } finally { rest.Dispose(); } } } /// /// Monadic join /// [Pure] public static Lst Flatten(this Lst> ma) => ma.Bind(identity); /// /// Monadic join /// [Pure] public static IEnumerable Flatten(this IEnumerable> ma) => ma.Bind(identity); /// /// Match empty list, or multi-item list /// /// Return value type /// Match for an empty list /// Match for a non-empty /// Result of match function invoked public static B Match(this IEnumerable list, Func Empty, Func, B> More) => toSeq(list).Match(Empty, More); /// /// List pattern matching /// [Pure] public static B Match(this IEnumerable list, Func Empty, Func, B> More) => toSeq(list).Match(Empty, More); /// /// List pattern matching /// [Pure] public static R Match(this IEnumerable list, Func Empty, Func One, Func, R> More ) => toSeq(list).Match(Empty, One, More); /// /// Get all items in the list except the last one /// /// /// Must evaluate the last item to know it's the last, but won't return it /// /// List /// The initial items (all but the last) [Pure] public static IEnumerable Init(this IEnumerable list) => List.init(list); /// /// Get the tail of the list (skips the head item) /// /// List /// Enumerable of T [Pure] public static Iterable Tail(this IEnumerable list) => List.tail(list); /// /// Inject a value in between each item in the enumerable /// /// Enumerable to inject values into /// Item to inject /// Bound type /// An enumerable with the values injected [Pure] public static IEnumerable Intersperse(this IEnumerable ma, A value) { var isFirst = true; foreach(var item in ma) { if (!isFirst) { yield return value; } yield return item; isFirst = false; } } /// /// Concatenate all strings into one /// [Pure] public static string Concat(this IEnumerable xs) { var sb = new StringBuilder(); foreach (var x in xs) { sb.Append(x); } return sb.ToString(); } /// /// Reverses the list (Reverse in LINQ) /// /// List item type /// Listto reverse /// Reversed list [Pure] public static Lst Rev(this Lst list) => List.rev(list); /// /// Applies a function to each element of the collection (from last element to first), threading /// an accumulator argument through the computation. This function first applies the function /// to the first two elements of the list. Then, it passes this result into the function along /// with the third element and so on. Finally, it returns the final result. /// /// Enumerable item type /// Enumerable to reduce /// Reduce function /// Aggregate value [Pure] public static T Reduce(this IEnumerable list, Func reducer) => List.reduce(list, reducer); /// /// Applies a function to each element of the collection, threading an accumulator argument /// through the computation. This function first applies the function to the first two /// elements of the list. Then, it passes this result into the function along with the third /// element and so on. Finally, it returns the final result. /// /// Enumerable item type /// Enumerable to reduce /// Reduce function /// Aggregate value [Pure] public static T ReduceBack(this IEnumerable list, Func reducer) => List.reduceBack(list, reducer); /// /// Applies a function to each element of the collection, threading an accumulator argument /// through the computation. This function takes the state argument, and applies the function /// to it and the first element of the list. Then, it passes this result into the function /// along with the second element, and so on. Finally, it returns the list of intermediate /// results and the final result. /// /// State type /// Enumerable item type /// Enumerable to fold /// Initial state /// Folding function /// Aggregate state [Pure] public static IEnumerable Scan(this IEnumerable list, S state, Func folder) => List.scan(list, state, folder); /// /// Applies a function to each element of the collection (from last element to first), /// threading an accumulator argument through the computation. This function takes the state /// argument, and applies the function to it and the first element of the list. Then, it /// passes this result into the function along with the second element, and so on. Finally, /// it returns the list of intermediate results and the final result. /// /// State type /// Enumerable item type /// Enumerable to fold /// Initial state /// Folding function /// Aggregate state [Pure] public static IEnumerable ScanBack(this IEnumerable list, S state, Func folder) => List.scanBack(list, state, folder); /// /// Iterate each item in the enumerable in order (consume items) /// /// Enumerable item type /// Enumerable to consume /// Unit public static Unit Consume(this IEnumerable list) => List.consume(list); /// /// Return a new enumerable with all duplicate values removed /// /// Enumerable item type /// Enumerable /// A new enumerable with all duplicate values removed [Pure] public static IEnumerable Distinct(this IEnumerable list) where EQ : Eq => List.distinct(list); /// /// The tails function returns all final segments of the argument, longest first. For example, /// i.e. tails(['a','b','c']) == [['a','b','c'], ['b','c'], ['c'],[]] /// /// List item type /// List /// Enumerable of Enumerables of T [Pure] public static IEnumerable> Tails(this IEnumerable self) => List.tails(self); /// /// Span, applied to a predicate 'pred' and a list, returns a tuple where first element is /// longest prefix (possibly empty) of elements that satisfy 'pred' and second element is the /// remainder of the list: /// /// /// List.span(List(1,2,3,4,1,2,3,4), x => x 〈 3) == (List(1,2),List(3,4,1,2,3,4)) /// /// /// List.span(List(1,2,3), x => x 〈 9) == (List(1,2,3),List()) /// /// /// List.span(List(1,2,3), x => x 〈 0) == (List(),List(1,2,3)) /// /// List element type /// List /// Predicate /// Split list [Pure] public static (IEnumerable, IEnumerable) Span(this IEnumerable self, Func pred) => List.span(self, pred); /// /// Monadic bind function for IEnumerable /// [Pure] public static IEnumerable Bind(this IEnumerable self, Func> binder) => EnumerableOptimal.BindFast(self, binder); /// /// LINQ Select implementation for Lst /// [Pure] public static Lst Select(this Lst self, Func map) => new (self.AsIterable().Select(map)); /// /// Monadic bind function for Lst that returns an IEnumerable /// [Pure] public static IEnumerable BindEnumerable(this Lst self, Func> binder) => EnumerableOptimal.BindFast(self, binder); /// /// Monadic bind function /// [Pure] public static Lst Bind(this Lst self, Func> binder) => new (self.BindEnumerable(binder)); /// /// Monadic bind function /// [Pure] public static Lst Bind(this Lst self, Func> binder) => Bind(self, x => binder(x).As()); /// /// Returns the number of items in the Lst T /// /// Item type /// List to count /// The number of items in the list [Pure] public static int Count(this Lst self) => self.Count; /// /// LINQ bind implementation for Lst /// [Pure] public static Lst SelectMany(this Lst self, Func> bind, Func project) => self.Bind(t => bind(t).Map(u => project(t, u))); /// /// Take all but the last item in an enumerable /// /// Bound value type public static IEnumerable SkipLast(this IEnumerable self) { using var iter = self.GetEnumerator(); bool remaining; var first = true; T? item = default; do { remaining = iter.MoveNext(); if (remaining) { if (!first) yield return item!; item = iter.Current; first = false; } } while (remaining); } /// /// Take all but the last n items in an enumerable /// /// Bound value type public static IEnumerable SkipLast(this IEnumerable self, int n) { using var iter = self.GetEnumerator(); bool remaining ; var cache = new Queue(n + 1); do { remaining = iter.MoveNext(); if (remaining) { cache.Enqueue(iter.Current); if (cache.Count > n) yield return cache.Dequeue(); } } while (remaining); } /// /// Convert the enumerable to an Option. /// /// Bound value type /// This /// If enumerable is empty then return None, else Some(head) public static Option ToOption(this IEnumerable self) => self.Match( () => Option.None, (x, _) => Option.Some(x)); /// /// Convert to a queryable /// [Pure] public static IQueryable AsQueryable(this Lst source) => // NOTE TO FUTURE ME: Don't delete this thinking it's not needed! source.Value.AsQueryable(); } ================================================ FILE: LanguageExt.Core/Immutable Collections/List/Internal/Lst.Internal.cs ================================================ using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Diagnostics.Contracts; using LanguageExt.Traits; using static LanguageExt.Prelude; using LanguageExt.ClassInstances; using System.Runtime.CompilerServices; namespace LanguageExt; /// /// Immutable list /// /// Value type [Serializable] internal class LstInternal : IReadOnlyList, IEquatable>, ListInfo { /// /// Empty list /// public static readonly LstInternal Empty = new (); internal ListItem root; internal int hashCode; [MethodImpl(MethodImplOptions.AggressiveInlining)] internal LstInternal(IEnumerable items) { hashCode = 0; if (items is Lst lst) { root = lst.Value.Root; } else { root = ListItem.EmptyM; root = ListModuleM.InsertMany(root, items, 0); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal LstInternal(ReadOnlySpan items) { hashCode = 0; root = ListItem.EmptyM; root = ListModuleM.InsertMany(root, items, 0); } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static LstInternal Wrap(ListItem list) => new (list); /// /// Ctor /// [MethodImpl(MethodImplOptions.AggressiveInlining)] internal LstInternal() { hashCode = 0; root = ListItem.Empty; } /// /// Ctor /// [MethodImpl(MethodImplOptions.AggressiveInlining)] internal LstInternal(ListItem root) { hashCode = 0; this.root = root; } internal ListItem Root { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => root; } /// /// Index accessor /// [Pure] public A this[int index] { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { if (index < 0 || index >= Root.Count) throw new IndexOutOfRangeException(); return ListModule.GetItem(Root, index); } } /// /// Number of items in the list /// [Pure] public int Count { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => Root.Count; } [Pure] int IReadOnlyCollection.Count { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => Count; } [Pure] A IReadOnlyList.this[int index] { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { if (index < 0 || index >= Root.Count) throw new IndexOutOfRangeException(); return ListModule.GetItem(Root, index); } } /// /// Add an item to the end of the list /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public LstInternal Add(A value) => Wrap(ListModule.Add(Root, value)); /// /// Add a range of items to the end of the list /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public LstInternal AddRange(IEnumerable items) { if (Count == 0) return new LstInternal(items); return Wrap(ListModule.AddRange(Root, items)); } /// /// Clear the list /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public LstInternal Clear() => Empty; /// /// Get enumerator /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public ListEnumerator GetEnumerator() => new (Root, false, 0); /// /// Find the index of an item /// [Pure] public int IndexOf(A item, int index = 0, int count = -1, IEqualityComparer? equalityComparer = null) { count = count == -1 ? Count : count; equalityComparer ??= EqualityComparer.Default; if (count == 0) return -1; if (index < 0 || index >= Root.Count) throw new IndexOutOfRangeException(); foreach (var x in Skip(index)) { if (equalityComparer.Equals(x, item)) { return index; } index++; count--; if (count == 0) return -1; } return -1; } /// /// Insert value at specified index /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public LstInternal Insert(int index, A value) { if (index < 0 || index > Root.Count) throw new IndexOutOfRangeException(); return Wrap(ListModule.Insert(Root, value, index)); } /// /// Insert range of values at specified index /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public LstInternal InsertRange(int index, IEnumerable items) { if (index < 0 || index > Root.Count) throw new IndexOutOfRangeException(); return Wrap(ListModule.InsertMany(Root, items, index)); } /// /// Find the last index of an item in the list /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public int LastIndexOf(A item, int index = 0, int count = -1, IEqualityComparer? equalityComparer = null) => Count - Reverse().IndexOf(item, index, count, equalityComparer) - 1; /// /// Remove an item from the list /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public LstInternal Remove(A value) => Remove(value, EqualityComparer.Default); /// /// Remove all items that match `value` from the list /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public LstInternal Remove(A value, IEqualityComparer equalityComparer) => Wrap(ListModule.Remove(Root, value, equalityComparer)); /// /// Remove all items that match a predicate /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public LstInternal RemoveAll(Func pred) => Wrap(ListModule.Remove(Root, pred)); /// /// Remove item at location /// /// /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public LstInternal RemoveAt(int index) { if (index < 0 || index >= Root.Count) throw new IndexOutOfRangeException(); return Wrap(ListModule.Remove(Root, index)); } /// /// Remove a range of items /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public LstInternal RemoveRange(int index, int count) { if (index < 0 || index >= Root.Count) throw new IndexOutOfRangeException(); if (index + count > Root.Count) throw new IndexOutOfRangeException(); var self = this; for (; count > 0; count--) { self = self.RemoveAt(index); } return self; } /// /// Set an item at the specified index /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public LstInternal SetItem(int index, A value) { if (isnull(value)) throw new ArgumentNullException(nameof(value)); if (index < 0 || index >= Root.Count) throw new IndexOutOfRangeException(); return new LstInternal(ListModule.SetItem(Root, value, index)); } [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] IEnumerator IEnumerable.GetEnumerator() => new ListEnumerator(Root, false, 0); [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] IEnumerator IEnumerable.GetEnumerator() => new ListEnumerator(Root, false, 0); [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Iterable Skip(int amount) { return Iterable.createRange(Go()); IEnumerable Go() { var iter = new ListEnumerator(Root, false, amount); while (iter.MoveNext()) { yield return iter.Current; } } } /// /// Reverse the order of the items in the list /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public LstInternal Reverse() => new (this.AsEnumerable().Reverse()); /// /// Fold /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public S Fold(S state, Func folder) { foreach (var item in this) { state = folder(state, item); } return state; } /// /// Map /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public LstInternal Map(Func map) => new (this.AsEnumerable().Select(map)); [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Iterable FindRange(int index, int count) { if (index < 0 || index >= Count) throw new ArgumentOutOfRangeException(nameof(index)); if (count < 0) throw new ArgumentOutOfRangeException(nameof(index)); return Iterable.createRange(Go()); IEnumerable Go() { var iter = new ListEnumerator(Root, false, index, count); while (iter.MoveNext()) { yield return iter.Current; } } } /// /// Filter /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public LstInternal Filter(Func pred) { IEnumerable Yield() { foreach (var item in this) { if (pred(item)) { yield return item; } } } return new LstInternal(Yield()); } [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static LstInternal operator +(LstInternal lhs, A rhs) => lhs.Add(rhs); [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static LstInternal operator +(A rhs, LstInternal lhs) => new (rhs.Cons(lhs)); [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static LstInternal operator +(LstInternal lhs, LstInternal rhs) => lhs.Combine(rhs); [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public LstInternal Combine(LstInternal rhs) => AddRange(rhs); [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static LstInternal operator -(LstInternal lhs, LstInternal rhs) => lhs.Subtract(rhs); [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public LstInternal Subtract(LstInternal rhs) { var self = this; foreach (var item in rhs) { self = self.Remove(item); } return self; } [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public override bool Equals(object? obj) => obj is LstInternal @as && Equals(@as); /// /// Get the hash code /// Lazily (and once only) calculates the hash from the elements in the list /// Empty list hash == 0 /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public override int GetHashCode() => hashCode == 0 ? hashCode = FNV32.Hash, A>(this.AsEnumerable()) : hashCode; [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(LstInternal? other) { if (ReferenceEquals(this, other)) return true; if (ReferenceEquals(other, null)) return false; return Count == other.Count && EqEnumerable.Equals(this, other); } [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(LstInternal lhs, LstInternal rhs) => lhs.Equals(rhs); [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(LstInternal lhs, LstInternal rhs) => !lhs.Equals(rhs); [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public int CompareTo(LstInternal other) { var cmp = Count.CompareTo(other.Count); if (cmp != 0) return cmp; using var iterA = GetEnumerator(); using var iterB = other.GetEnumerator(); while (iterA.MoveNext() && iterB.MoveNext()) { cmp = OrdDefault.Compare(iterA.Current, iterB.Current); if (cmp != 0) return cmp; } return 0; } [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public int CompareTo(LstInternal other) where OrdA : Ord { var cmp = Count.CompareTo(other.Count); if (cmp != 0) return cmp; using var iterA = GetEnumerator(); using var iterB = other.GetEnumerator(); while (iterA.MoveNext() && iterB.MoveNext()) { cmp = OrdA.Compare(iterA.Current, iterB.Current); if (cmp != 0) return cmp; } return 0; } } [Serializable] class ListItem { public static ListItem EmptyM => new (0, 0, null!, default!, null!); public static readonly ListItem Empty = new(0, 0, null!, default!, null!); public bool IsEmpty { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => Count == 0; } public int Count; public byte Height; public ListItem Left; public ListItem Right; /// /// Ctor /// [MethodImpl(MethodImplOptions.AggressiveInlining)] internal ListItem(byte height, int count, ListItem left, T key, ListItem right) { Count = count; Height = height; Key = key; Left = left; Right = right; } internal int BalanceFactor { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => Count == 0 ? 0 : Right.Height - Left.Height; } public T Key { [MethodImpl(MethodImplOptions.AggressiveInlining)] get; [MethodImpl(MethodImplOptions.AggressiveInlining)] internal set; } public bool IsBalanced { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => (uint)(BalanceFactor + 1) <= 2; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public override string ToString() => IsEmpty ? "(empty)" : Key?.ToString() ?? "[null]"; } internal static class ListModuleM { [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ListItem InsertMany(ListItem node, IEnumerable items, int index) => Insert(node, BuildSubTree(items), index); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ListItem InsertMany(ListItem node, ReadOnlySpan items, int index) => Insert(node, BuildSubTree(items), index); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ListItem BuildSubTree(IEnumerable items) { var root = ListItem.EmptyM; var subIndex = 0; foreach (var item in items) { root = Insert(root, new ListItem(1, 1, ListItem.Empty, item, ListItem.Empty), subIndex); subIndex++; } return root; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ListItem BuildSubTree(ReadOnlySpan items) { var root = ListItem.EmptyM; var subIndex = 0; foreach (var item in items) { root = Insert(root, new ListItem(1, 1, ListItem.Empty, item, ListItem.Empty), subIndex); subIndex++; } return root; } public static ListItem Insert(ListItem node, ListItem insertNode, int index) { if (node.IsEmpty) { return insertNode; } else if (index == node.Left.Count) { insertNode.Left = node.Left; insertNode = Balance(insertNode); node.Left = insertNode; node = Balance(node); return node; } else if (index < node.Left.Count) { node.Left = Insert(node.Left, insertNode, index); return Balance(node); } else { node.Right = Insert(node.Right, insertNode, index - node.Left.Count - 1); return Balance(node); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ListItem Balance(ListItem node) { node.Height = (byte)(1 + Math.Max(node.Left.Height, node.Right.Height)); node.Count = 1 + node.Left.Count + node.Right.Count; return node.BalanceFactor >= 2 ? node.Right.BalanceFactor < 0 ? DblRotLeft(node) : RotLeft(node) : node.BalanceFactor <= -2 ? node.Left.BalanceFactor > 0 ? DblRotRight(node) : RotRight(node) : node; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ListItem DblRotRight(ListItem node) { node.Left = RotLeft(node.Left); return RotRight(node); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ListItem DblRotLeft(ListItem node) { node.Right = RotRight(node.Right); return RotLeft(node); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ListItem RotRight(ListItem node) { if (node.IsEmpty || node.Left.IsEmpty) return node; var y = node; var x = y.Left; var t2 = x.Right; x.Right = y; y.Left = t2; y.Height = (byte)(1 + Math.Max(y.Left.Height, y.Right.Height)); x.Height = (byte)(1 + Math.Max(x.Left.Height, x.Right.Height)); y.Count = 1 + y.Left.Count + y.Right.Count; x.Count = 1 + x.Left.Count + x.Right.Count; return x; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ListItem RotLeft(ListItem node) { if (node.IsEmpty || node.Right.IsEmpty) return node; var x = node; var y = x.Right; var t2 = y.Left; y.Left = x; x.Right = t2; x.Height = (byte)(1 + Math.Max(x.Left.Height, x.Right.Height)); y.Height = (byte)(1 + Math.Max(y.Left.Height, y.Right.Height)); x.Count = 1 + x.Left.Count + x.Right.Count; y.Count = 1 + y.Left.Count + y.Right.Count; return y; } } static class ListModule { public static S Fold(ListItem node, S state, Func folder) { if (node.IsEmpty) { return state; } state = Fold(node.Left, state, folder); state = folder(state, node.Key); state = Fold(node.Right, state, folder); return state; } public static bool ForAll(ListItem node, Func pred) => node.IsEmpty || (pred(node.Key) && ForAll(node.Left, pred) && ForAll(node.Right, pred)); public static bool Exists(ListItem node, Func pred) => !node.IsEmpty && (pred(node.Key) || Exists(node.Left, pred) || Exists(node.Right, pred)); public static ListItem Map(ListItem node, Func f) => node.IsEmpty ? ListItem.Empty : new ListItem(node.Height, node.Count, Map(node.Left, f), f(node.Key), Map(node.Right, f)); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ListItem AddRange(ListItem node, IEnumerable items) => AddRange(node, ListModuleM.BuildSubTree(items)); static ListItem AddRange(ListItem node, ListItem insertNode) => node.IsEmpty ? insertNode : Balance(Make(node.Key, node.Left, AddRange(node.Right, insertNode))); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ListItem InsertMany(ListItem node, IEnumerable items, int index) { var root = node; var subIndex = index; foreach(var item in items) { root = Insert(root, item, subIndex); subIndex++; } return root; } public static ListItem Insert(ListItem node, A key, int index) { if (node.IsEmpty) { return new ListItem(1, 1, ListItem.Empty, key, ListItem.Empty); } else if (index == node.Left.Count) { var insertedLeft = Balance(Make(key, node.Left, ListItem.Empty)); var newThis = Balance(Make(node.Key, insertedLeft, node.Right)); return newThis; } else if (index < node.Left.Count) { return Balance(Make(node.Key, Insert(node.Left, key, index), node.Right)); } else { return Balance(Make(node.Key, node.Left, Insert(node.Right, key, index - node.Left.Count - 1))); } } public static ListItem Add(ListItem node, A key) => node.IsEmpty ? new ListItem(1, 1, ListItem.Empty, key, ListItem.Empty) : Balance(Make(node.Key, node.Left, Add(node.Right, key))); public static ListItem SetItem(ListItem node, A key, int index) { if (node.IsEmpty) { throw new ArgumentException("Index outside the bounds of the list"); } if (index == node.Left.Count) { return new ListItem(node.Height, node.Count, node.Left, key, node.Right); } else if (index < node.Left.Count) { return new ListItem(node.Height, node.Count, SetItem(node.Left, key, index), node.Key, node.Right); } else { return new ListItem(node.Height, node.Count, node.Left, node.Key, SetItem(node.Right, key, index - node.Left.Count - 1)); } } public static T GetItem(ListItem node, int index) { if (node.IsEmpty) { throw new ArgumentException("Index outside the bounds of the list"); } if (index == node.Left.Count) { return node.Key; } else if (index < node.Left.Count) { return GetItem(node.Left, index); } else { return GetItem(node.Right, index - node.Left.Count - 1); } } public static ListItem Remove(ListItem node, Func pred) { if (node.IsEmpty) { return node; } var result = node; var left = node.Left.IsEmpty ? node.Left : Remove(node.Left, pred); var right = node.Right.IsEmpty ? node.Right : Remove(node.Right, pred); if (pred(node.Key)) { if (right.IsEmpty && left.IsEmpty) { result = ListItem.Empty; } else if (right.IsEmpty && !left.IsEmpty) { result = left; } else if (!right.IsEmpty && left.IsEmpty) { result = Balance(right); } else { var next = right; while (!next.Left.IsEmpty) { next = next.Left; } right = Remove(right, 0); result = Balance(Make(next.Key, left, right)); } } else { if (!ReferenceEquals(left, node.Left) || !ReferenceEquals(right, node.Right)) { result = Balance(Make(node.Key, left, right)); } } return result.IsEmpty || result.IsBalanced ? result : Balance(result); } public static ListItem Remove(ListItem node, T value, IEqualityComparer compare) { if (node.IsEmpty) { return node; } var result = node; var left = node.Left.IsEmpty ? node.Left : Remove(node.Left, value, compare); var right = node.Right.IsEmpty ? node.Right : Remove(node.Right, value, compare); if (ReferenceEquals(node.Key, value) || compare.Equals(node.Key, value)) { if (right.IsEmpty && left.IsEmpty) { result = ListItem.Empty; } else if (right.IsEmpty && !left.IsEmpty) { result = left; } else if (!right.IsEmpty && left.IsEmpty) { result = Balance(right); } else { var next = right; while (!next.Left.IsEmpty) { next = next.Left; } right = Remove(right, 0); result = Balance(Make(next.Key, left, right)); } } else { if(!ReferenceEquals(left, node.Left) || !ReferenceEquals(right, node.Right)) { result = Balance(Make(node.Key, left, right)); } } return result.IsEmpty || result.IsBalanced ? result : Balance(result); } public static ListItem Remove(ListItem node, int index) { if (node.IsEmpty) { return node; } ListItem result; if (index == node.Left.Count) { if (node.Right.IsEmpty && node.Left.IsEmpty) { result = ListItem.Empty; } else if (node.Right.IsEmpty && !node.Left.IsEmpty) { result = node.Left; } else if (!node.Right.IsEmpty && node.Left.IsEmpty) { result = Balance(node.Right); } else { var next = node.Right; while (!next.Left.IsEmpty) { next = next.Left; } var right = Remove(node.Right, 0); result = Balance(Make(next.Key, node.Left, right)); } } else if (index < node.Left.Count) { var left = Remove(node.Left, index); result = Make(node.Key, left, node.Right); } else { var right = Remove(node.Right, index - node.Left.Count - 1); result = Make(node.Key, node.Left, right); } return result.IsEmpty || result.IsBalanced ? result : Balance(result); } public static int Find(ListItem node, T key, int index, int count, IComparer comparer) { if (node.IsEmpty || node.Count <= 0) { return ~index; } int nodeIndex = node.Left.Count; if (index + count <= nodeIndex) { return Find(node.Left, key, index, count, comparer); } else if (index > nodeIndex) { int result = Find(node.Right, key, index - nodeIndex - 1, count, comparer); int offset = nodeIndex + 1; return result < 0 ? result - offset : result + offset; } int compare = comparer.Compare(key, node.Key); if (compare == 0) { return nodeIndex; } else if (compare > 0) { int adjcount = count - (nodeIndex - index) - 1; int result = adjcount < 0 ? -1 : Find(node.Right,key, 0, adjcount, comparer); int offset = nodeIndex + 1; return result < 0 ? result - offset : result + offset; } else { if (index == nodeIndex) { return ~index; } return Find(node.Left,key,index,count,comparer); } } public static ListItem Skip(ListItem node, int amount) { if (amount == 0 || node.IsEmpty) { return node; } if (amount > node.Count) { return ListItem.Empty; } if (!node.Left.IsEmpty && node.Left.Count == amount) { return Balance(Make(node.Key, ListItem.Empty, node.Right)); } if (!node.Left.IsEmpty && node.Left.Count == amount - 1) { return node.Right; } if (node.Left.IsEmpty) { return Skip(node.Right, amount - 1); } var newleft = Skip(node.Left, amount); var remaining = amount - node.Left.Count - newleft.Count; if (remaining > 0) { return Skip(Balance(Make(node.Key, newleft, node.Right)), remaining); } else { return Balance(Make(node.Key, newleft, node.Right)); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ListItem Make(T k, ListItem l, ListItem r) => new ((byte)(1 + Math.Max(l.Height, r.Height)), l.Count + r.Count + 1, l, k, r); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ListItem Balance(ListItem node) => node.BalanceFactor >= 2 ? node.Right.BalanceFactor < 0 ? DblRotLeft(node) : RotLeft(node) : node.BalanceFactor <= -2 ? node.Left.BalanceFactor > 0 ? DblRotRight(node) : RotRight(node) : node; [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ListItem RotRight(ListItem node) => node.IsEmpty || node.Left.IsEmpty ? node : Make(node.Left.Key, node.Left.Left, Make(node.Key, node.Left.Right, node.Right)); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ListItem RotLeft(ListItem node) => node.IsEmpty || node.Right.IsEmpty ? node : Make(node.Right.Key, Make(node.Key, node.Left, node.Right.Left), node.Right.Right); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ListItem DblRotRight(ListItem node) => node.IsEmpty ? node : RotRight(Make(node.Key, RotLeft(node.Left), node.Right)); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ListItem DblRotLeft(ListItem node) => node.IsEmpty ? node : RotLeft(Make(node.Key, node.Left, RotRight(node.Right))); } public struct ListEnumerator : IEnumerator { internal struct NewStack : New[]> { public ListItem[] New() => new ListItem[32]; } ListItem[] stack; int stackDepth; readonly ListItem map; int left; readonly bool rev; readonly int start; int count; [MethodImpl(MethodImplOptions.AggressiveInlining)] internal ListEnumerator(ListItem root, bool rev, int start, int count = int.MaxValue) { this.rev = rev; this.start = start; map = root; stack = Pool[]>.Pop(); this.count = count; stackDepth = default; left = default; NodeCurrent = default!; Reset(); } private ListItem NodeCurrent { [MethodImpl(MethodImplOptions.AggressiveInlining)] get; [MethodImpl(MethodImplOptions.AggressiveInlining)] set; } public readonly T Current { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => NodeCurrent.Key; } object IEnumerator.Current { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => NodeCurrent.Key!; } public void Dispose() { if (stack != null) { Pool[]>.Push(stack); stack = null!; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private ListItem Next(ListItem node) => rev ? node.Left : node.Right; [MethodImpl(MethodImplOptions.AggressiveInlining)] private ListItem Prev(ListItem node) => rev ? node.Right : node.Left; [MethodImpl(MethodImplOptions.AggressiveInlining)] private void Push(ListItem node) { while (!node.IsEmpty) { stack[stackDepth] = node; stackDepth++; node = Prev(node); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool MoveNext() { if (count > 0 && left > 0 && stackDepth > 0) { stackDepth--; NodeCurrent = stack[stackDepth]; Push(Next(NodeCurrent)); left--; count--; return true; } NodeCurrent = default!; return false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Reset() { var skip = rev ? map.Count - start - 1 : start; stackDepth = 0; NodeCurrent = map; left = map.Count; while (!NodeCurrent.IsEmpty && skip != Prev(NodeCurrent).Count) { if (skip < Prev(NodeCurrent).Count) { stack[stackDepth] = NodeCurrent; stackDepth++; NodeCurrent = Prev(NodeCurrent); } else { skip -= Prev(NodeCurrent).Count + 1; NodeCurrent = Next(NodeCurrent); } } if (!NodeCurrent.IsEmpty) { stack[stackDepth] = NodeCurrent; stackDepth++; } } } ================================================ FILE: LanguageExt.Core/Immutable Collections/List/ListInfo.cs ================================================ namespace LanguageExt; public interface ListInfo { int Count { get; } } ================================================ FILE: LanguageExt.Core/Immutable Collections/List/Lst.Module.cs ================================================ #pragma warning disable CS0693 // Type parameter has the same name as the type parameter from outer type using System; using System.Linq; using System.Collections.Generic; using static LanguageExt.Prelude; using System.ComponentModel; using System.Diagnostics.Contracts; using LanguageExt.Traits; using LanguageExt.ClassInstances; namespace LanguageExt; public static class List { /// /// Monadic join /// [Pure] public static Lst flatten(Lst> ma) => ma.Bind(identity); /// /// Monadic join /// [Pure] public static Iterable flatten(IEnumerable> ma) => ma.Bind(identity).AsIterable(); /// /// Create an empty IEnumerable T /// [Pure] public static Lst empty() => Lst.Empty; /// /// Create a singleton collection /// /// Single value /// Collection with a single item in it [Pure] public static Lst singleton(A value) => [value]; /// /// Create a new empty list /// /// Lst T [Pure] public static Lst create() => Lst.Empty; /// /// Create a list from a initial set of items /// /// Items /// Lst T [Pure] public static Lst create(params T[] items) => new (items.AsSpan()); /// /// Create a list from an initial set of items /// /// Items /// Lst T [Pure] public static Lst createRange(IEnumerable items) => new (items); /// /// Create a list from an initial set of items /// /// Items /// Lst T [Pure] public static Lst createRange(ReadOnlySpan items) => items.IsEmpty ? Lst.Empty : new (items); /// /// Generates a sequence of T using the provided delegate to initialise /// each item. /// [Pure] public static Iterable generate(int count, Func generator) => IterableExtensions.AsIterable(Range(0, count)).Map(generator); /// /// Generates an int.MaxValue sequence of T using the provided delegate to initialise /// each item. /// [Pure] public static Iterable generate(Func generator) => IterableExtensions.AsIterable(Range(0, int.MaxValue)).Map(generator); /// /// Generates a sequence that contains one repeated value. /// [Pure] public static Iterable repeat(T item, int count) => IterableExtensions.AsIterable(Range(0, count)).Map(_ => item); /// /// Add an item to the list /// /// List /// Item to add /// A new Lst T [Pure] public static Lst add(Lst list, T value) => list.Add(value); /// /// Add a range of items to the list /// /// List /// Items to add /// A new Lst T [Pure] public static Lst addRange(Lst list, IEnumerable value) => list.AddRange(value); /// /// Remove an item from the list /// /// List /// value to remove /// A new Lst T [Pure] public static Lst remove(Lst list, T value) => list.Remove(value); /// /// Remove an item at a specified index in the list /// /// List /// Index of item to remove /// A new Lst T [Pure] public static Lst removeAt(Lst list, int index) => list.RemoveAt(index); /// /// Get the item at the head (first) of the list /// /// List /// Head item [Pure] public static T head(IEnumerable list) => list.First(); /// /// Get the item at the head (first) of the list or None if the list is empty /// /// List /// Optional head item [Pure] public static Option headOrNone(IEnumerable list) => list.Select(Option.Some) .DefaultIfEmpty(Option.None) .FirstOrDefault(); /// /// Get the item at the head (first) of the list or Left if the list is empty /// /// List /// Either head item or left [Pure] public static Either headOrLeft(IEnumerable list, L left) => list.Select(Either.Right) .DefaultIfEmpty(Either.Left(left)) .FirstOrDefault() ?? left; /// /// Get the item at the head (first) of the list or fail if the list is empty /// /// List /// Either head item or fail [Pure] public static Validation headOrInvalid(IEnumerable list, Fail fail) where Fail : Monoid => list.Select(Validation.Success) .DefaultIfEmpty(Validation.Fail(fail)) .FirstOrDefault() ?? Fail.Empty; /// /// Get the item at the head (first) of the list or fail if the list is empty /// /// List /// Either head item or fail [Pure] public static Validation headOrInvalid(IEnumerable list) where Fail : Monoid => list.Select(Validation.Success) .DefaultIfEmpty(Validation.Fail(Fail.Empty)) .FirstOrDefault() ?? Fail.Empty; /// /// Get the last item of the list /// /// List /// Last item [Pure] public static A last(IEnumerable list) => list.Last(); /// /// Get the last item of the list /// /// List /// Last item [Pure] public static Option lastOrNone(IEnumerable list) => list.Select(Option.Some) .DefaultIfEmpty(Option.None) .LastOrDefault(); /// /// Get the last item of the list /// /// List /// Last item [Pure] public static Either lastOrLeft(IEnumerable list, L left) => list.Select(Either.Right) .DefaultIfEmpty(Either.Left(left)) .LastOrDefault() ?? left; /// /// Get the last item of the list /// /// List /// Last item [Pure] public static Validation lastOrInvalid(IEnumerable list, Fail fail) where Fail : Monoid => list.Select(Validation.Success) .DefaultIfEmpty(Validation.Fail(fail)) .LastOrDefault() ?? fail; /// /// Get the last item of the list /// /// List /// Last item [Pure] public static Validation lastOrInvalid(IEnumerable list) where Fail : Monoid => list.Select(Validation.Success) .DefaultIfEmpty(Validation.Fail(Fail.Empty)) .LastOrDefault() ?? Fail.Empty; /// /// Get all items in the list except the last one /// /// /// Must evaluate the last item to know it's the last, but won't return it /// /// List /// The initial items (all but the last) [Pure] public static Seq init(IEnumerable list) { var items = list.ToArray(); return new Seq(new SeqStrict(items, 0, Math.Max(0, items.Length - 1), 0, 0)); } /// /// Get the tail of the list (skips the head item) /// /// List /// Enumerable of T [Pure] public static Iterable tail(IEnumerable list) => list.Skip(1).AsIterable(); /// /// Projects the values in the enumerable using a map function into a new enumerable (Select in LINQ). /// /// Enumerable item type /// Return enumerable item type /// Enumerable to map /// Map function /// Mapped enumerable [Pure] public static Iterable map(IEnumerable list, Func map) => list.Select(map).AsIterable(); /// /// Projects the values in the enumerable into a new enumerable using a map function, which is also given an index value /// (Select in LINQ - note that the order of the arguments of the map function are the other way around, here the index /// is the first argument). /// /// Enumerable item type /// Return enumerable item type /// Enumerable to map /// Map function /// Mapped enumerable [Pure] public static Iterable map(IEnumerable list, Func map) => zip(list, Range(0, int.MaxValue), (t, i) => map(i, t)).AsIterable(); /// /// Removes items from the list that do not match the given predicate (Where in LINQ) /// /// Enumerable item type /// Enumerable to filter /// Predicate function /// Filtered enumerable [Pure] public static Iterable filter(IEnumerable list, Func predicate) => list.Where(predicate).AsIterable(); /// /// Applies the given function 'selector' to each element of the list. Returns the list comprised of /// the results for each element where the function returns Some(f(x)). /// /// Enumerable item type /// Enumerable /// Selector function /// Mapped and filtered enumerable [Pure] public static Iterable choose(IEnumerable list, Func> selector) => map(filter(map(list, selector), t => t.IsSome), t => t.Value!); /// /// Applies the given function 'selector' to each element of the list. Returns the list comprised of /// the results for each element where the function returns Some(f(x)). /// An index value is passed through to the selector function also. /// /// Enumerable item type /// Enumerable /// Selector function /// Mapped and filtered enumerable [Pure] public static Iterable choose(IEnumerable list, Func> selector) => map(filter(map(list, selector), t => t.IsSome), t => t.Value!); /// /// For each element of the list, applies the given function. Concatenates all the results and /// returns the combined list. /// /// Enumerable item type /// Return enumerable item type /// Enumerable to map /// Map function /// Mapped enumerable [Pure] public static Iterable collect(IEnumerable list, Func> map) => (from t in list from r in map(t) select r).AsIterable(); /// /// Returns the sum total of all the items in the list (Sum in LINQ) /// /// List to sum /// Sum total [Pure] public static int sum(IEnumerable list) => fold(list, 0, (s, x) => s + x); /// /// Returns the sum total of all the items in the list (Sum in LINQ) /// /// List to sum /// Sum total [Pure] public static float sum(IEnumerable list) => fold(list, 0.0f, (s, x) => s + x); /// /// Returns the sum total of all the items in the list (Sum in LINQ) /// /// List to sum /// Sum total [Pure] public static double sum(IEnumerable list) => fold(list, 0.0, (s, x) => s + x); /// /// Returns the sum total of all the items in the list (Sum in LINQ) /// /// List to sum /// Sum total [Pure] public static decimal sum(IEnumerable list) => fold(list, (decimal)0, (s, x) => s + x); /// /// Reverses the enumerable (Reverse in LINQ) /// /// Enumerable item type /// Enumerable to reverse /// Reversed enumerable [Pure] public static Iterable rev(IEnumerable list) => list.Reverse().AsIterable(); /// /// Reverses the list (Reverse in LINQ) /// /// List item type /// List to reverse /// Reversed list [Pure] public static Lst rev(Lst list) => list.Reverse(); /// /// Concatenate two enumerables (Concat in LINQ) /// /// Enumerable item type /// First enumerable /// Second enumerable /// Concatenated enumerable [Pure] public static Iterable append(IEnumerable lhs, IEnumerable rhs) => lhs.ConcatFast(rhs).AsIterable(); /// /// Concatenate an enumerable and an enumerable of enumerables /// /// List item type /// First list /// Second list /// Concatenated list [Pure] public static Iterable append(IEnumerable x, IEnumerable> xs) => xs.HeadAndTailSafe() .Match( None: x.AsIterable, Some: tuple => append(x, append(tuple.Head, tuple.Tail))); /// /// Concatenate N enumerables /// /// Enumerable type /// Enumerables to concatenate /// A single enumerable with all of the items concatenated [Pure] public static Iterable append(params IEnumerable[] lists) => lists.Length == 0 ? Iterable.empty() : lists.Length == 1 ? lists[0].AsIterable() : append(lists[0], lists.Skip(1)); /// /// Applies a function 'folder' to each element of the collection, threading an accumulator /// argument through the computation. The fold function takes the state argument, and /// applies the function 'folder' to it and the first element of the list. Then, it feeds this /// result into the function 'folder' along with the second element, and so on. It returns the /// final result. (Aggregate in LINQ) /// /// State type /// Enumerable item type /// Enumerable to fold /// Initial state /// Fold function /// Aggregate value [Pure] public static S fold(IEnumerable list, S state, Func folder) { foreach (var item in list) { state = folder(state, item); } return state; } /// /// Applies a function 'folder' to each element of the collection (from last element to first), /// threading an aggregate state through the computation. The fold function takes the state /// argument, and applies the function 'folder' to it and the first element of the list. Then, /// it feeds this result into the function 'folder' along with the second element, and so on. It /// returns the final result. /// /// State type /// Enumerable item type /// Enumerable to fold /// Initial state /// Fold function /// Aggregate value [Pure] public static S foldBack(IEnumerable list, S state, Func folder) => fold(rev(list), state, folder); /// /// Applies a function 'folder' to each element of the collection whilst the predicate function /// returns True for the item being processed, threading an aggregate state through the /// computation. The fold function takes the state argument, and applies the function 'folder' /// to it and the first element of the list. Then, it feeds this result into the function 'folder' /// along with the second element, and so on. It returns the final result. /// /// State type /// Enumerable item type /// Enumerable to fold /// Initial state /// Fold function /// Predicate function /// Aggregate value [Pure] public static S foldWhile(IEnumerable list, S state, Func folder, Func preditem) { foreach (var item in list) { if (!preditem(item)) { return state; } state = folder(state, item); } return state; } /// /// Applies a function 'folder' to each element of the collection, threading an accumulator /// argument through the computation (and whilst the predicate function returns True when passed /// the aggregate state). The fold function takes the state argument, and applies the function /// 'folder' to it and the first element of the list. Then, it feeds this result into the /// function 'folder' along with the second element, and so on. It returns the final result. /// /// State type /// Enumerable item type /// Enumerable to fold /// Initial state /// Fold function /// Predicate function /// Aggregate value [Pure] public static S foldWhile(IEnumerable list, S state, Func folder, Func predstate) { foreach (var item in list) { if (!predstate(state)) { return state; } state = folder(state, item); } return state; } /// /// Applies a function 'folder' to each element of the collection (from last element to first) /// whilst the predicate function returns True for the item being processed, threading an /// aggregate state through the computation. The fold function takes the state argument, and /// applies the function 'folder' to it and the first element of the list. Then, it feeds this /// result into the function 'folder' along with the second element, and so on. It returns the /// final result. /// /// State type /// Enumerable item type /// Enumerable to fold /// Initial state /// Fold function /// Predicate function /// Aggregate value [Pure] public static S foldBackWhile(IEnumerable list, S state, Func folder, Func preditem) => foldWhile(rev(list), state, folder, preditem: preditem); /// /// Applies a function 'folder' to each element of the collection (from last element to first), /// threading an accumulator argument through the computation (and whilst the predicate function /// returns True when passed the aggregate state). The fold function takes the state argument, /// and applies the function 'folder' to it and the first element of the list. Then, it feeds /// this result into the function 'folder' along with the second element, and so on. It returns /// the final result. /// /// State type /// Enumerable item type /// Enumerable to fold /// Initial state /// Fold function /// Predicate function /// Aggregate value [Pure] public static S foldBackWhile(IEnumerable list, S state, Func folder, Func predstate) => foldWhile(rev(list), state, folder, predstate: predstate); /// /// Applies a function 'folder' to each element of the collection whilst the predicate function /// returns False for the item being processed, threading an aggregate state through the /// computation. The fold function takes the state argument, and applies the function 'folder' /// to it and the first element of the list. Then, it feeds this result into the function 'folder' /// along with the second element, and so on. It returns the final result. /// /// State type /// Enumerable item type /// Enumerable to fold /// Initial state /// Fold function /// Predicate function /// Aggregate value [Pure] public static S foldUntil(IEnumerable list, S state, Func folder, Func preditem) { foreach (var item in list) { if (preditem(item)) { return state; } state = folder(state, item); } return state; } /// /// Applies a function 'folder' to each element of the collection, threading an accumulator /// argument through the computation (and whilst the predicate function returns False when passed /// the aggregate state). The fold function takes the state argument, and applies the function /// 'folder' to it and the first element of the list. Then, it feeds this result into the /// function 'folder' along with the second element, and so on. It returns the final result. /// /// State type /// Enumerable item type /// Enumerable to fold /// Initial state /// Fold function /// Predicate function /// Aggregate value [Pure] public static S foldUntil(IEnumerable list, S state, Func folder, Func predstate) { foreach (var item in list) { if (predstate(state)) { return state; } state = folder(state, item); } return state; } /// /// Applies a function 'folder' to each element of the collection (from last element to first) /// whilst the predicate function returns False for the item being processed, threading an /// aggregate state through the computation. The fold function takes the state argument, and /// applies the function 'folder' to it and the first element of the list. Then, it feeds this /// result into the function 'folder' along with the second element, and so on. It returns the /// final result. /// /// State type /// Enumerable item type /// Enumerable to fold /// Initial state /// Fold function /// Predicate function /// Aggregate value [Pure] public static S foldBackUntil(IEnumerable list, S state, Func folder, Func preditem) => foldUntil(rev(list), state, folder, preditem: preditem); /// /// Applies a function 'folder' to each element of the collection (from last element to first), /// threading an accumulator argument through the computation (and whilst the predicate function /// returns False when passed the aggregate state). The fold function takes the state argument, /// and applies the function 'folder' to it and the first element of the list. Then, it feeds /// this result into the function 'folder' along with the second element, and so on. It returns /// the final result. /// /// State type /// Enumerable item type /// Enumerable to fold /// Initial state /// Fold function /// Predicate function /// Aggregate value [Pure] public static S foldBackUntil(IEnumerable list, S state, Func folder, Func predstate) => foldUntil(rev(list), state, folder, predstate: predstate); /// /// Applies a function to each element of the collection (from first element to last), threading /// an accumulator argument through the computation. This function first applies the function /// to the first two elements of the list. Then, it passes this result into the function along /// with the third element and so on. Finally, it returns the final result. /// /// The enumerable must contain at least one item or an excpetion will be thrown /// Bound item type /// Enumerable to reduce /// Reduce function /// Aggregate value [Pure] public static A reduce(IEnumerable list, Func reducer) => list.Match( () => failwith("Input list was empty"), (x, xs) => fold(xs, x, reducer)); /// /// Applies a function to each element of the collection (from first element to last), threading /// an accumulator argument through the computation. This function first applies the function /// to the first two elements of the list. Then, it passes this result into the function along /// with the third element and so on. Finally, it returns the final result. /// /// The enumerable must contain at least one item or None will be returned /// Bound item type /// Enumerable to reduce /// Reduce function /// Optional aggregate value [Pure] public static Option reduceOrNone(IEnumerable list, Func reducer) => list.Match( () => None, (x, xs) => Some(fold(xs, x, reducer))); /// /// Applies a function to each element of the collection, threading an accumulator argument /// through the computation. This function first applies the function to the first two /// elements of the list. Then, it passes this result into the function along with the third /// element and so on. Finally, it returns the final result. /// /// The enumerable must contain at least one item or an excpetion will be thrown /// Bound item type /// Enumerable to reduce /// Reduce function /// Aggregate value [Pure] public static A reduceBack(IEnumerable list, Func reducer) => list.Match( () => failwith("Input list was empty"), (x, xs) => foldBack(xs, x, reducer)); /// /// Applies a function to each element of the collection, threading an accumulator argument /// through the computation. This function first applies the function to the first two /// elements of the list. Then, it passes this result into the function along with the third /// element and so on. Finally, it returns the final result. /// /// The enumerable must contain at least one item or None will be returned /// Bound item type /// Enumerable to reduce /// Reduce function /// Optional aggregate value [Pure] public static Option reduceBackOrNone(IEnumerable list, Func reducer) => list.Match( () => None, (x, xs) => Some(foldBack(xs, x, reducer))); /// /// Applies a function to each element of the collection, threading an accumulator argument /// through the computation. This function takes the state argument, and applies the function /// to it and the first element of the list. Then, it passes this result into the function /// along with the second element, and so on. Finally, it returns the list of intermediate /// results and the final result. /// /// State type /// Enumerable item type /// Enumerable to fold /// Initial state /// Folding function /// Aggregate state [Pure] public static IEnumerable scan(IEnumerable list, S state, Func folder) { yield return state; foreach (var item in list) { state = folder(state, item); yield return state; } } /// /// Applies a function to each element of the collection (from last element to first), /// threading an accumulator argument through the computation. This function takes the state /// argument, and applies the function to it and the first element of the list. Then, it /// passes this result into the function along with the second element, and so on. Finally, /// it returns the list of intermediate results and the final result. /// /// State type /// Enumerable item type /// Enumerable to fold /// Initial state /// Folding function /// Aggregate state [Pure] public static IEnumerable scanBack(IEnumerable list, S state, Func folder) => scan(rev(list), state, folder); /// /// Returns Some(x) for the first item in the list that matches the predicate /// provided, None otherwise. /// /// Enumerable item type /// Enumerable to search /// Predicate /// Some(x) for the first item in the list that matches the predicate /// provided, None otherwise. [Pure] public static Option find(IEnumerable list, Func pred) { foreach (var item in list) { if (pred(item)) return Some(item); } return None; } /// /// Returns [x] for the first item in the list that matches the predicate /// provided, [] otherwise. /// /// Enumerable item type /// Enumerable to search /// Predicate /// [x] for the first item in the list that matches the predicate /// provided, [] otherwise. [Pure] public static IEnumerable findSeq(IEnumerable list, Func pred) { foreach (var item in list) { if (pred(item)) { yield return item; break; } } } /// /// Convert any enumerable into an immutable Lst T /// /// Enumerable item type /// Enumerable to convert /// Lst of T [Pure] public static Lst freeze(IEnumerable list) => toList(list); /// /// Joins two enumerables together either into a single enumerable /// using the join function provided /// /// First list to join /// Second list to join /// Join function /// Joined enumerable [Pure] public static IEnumerable zip(IEnumerable list, IEnumerable other, Func zipper) => list.Zip(other, zipper); /// /// Joins two enumerables together either into an enumerables of tuples /// /// First list to join /// Second list to join /// Join function /// Joined enumerable of tuples [Pure] public static IEnumerable<(T First, U Second)> zip(IEnumerable list, IEnumerable other) => list.Zip(other, (t, u) => (t, u)); /// /// Returns the number of items in the enumerable /// /// Enumerable item type /// Enumerable to count /// The number of items in the enumerable [Pure] public static int length(IEnumerable list) => list.Count(); /// /// Invokes an action for each item in the enumerable in order /// /// Enumerable item type /// Enumerable to iterate /// Action to invoke with each item /// Unit public static Unit iter(IEnumerable list, Action action) { foreach (var item in list) { action(item); } return unit; } /// /// Invokes an action for each item in the enumerable in order and supplies /// a running index value. /// /// Enumerable item type /// Enumerable to iterate /// Action to invoke with each item /// Unit public static Unit iter(IEnumerable list, Action action) { var i = 0; foreach (var item in list) { action(i++, item); } return unit; } /// /// Iterate each item in the enumerable in order (consume items) /// /// Enumerable item type /// Enumerable to consume /// Unit public static Unit consume(IEnumerable list) { foreach (var _ in list) { } return unit; } /// /// Returns true if all items in the enumerable match a predicate (Any in LINQ) /// /// Enumerable item type /// Enumerable to test /// Predicate /// True if all items in the enumerable match the predicate [Pure] public static bool forall(IEnumerable list, Func pred) => list.All(pred); /// /// Return a new enumerable with all duplicate values removed /// /// Enumerable item type /// Enumerable /// A new enumerable with all duplicate values removed [Pure] public static IEnumerable distinct(IEnumerable list) => list.Distinct(); /// /// Return a new enumerable with all duplicate values removed /// /// Enumerable item type /// Enumerable /// A new enumerable with all duplicate values removed [Pure] public static IEnumerable distinct(IEnumerable list) where EQ : Eq => list.Distinct(new EqCompare(static (x, y) => EQ.Equals(x, y), static x => EQ.GetHashCode(x))); /// /// Return a new enumerable with all duplicate values removed /// /// Enumerable item type /// Enumerable /// A new enumerable with all duplicate values removed [Pure] public static IEnumerable distinct(IEnumerable list, Func keySelector, Option> compare = default) => list.Distinct(new EqCompare( (a, b) => compare.IfNone(EqDefault.Equals)(keySelector(a), keySelector(b)), a => keySelector(a)?.GetHashCode() ?? 0)); /// /// Returns a new enumerable with the first 'count' items from the enumerable provided /// /// Enumerable item type /// Enumerable /// Number of items to take /// A new enumerable with the first 'count' items from the enumerable provided [Pure] public static IEnumerable take(IEnumerable list, int count) => list.Take(count); /// /// Iterate the list, yielding items if they match the predicate provided, and stopping /// as soon as one doesn't /// /// Enumerable item type /// Enumerable /// Number of items to take /// A new enumerable with the first items that match the predicate [Pure] public static IEnumerable takeWhile(IEnumerable list, Func pred) => list.TakeWhile(pred); /// /// Iterate the list, yielding items if they match the predicate provided, and stopping /// as soon as one doesn't. An index value is also provided to the predicate function. /// /// Enumerable item type /// Enumerable /// Number of items to take /// A new enumerable with the first items that match the predicate [Pure] public static IEnumerable takeWhile(IEnumerable list, Func pred) => list.TakeWhile(pred); /// /// Generate a new list from an intial state value and an 'unfolding' function. /// The unfold function generates the items in the resulting list until None is returned. /// /// State type /// Initial state /// Unfold function /// Unfolded enumerable [Pure] public static IEnumerable unfold(S state, Func> unfolder) { while (true) { yield return state; var res = unfolder(state); if (res.IsNone) { yield break; } else { state = res.Value!; } } } /// /// Generate a new list from an intial state value and an 'unfolding' function. An aggregate /// state value is threaded through separately to the yielded value. /// The unfold function generates the items in the resulting list until None is returned. /// /// Bound value of resulting enumerable /// State type /// Initial state /// Unfold function /// Unfolded enumerable [Pure] public static IEnumerable unfold(S state, Func> unfolder) { while (true) { var res = unfolder(state); if (res.IsNone) { yield break; } else { state = res.Value.Item2; yield return res.Value.Item1; } } } /// /// Generate a new list from an intial state value and an 'unfolding' function. An aggregate /// state value is threaded through separately to the yielded value. /// The unfold function generates the items in the resulting list until None is returned. /// /// Bound value of resulting enumerable /// State type /// State type /// Initial state /// Unfold function /// Unfolded enumerable [Pure] public static IEnumerable unfold((S1, S2) state, Func> unfolder) { while (true) { var res = unfolder(state.Item1, state.Item2); if (res.IsNone) { yield break; } else { state = (res.Value.Item2, res.Value.Item3); yield return res.Value.Item1; } } } /// /// Generate a new list from an intial state value and an 'unfolding' function. An aggregate /// state value is threaded through separately to the yielded value. /// The unfold function generates the items in the resulting list until None is returned. /// /// Bound value of resulting enumerable /// State type /// State type /// State type /// Initial state /// Unfold function /// Unfolded enumerable [Pure] public static IEnumerable unfold((S1, S2, S3) state, Func> unfolder) { while (true) { var res = unfolder(state.Item1, state.Item2, state.Item3); if (res.IsNone) { yield break; } else { state = (res.Value.Item2, res.Value.Item3, res.Value.Item4); yield return res.Value.Item1; } } } /// /// Generate a new list from an intial state value and an 'unfolding' function. An aggregate /// state value is threaded through separately to the yielded value. /// The unfold function generates the items in the resulting list until None is returned. /// /// Bound value of resulting enumerable /// State type /// State type /// State type /// State type /// Initial state /// Unfold function /// Unfolded enumerable [Pure] public static IEnumerable unfold((S1, S2, S3, S4) state, Func> unfolder) { while (true) { var res = unfolder(state.Item1, state.Item2, state.Item3, state.Item4); if (res.IsNone) { yield break; } else { state = (res.Value.Item2, res.Value.Item3, res.Value.Item4, res.Value.Item5); yield return res.Value.Item1; } } } /// /// Returns true if any item in the enumerable matches the predicate provided /// /// Enumerable item type /// Enumerable to test /// Predicate /// True if any item in the enumerable matches the predicate provided [Pure] public static bool exists(IEnumerable list, Func pred) { foreach (var item in list) { if (pred(item)) return true; } return false; } [EditorBrowsable(EditorBrowsableState.Never)] [Obsolete("headSafe has been deprecated, please use headOrNone")] public static Option headSafe(IEnumerable list) => (from x in list select Some(x)) .DefaultIfEmpty(None) .FirstOrDefault(); /// /// The tails function returns all final segments of the argument, longest first. For example, /// i.e. tails(['a','b','c']) == [['a','b','c'], ['b','c'], ['c'],[]] /// /// List item type /// List /// Enumerable of Enumerables of T [Pure] public static IEnumerable> tails(IEnumerable self) { var lst = new List(self); for (var skip = 0; skip < lst.Count; skip++) { yield return lst.Skip(skip); } yield return Enumerable.Empty(); } /// /// Span, applied to a predicate 'pred' and a list, returns a tuple where first element is /// longest prefix (possibly empty) of elements that satisfy 'pred' and second element is the /// remainder of the list: /// /// /// List.span(List(1,2,3,4,1,2,3,4), x => x 〈 3) == (List(1,2),List(3,4,1,2,3,4)) /// /// /// List.span(List(1,2,3), x => x 〈 9) == (List(1,2,3),List()) /// /// /// List.span(List(1,2,3), x => x 〈 0) == (List(),List(1,2,3)) /// /// List element type /// List /// Predicate /// Split list [Pure] public static (IEnumerable Initial, IEnumerable Remainder) span(IEnumerable self, Func pred) { var iter = self.GetEnumerator(); var diposed = false; IEnumerable first(IEnumerator items) { while (items.MoveNext()) { if (pred(items.Current)) { yield return items.Current; } else { yield break; } } items.Dispose(); diposed = true; } IEnumerable second(IEnumerator items) { if (diposed) yield break; while (items.MoveNext()) { yield return items.Current; } items.Dispose(); } return (first(iter), second(iter)); } } class EqCompare : IEqualityComparer { readonly Func compare; readonly Option> hashCode = None; public EqCompare(Func compare) => this.compare = compare; public EqCompare(Func compare, Func hashCode) { this.compare = compare; this.hashCode = hashCode; } [Pure] public bool Equals(T? x, T? y) => isnull(x) && isnull(y) || (!isnull(x) && !isnull(y) && compare(x!, y!)); [Pure] public int GetHashCode(T obj) => hashCode.Match( f => isnull(obj) ? 0 : f(obj), () => 0); } ================================================ FILE: LanguageExt.Core/Immutable Collections/List/Lst.cs ================================================ using System; using System.Linq; using System.Collections; using System.Collections.Generic; using System.Diagnostics.Contracts; using System.Numerics; using static LanguageExt.Prelude; using LanguageExt.ClassInstances; using LanguageExt.Traits; using System.Runtime.CompilerServices; namespace LanguageExt; /// /// Immutable list /// /// Value type [Serializable] [CollectionBuilder(typeof(List), nameof(List.createRange))] public readonly struct Lst : IComparable>, IComparable, IReadOnlyList, IEquatable>, IComparisonOperators, Lst, bool>, IAdditionOperators, Lst, Lst>, ISubtractionOperators, Lst, Lst>, IAdditiveIdentity, Lst>, Monoid>, K { /// /// Empty list /// public static Lst Empty { get; } = new (System.Array.Empty().AsSpan()); readonly LstInternal? value; internal LstInternal Value { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => value ?? LstInternal.Empty; } /// /// Ctor /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public Lst(IEnumerable initial) => value = new LstInternal(initial); /// /// Ctor /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public Lst(ReadOnlySpan initial) => value = new LstInternal(initial); /// /// Ctor /// [MethodImpl(MethodImplOptions.AggressiveInlining)] Lst(LstInternal initial) => value = initial; /// /// Ctor /// [MethodImpl(MethodImplOptions.AggressiveInlining)] internal Lst(ListItem root) => value = new LstInternal(root); ListItem Root { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => Value.Root; } [Pure] public bool IsEmpty { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => Count == 0; } /// /// Reference version for use in pattern-matching /// /// /// /// Empty collection = null /// Singleton collection = A /// More = (A, Seq〈A〉) -- head and tail /// /// Example: /// /// var res = list.Case switch /// { /// /// A value => ..., /// (var x, var xs) => ..., /// _ => ... /// } /// /// [Pure] public object? Case => IsEmpty ? null : Count == 1 ? this[0] : toSeq(this).Case; /// /// Head lens /// [Pure] public static Lens, A> head { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => Lens, A>.New( Get: la => la.Count == 0 ? throw new IndexOutOfRangeException() : la[0], Set: a => la => la.Count == 0 ? throw new IndexOutOfRangeException() : la.SetItem(0, a)); } /// /// Head or none lens /// [Pure] public static Lens, Option> headOrNone { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => Lens, Option>.New( Get: la => la.Count == 0 ? None : Some(la[0]), Set: a => la => la.Count == 0 || a.IsNone ? la : la.SetItem(0, a.Value!)); } /// /// Tail lens /// [Pure] public static Lens, A> tail { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => Lens, A>.New( Get: la => la.Count == 0 ? throw new IndexOutOfRangeException() : la[^1], Set: a => la => la.Count == 0 ? throw new IndexOutOfRangeException() : la.SetItem(la.Count - 1, a)); } /// /// Tail or none lens /// [Pure] public static Lens, Option> tailOrNone { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => Lens, Option>.New( Get: la => la.Count == 0 ? None : Some(la[la.Count - 1]), Set: a => la => la.Count == 0 || a.IsNone ? la : la.SetItem(la.Count - 1, a.Value!)); } /// /// Item at index lens /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Lens, A> item(int index) => Lens, A>.New( Get: la => la.Count == 0 ? throw new IndexOutOfRangeException() : la[index], Set: a => la => la.Count == 0 ? throw new IndexOutOfRangeException() : la.SetItem(index, a) ); /// /// Item or none at index lens /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Lens, Option> itemOrNone(int index) => Lens, Option>.New( Get: la => la.Count < index - 1 ? None : Some(la[index]), Set: a => la => la.Count < index - 1 || a.IsSome ? la : la.SetItem(index, a.Value!) ); /// /// Lens map /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Lens, Lst> map(Lens lens) => Lens, Lst>.New( Get: la => la.Map(lens.Get), Set: lb => la => la.Zip(lb).Map(ab => lens.Set(ab.Item2, ab.Item1)).ToLst() ); /// /// Index accessor /// [Pure] public A this[Index index] { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { if (index.Value < 0 || index.Value >= Root.Count) throw new IndexOutOfRangeException(); return ListModule.GetItem(Root, index.GetOffset(Count)); } } /// /// Safe index accessor /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Option At(int index) { if (index < 0 || index >= Root.Count) return default; return ListModule.GetItem(Root, index); } /// /// Number of items in the list /// [Pure] public int Count { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => Root.Count; } [Pure] int IReadOnlyCollection.Count { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => Count; } /* /// /// Stream as an enumerable /// [Pure] public StreamT AsStream() where M : Monad => StreamT.Lift(this); */ [Pure] A IReadOnlyList.this[int index] { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { if (index < 0 || index >= Root.Count) throw new IndexOutOfRangeException(); return ListModule.GetItem(Root, index); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] Lst Wrap(LstInternal list) => new (list); [MethodImpl(MethodImplOptions.AggressiveInlining)] static Lst Wrap(LstInternal list) => new (list); /// /// Find if a value is in the collection /// /// Value to test /// True if collection contains value [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Contains(A value) => Value.AsIterable().Find(a => EqDefault.Equals(a, value)).IsSome; /// /// Contains with provided Eq class instance /// /// Eq class instance /// Value to test /// True if collection contains value [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Contains(A value) where EqA : Eq => Value.AsIterable().Find(a => EqA.Equals(a, value)).IsSome; /// /// Add an item to the end of the list /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Lst Add(A value) => Wrap(Value.Add(value)); /// /// Add a range of items to the end of the list /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Lst AddRange(IEnumerable items) => Wrap(Value.AddRange(items)); /// /// Clear the list /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Lst Clear() => Empty; /// /// Get enumerator /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public ListEnumerator GetEnumerator() => new (Root, false, 0); /// /// Find the index of an item /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public int IndexOf(A item, int index = 0, int count = -1, IEqualityComparer? equalityComparer = null) => Value.IndexOf(item, index, count, equalityComparer); /// /// Insert value at specified index /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Lst Insert(int index, A value) => Wrap(Value.Insert(index, value)); /// /// Insert range of values at specified index /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Lst InsertRange(int index, IEnumerable items) => Wrap(Value.InsertRange(index, items)); /// /// Find the last index of an item in the list /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public int LastIndexOf(A item, int index = 0, int count = -1, IEqualityComparer? equalityComparer = null) => Value.LastIndexOf(item, index, count, equalityComparer); /// /// Remove all items that match the value from the list /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Lst Remove(A value) => Wrap(Value.Remove(value)); /// /// Remove all items that match the value from the list /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Lst Remove(A value, IEqualityComparer equalityComparer) => Wrap(Value.Remove(value, equalityComparer)); /// /// Remove all items that match a predicate /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Lst RemoveAll(Func pred) => Wrap(Value.RemoveAll(pred)); /// /// Remove item at location /// /// /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Lst RemoveAt(int index) => Wrap(Value.RemoveAt(index)); /// /// Remove a range of items /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Lst RemoveRange(int index, int count) => Wrap(Value.RemoveRange(index, count)); /// /// Set an item at the specified index /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Lst SetItem(int index, A value) => Wrap(Value.SetItem(index, value)); /// /// Returns an enumerable range from the collection. This is the fastest way of /// iterating sub-ranges of the collection. /// /// Index into the collection /// Number of items to find /// IEnumerable of items [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Iterable FindRange(int index, int count) => Value.FindRange(index, count); [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] IEnumerator IEnumerable.GetEnumerator() => new ListEnumerator(Root, false, 0); [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] IEnumerator IEnumerable.GetEnumerator() => new ListEnumerator(Root, false, 0); [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Seq ToSeq() => toSeq(this); /// /// Format the collection as `[a, b, c, ...]` /// The ellipsis is used for collections over 50 items /// To get a formatted string with all the items, use `ToFullString` /// or `ToFullArrayString`. /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public override string ToString() => CollectionFormat.ToShortArrayString(this, Count); /// /// Format the collection as `a, b, c, ...` /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public string ToFullString(string separator = ", ") => CollectionFormat.ToFullString(this, separator); /// /// Format the collection as `[a, b, c, ...]` /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public string ToFullArrayString(string separator = ", ") => CollectionFormat.ToFullArrayString(this, separator); [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Iterable AsIterable() => Iterable.createRange(this); [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Iterable Skip(int amount) => Value.Skip(amount); /// /// Reverse the order of the items in the list /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Lst Reverse() => Wrap(Value.Reverse()); /// /// Impure iteration of the bound values in the structure /// /// /// Returns the original unmodified structure /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public Lst Do(Action f) { this.Iter(f); return this; } /// /// Map /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Lst Map(Func map) => Wrap(Value.Map(map)); /// /// Map each element of a structure to an action, evaluate these actions from /// left to right, and collect the results. /// /// /// Traversable structure /// Applicative functor trait /// Bound value (output) [Pure] public K> Traverse(Func> f) where F : Applicative => F.Map(x => x.As(), Traversable.traverse(f, this)); /// /// Map each element of a structure to an action, evaluate these actions from /// left to right, and collect the results. /// /// /// Traversable structure /// Monad trait /// Bound value (output) [Pure] public K> TraverseM(Func> f) where M : Monad => M.Map(x => x.As(), Traversable.traverseM(f, this)); /// /// Filter /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Lst Filter(Func pred) => Wrap(Value.Filter(pred)); [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Lst operator +(Lst lhs, A rhs) => lhs.Add(rhs); [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Lst operator +(A lhs, Lst rhs) => lhs.Cons(rhs); [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Lst operator +(Lst lhs, Lst rhs) => lhs.Combine(rhs); /// /// Choice operator /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Lst operator |(Lst x, K y) => x.Choose(y).As(); /// /// Choice operator /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Lst operator |(K x, Lst y) => x.Choose(y).As(); [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Lst Combine(Lst rhs) => new (Value.Combine(rhs.Value)); [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Lst operator -(Lst lhs, Lst rhs) => lhs.Subtract(rhs); [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Lst Subtract(Lst rhs) => Wrap(Value.Subtract(rhs.Value)); [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public override bool Equals(object? obj) => obj switch { Lst s => Equals(s), IEnumerable e => Equals(e.AsIterable().ToLst()), _ => false }; /// /// Get the hash code /// Lazily (and once only) calculates the hash from the elements in the list /// Empty list hash == 0 /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public override int GetHashCode() => Value.GetHashCode(); [Pure] public int CompareTo(object? obj) => obj switch { Lst s => CompareTo(s), IEnumerable e => CompareTo(e.AsIterable().ToLst()), _ => 1 }; [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(Lst other) => Value.Equals(other.Value); [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(Lst lhs, Lst rhs) => lhs.Value.Equals(rhs.Value); [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(Lst lhs, Lst rhs) => !(lhs == rhs); [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator <(Lst lhs, Lst rhs) => lhs.CompareTo(rhs) < 0; [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator <=(Lst lhs, Lst rhs) => lhs.CompareTo(rhs) <= 0; [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator >(Lst lhs, Lst rhs) => lhs.CompareTo(rhs) > 0; [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator >=(Lst lhs, Lst rhs) => lhs.CompareTo(rhs) >= 0; [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Arr ToArr() => toArray(this); [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] internal A[] ToArray() => Value.ToArray(); [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public int CompareTo(Lst other) => Value.CompareTo(other.Value); /// /// Implicit conversion from an untyped empty list /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator Lst(SeqEmpty _) => Empty; public static Lst AdditiveIdentity => Empty; } ================================================ FILE: LanguageExt.Core/Immutable Collections/List/Operators/Lst.Operators.cs ================================================ using LanguageExt.Traits; namespace LanguageExt; public static partial class SeqExtensions { extension(K _) { /// /// Downcast operator /// public static Lst operator +(K ma) => (Lst)ma; public static Lst operator >> (K ma, Lower lower) => (Lst)ma; } } ================================================ FILE: LanguageExt.Core/Immutable Collections/List/Prelude/Lst.Prelude.mapapply.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public static partial class Prelude { /// /// Functor map operation /// /// /// Unwraps the value within the functor, passes it to the map function `f` provided, and /// then takes the mapped value and wraps it back up into a new functor. /// /// Functor to map /// Mapping function /// Mapped functor public static Lst map(Func f, K ma) => Functor.map(f, ma).As(); /// /// Applicative action: runs the first applicative, ignores the result, and returns the second applicative /// public static Lst action(K ma, K mb) => Applicative.action(ma, mb).As(); /// /// Applicative functor apply operation /// /// /// Unwraps the value within the `ma` applicative-functor, passes it to the unwrapped function(s) within `mf`, and /// then takes the resulting value and wraps it back up into a new applicative-functor. /// /// Value(s) applicative functor /// Mapping function(s) /// Mapped applicative functor public static Lst apply(K> mf, K ma) => Applicative.apply(mf, ma).As(); } ================================================ FILE: LanguageExt.Core/Immutable Collections/List/Trait/Lst.TraitImpl.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; public class Lst : Monad, MonoidK, Alternative, Traversable { static K Monad.Recur(A value, Func>> f) => List.createRange(Monad.enumerableRecur(value, x =>f(x).As().AsEnumerable())); static K Monad.Bind(K ma, Func> f) { return new Lst(go()); IEnumerable go() { foreach (var x in ma.As()) { foreach (var y in f(x).As()) { yield return y; } } } } static K Functor.Map(Func f, K ma) { return new Lst(go()); IEnumerable go() { foreach (var x in ma.As()) { yield return f(x); } } } static K Applicative.Pure(A value) => List.singleton(value); static K Applicative.Apply(K> mf, K ma) { return new Lst(go()); IEnumerable go() { foreach (var f in mf.As()) { foreach (var a in ma.As()) { yield return f(a); } } } } static K Applicative.Apply(K> mf, Memo ma) { return new Lst(go()); IEnumerable go() { foreach (var f in mf.As()) { foreach (var a in ma.Value.As()) { yield return f(a); } } } } static K MonoidK.Empty() => Lst.Empty; static K Alternative.Empty() => Lst.Empty; static K SemigroupK.Combine(K ma, K mb) => ma.As() + mb.As(); static K Choice.Choose(K ma, K mb) => ma.IsEmpty ? mb : ma; static K Choice.Choose(K ma, Memo mb) => ma.IsEmpty ? mb.Value : ma; static K> Traversable.Traverse(Func> f, K ta) { return Foldable.fold(add, F.Pure(Lst.Empty), ta) .Map(bs => bs.Kind()); Func>, K>> add(A value) => state => Applicative.lift((bs, b) => bs.Add(b), state, f(value)); } static K> Traversable.TraverseM(Func> f, K ta) { return Foldable.fold(add, F.Pure(Lst.Empty), ta) .Map(bs => bs.Kind()); Func>, K>> add(A value) => state => state.Bind( bs => f(value).Bind( b => F.Pure(bs.Add(b)))); } static S Foldable.FoldWhile( Func> f, Func<(S State, A Value), bool> predicate, S state, K ta) { foreach (var x in ta.As()) { if (!predicate((state, x))) return state; state = f(x)(state); } return state; } static S Foldable.FoldBackWhile( Func> f, Func<(S State, A Value), bool> predicate, S state, K ta) { foreach (var x in ta.As().Reverse()) { if (!predicate((state, x))) return state; state = f(state)(x); } return state; } static int Foldable.Count(K ta) => ta.As().Count; static bool Foldable.IsEmpty(K ta) => ta.As().IsEmpty; static Option Foldable.At(K ta, Index index) { var list = ta.As().Value; return index.Value >= 0 && index.Value < list.Count ? Some(list[index]) : Option.None; } static Arr Foldable.ToArr(K ta) => new(ta.As()); static Lst Foldable.ToLst(K ta) => ta.As(); static Iterable Foldable.ToIterable(K ta) => Iterable.createRange (ta.As()); static Seq Foldable.ToSeq(K ta) => new (ta.As()); static Fold Foldable.FoldStep(K ta, S initialState) { // ReSharper disable once GenericEnumeratorNotDisposed var iter = ta.As().GetEnumerator(); return go(initialState); Fold go(S state) => iter.MoveNext() ? Fold.Loop(state, iter.Current, go) : Fold.Done(state); } static Fold Foldable.FoldStepBack(K ta, S initialState) { // ReSharper disable once GenericEnumeratorNotDisposed var iter = ta.As().Reverse().GetEnumerator(); return go(initialState); Fold go(S state) => iter.MoveNext() ? Fold.Loop(state, iter.Current, go) : Fold.Done(state); } } ================================================ FILE: LanguageExt.Core/Immutable Collections/Map/Map.EnumeratorK.cs ================================================ using System.Collections; using System.Collections.Generic; namespace LanguageExt; public struct MapKeyEnumerator : IEnumerator { struct NewStack : New[]> { public MapItem[] New() => new MapItem[32]; } int stackDepth; MapItem[] stack; readonly MapItem map; int left; readonly bool rev; readonly int start; internal MapKeyEnumerator(MapItem root, bool rev, int start) { this.rev = rev; this.start = start; map = root; stack = Pool[]>.Pop(); stackDepth = default; left = default; NodeCurrent = default!; Reset(); } private MapItem NodeCurrent { get; set; } public readonly K Current => NodeCurrent.KeyValue.Key; readonly object IEnumerator.Current => NodeCurrent.KeyValue.Key!; public void Dispose() { if (stack is not null) { Pool[]>.Push(stack); stack = default!; } } private MapItem Next(MapItem node) => rev ? node.Left : node.Right; private MapItem Prev(MapItem node) => rev ? node.Right : node.Left; private void Push(MapItem node) { while (!node.IsEmpty) { stack[stackDepth] = node; stackDepth++; node = Prev(node); } } public bool MoveNext() { if (left > 0 && stackDepth > 0) { stackDepth--; NodeCurrent = stack[stackDepth]; Push(Next(NodeCurrent)); left--; return true; } NodeCurrent = null!; return false; } public void Reset() { var skip = rev ? map.Count - start - 1 : start; stackDepth = 0; NodeCurrent = map; left = map.Count; while (!NodeCurrent.IsEmpty && skip != Prev(NodeCurrent).Count) { if (skip < Prev(NodeCurrent).Count) { stack[stackDepth] = NodeCurrent; stackDepth++; NodeCurrent = Prev(NodeCurrent); } else { skip -= Prev(NodeCurrent).Count + 1; NodeCurrent = Next(NodeCurrent); } } if (!NodeCurrent.IsEmpty) { stack[stackDepth] = NodeCurrent; stackDepth++; } } } ================================================ FILE: LanguageExt.Core/Immutable Collections/Map/Map.EnumeratorKV.cs ================================================ using System.Collections; using System.Collections.Generic; namespace LanguageExt; public struct MapEnumerator : IEnumerator<(K Key, V Value)> { internal struct NewStack : New[]> { public MapItem[] New() => new MapItem[32]; } int stackDepth; MapItem[] stack; readonly MapItem map; int left; readonly bool rev; readonly int start; internal MapEnumerator(MapItem root, bool rev, int start) { this.rev = rev; this.start = start; map = root; stack = Pool[]>.Pop(); stackDepth = default; left = default; NodeCurrent = default!; Reset(); } private MapItem NodeCurrent { get; set; } public readonly (K Key, V Value) Current => NodeCurrent.KeyValue; readonly object IEnumerator.Current => NodeCurrent.KeyValue; public void Dispose() { if (stack is not null) { Pool[]>.Push(stack); stack = default!; } } private MapItem Next(MapItem node) => rev ? node.Left : node.Right; private MapItem Prev(MapItem node) => rev ? node.Right : node.Left; private void Push(MapItem node) { while (!node.IsEmpty) { stack[stackDepth] = node; stackDepth++; node = Prev(node); } } public bool MoveNext() { if (left > 0 && stackDepth > 0) { stackDepth--; NodeCurrent = stack[stackDepth]; Push(Next(NodeCurrent)); left--; return true; } NodeCurrent = default!; return false; } public void Reset() { var skip = rev ? map.Count - start - 1 : start; stackDepth = 0; NodeCurrent = map; left = map.Count; while (!NodeCurrent.IsEmpty && skip != Prev(NodeCurrent).Count) { if (skip < Prev(NodeCurrent).Count) { stack[stackDepth] = NodeCurrent; stackDepth++; NodeCurrent = Prev(NodeCurrent); } else { skip -= Prev(NodeCurrent).Count + 1; NodeCurrent = Next(NodeCurrent); } } if (!NodeCurrent.IsEmpty) { stack[stackDepth] = NodeCurrent; stackDepth++; } } } ================================================ FILE: LanguageExt.Core/Immutable Collections/Map/Map.EnumeratorV.cs ================================================ using System.Collections; using System.Collections.Generic; namespace LanguageExt; public struct MapValueEnumerator : IEnumerator { internal struct NewStack : New[]> { public MapItem[] New() => new MapItem[32]; } int stackDepth; MapItem[] stack; readonly MapItem map; int left; readonly bool rev; readonly int start; internal MapValueEnumerator(MapItem root, bool rev, int start) { this.rev = rev; this.start = start; map = root; stack = Pool[]>.Pop(); stackDepth = default; left = default; NodeCurrent = default!; Reset(); } private MapItem NodeCurrent { get; set; } public readonly V Current => NodeCurrent.KeyValue.Value; readonly object IEnumerator.Current => NodeCurrent.KeyValue.Value!; public void Dispose() { if (stack is not null) { Pool[]>.Push(stack); stack = default!; } } private MapItem Next(MapItem node) => rev ? node.Left : node.Right; private MapItem Prev(MapItem node) => rev ? node.Right : node.Left; private void Push(MapItem node) { while (!node.IsEmpty) { stack[stackDepth] = node; stackDepth++; node = Prev(node); } } public bool MoveNext() { if (left > 0 && stackDepth > 0) { stackDepth--; NodeCurrent = stack[stackDepth]; Push(Next(NodeCurrent)); left--; return true; } NodeCurrent = default!; return false; } public void Reset() { var skip = rev ? map.Count - start - 1 : start; stackDepth = 0; NodeCurrent = map; left = map.Count; while (!NodeCurrent.IsEmpty && skip != Prev(NodeCurrent).Count) { if (skip < Prev(NodeCurrent).Count) { stack[stackDepth] = NodeCurrent; stackDepth++; NodeCurrent = Prev(NodeCurrent); } else { skip -= Prev(NodeCurrent).Count + 1; NodeCurrent = Next(NodeCurrent); } } if (!NodeCurrent.IsEmpty) { stack[stackDepth] = NodeCurrent; stackDepth++; } } } ================================================ FILE: LanguageExt.Core/Immutable Collections/Map/Map.Extensions.MapApply.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public static partial class MapExtensions { /// /// Functor map operation /// /// /// Unwraps the value within the functor, passes it to the map function `f` provided, and /// then takes the mapped value and wraps it back up into a new functor. /// /// Functor to map /// Mapping function /// Mapped functor public static Map Map(this Func f, K, A> ma) => Functor.map(f, ma).As(); /// /// Functor map operation /// /// /// Unwraps the value within the functor, passes it to the map function `f` provided, and /// then takes the mapped value and wraps it back up into a new functor. /// /// Functor to map /// Mapping function /// Mapped functor public static Map Map(this Func f, Map ma) => Functor.map(f, ma).As(); } ================================================ FILE: LanguageExt.Core/Immutable Collections/Map/Map.Extensions.Ord.cs ================================================ #pragma warning disable CS0693 // Type parameter has the same name as the type parameter from outer type using System; using System.Linq; using System.Diagnostics.Contracts; using LanguageExt.Traits; namespace LanguageExt; public static class MapOrdExtensions { /// /// Atomically maps the map to a new map /// /// Mapped items in a new map [Pure] public static Map Map(this Map self, Func mapper) where OrdK : Ord => new (self.AsEnumerable().Select(kv => (kv.Key, mapper(kv.Value)))); /// /// Atomically maps the map to a new map /// /// Mapped items in a new map [Pure] public static Map Map(this Map self, Func mapper) where OrdK : Ord => new (self.AsEnumerable().Select(kv => (kv.Key, mapper(kv.Key, kv.Value)))); /// /// Number of items in the map /// [Pure] public static int Count(this Map self) where OrdK : Ord => self.Count; [Pure] public static int Sum(this Map self) where OrdK : Ord => self.Values.Sum(); } ================================================ FILE: LanguageExt.Core/Immutable Collections/Map/Map.Extensions.cs ================================================ #pragma warning disable CS0693 // Type parameter has the same name as the type parameter from outer type using System; using System.Collections.Generic; using System.Diagnostics.Contracts; using System.Linq; using LanguageExt.Traits; namespace LanguageExt; public static partial class MapExtensions { public static Map As(this K, V> ma) => (Map)ma; /// /// Create an immutable map /// [Pure] public static Map ToMap(this IEnumerable<(K, V)> items) => LanguageExt.Map.createRange(items); /// /// Create an immutable map /// [Pure] public static Map ToMap(this IEnumerable> items) => LanguageExt.Map.createRange(items); /// /// Create an immutable map /// [Pure] public static Map ToMap(this IEnumerable> items) => LanguageExt.Map.createRange(items); /// /// Create an immutable map /// [Pure] public static Map<(K1, K2), V> ToMap(this IEnumerable<(K1, K2, V)> items) => new(items.Select(x => ((x.Item1, x.Item2), x.Item3))); /// /// Create an immutable map /// [Pure] public static Map<(K1, K2, K3), V> ToMap(this IEnumerable<(K1, K2, K3, V)> items) => new(items.Select(x => ((x.Item1, x.Item2, x.Item3), x.Item4))); /// /// Create an immutable map /// [Pure] public static Map<(K1, K2, K3, K4), V> ToMap(this IEnumerable<(K1, K2, K3, K4, V)> items) => new(items.Select(x => ((x.Item1, x.Item2, x.Item3, x.Item4), x.Item5))); /// /// Atomically maps the map to a new map /// /// Mapped items in a new map [Pure] public static Map Map(this Map self, Func mapper) => new (self.AsIterable().Select(kv => (kv.Key, mapper( kv.Value)))); /// /// Atomically maps the map to a new map /// /// Mapped items in a new map [Pure] public static Map Map(this Map self, Func mapper) => new (self.AsIterable().Select(kv => (kv.Key, mapper(kv.Key, kv.Value)))); } ================================================ FILE: LanguageExt.Core/Immutable Collections/Map/Map.Internal.cs ================================================ using System; using System.Collections; using System.Collections.Generic; using System.Linq; using static LanguageExt.Prelude; using System.ComponentModel; using System.Diagnostics.Contracts; using System.Runtime.Serialization; using LanguageExt.Traits; using LanguageExt.ClassInstances; using System.Runtime.CompilerServices; #pragma warning disable CS0693 // Type parameter has the same name as the type parameter from outer type namespace LanguageExt; /// /// Immutable map /// AVL tree implementation /// AVL tree is a self-balancing binary search tree. /// [wikipedia.org/wiki/AVL_tree](http://en.wikipedia.org/wiki/AVL_tree) /// /// Key type /// Value type [Serializable] internal class MapInternal : IEnumerable<(K Key, V Value)> where OrdK : Ord { public static readonly MapInternal Empty = new (MapItem.Empty, false); internal readonly MapItem Root; internal readonly bool Rev; int hashCode; /// /// Ctor /// [MethodImpl(MethodImplOptions.AggressiveInlining)] internal MapInternal() => Root = MapItem.Empty; /// /// Ctor /// [MethodImpl(MethodImplOptions.AggressiveInlining)] internal MapInternal(MapItem root, bool rev) { Root = root; Rev = rev; } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal MapInternal(ReadOnlySpan<(K Key, V Value)> items, MapModuleM.AddOpt option) { var root = MapItem.Empty; foreach (var item in items) { root = MapModuleM.Add(root, item.Key, item.Value, option); } Root = root; } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal MapInternal(IEnumerable<(K Key, V Value)> items, MapModuleM.AddOpt option) { var root = MapItem.Empty; foreach (var item in items) { root = MapModuleM.Add(root, item.Key, item.Value, option); } Root = root; } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal MapInternal(IEnumerable> items, MapModuleM.AddOpt option) { var root = MapItem.Empty; foreach (var item in items) { root = MapModuleM.Add(root, item.Key, item.Value, option); } Root = root; } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal MapInternal(IEnumerable> items, MapModuleM.AddOpt option) { var root = MapItem.Empty; foreach (var item in items) { root = MapModuleM.Add(root, item.Item1, item.Item2, option); } Root = root; } /// /// 'this' accessor /// /// Key /// Optional value [Pure] public V this[K key] { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => Find(key).IfNone(() => failwith("Key doesn't exist in map")); } /// /// Is the map empty /// [Pure] public bool IsEmpty { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => Count == 0; } /// /// Number of items in the map /// [Pure] public int Count { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => Root.Count; } /// /// Alias of Count /// [Pure] public int Length { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => Count; } [Pure] public Option<(K, V)> Min { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => Root.IsEmpty ? None : MapModule.Min(Root); } [Pure] public Option<(K, V)> Max { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => Root.IsEmpty ? None : MapModule.Max(Root); } /// /// Get the hash code of all items in the map /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public override int GetHashCode() => hashCode == 0 ? (hashCode = FNV32.Hash, K, V>, (K, V)>(AsIterable())) : hashCode; /// /// Atomically adds a new item to the map /// /// Null is not allowed for a Key or a Value /// Key /// Value /// Throws ArgumentException if the key already exists /// Throws ArgumentNullException the key or value are null /// New Map with the item added [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public MapInternal Add(K key, V value) { if (isnull(key)) throw new ArgumentNullException(nameof(key)); if (isnull(value)) throw new ArgumentNullException(nameof(value)); return SetRoot(MapModule.Add(Root, key, value)); } /// /// Atomically adds a new item to the map. /// If the key already exists, then the new item is ignored /// /// Null is not allowed for a Key or a Value /// Key /// Value /// Throws ArgumentNullException the key or value are null /// New Map with the item added [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public MapInternal TryAdd(K key, V value) { if (isnull(key)) throw new ArgumentNullException(nameof(key)); return SetRoot(MapModule.TryAdd(Root, key, value)); } /// /// Atomically adds a new item to the map. /// If the key already exists then the Fail handler is called with the unaltered map /// and the value already set for the key, it expects a new map returned. /// /// Null is not allowed for a Key or a Value /// Key /// Value /// Delegate to handle failure, you're given the unaltered map /// and the value already set for the key /// Throws ArgumentNullException the key or value are null /// New Map with the item added [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public MapInternal TryAdd(K key, V value, Func, V, MapInternal> Fail) { if (isnull(key)) throw new ArgumentNullException(nameof(key)); return Find(key, v => Fail(this, v), () => Add(key, value)); } /// /// Atomically adds a range of items to the map. /// /// Null is not allowed for a Key or a Value /// Range of tuples to add /// Throws ArgumentException if any of the keys already exist /// Throws ArgumentNullException the keys or values are null /// New Map with the items added [Pure] public MapInternal AddRange(IEnumerable> range) { if (Count == 0) { return new MapInternal(range, MapModuleM.AddOpt.ThrowOnDuplicate); } var self = Root; foreach (var item in range) { if (isnull(item.Item1)) throw new ArgumentNullException(nameof(item.Item1)); self = MapModule.Add(self, item.Item1, item.Item2); } return SetRoot(self); } /// /// Atomically adds a range of items to the map. /// /// Null is not allowed for a Key or a Value /// Range of tuples to add /// Throws ArgumentException if any of the keys already exist /// Throws ArgumentNullException the keys or values are null /// New Map with the items added [Pure] public MapInternal AddRange(IEnumerable<(K, V)> range) { if (Count == 0) { return new MapInternal(range, MapModuleM.AddOpt.ThrowOnDuplicate); } var self = Root; foreach (var item in range) { if (isnull(item.Item1)) throw new ArgumentNullException(nameof(item.Item1)); self = MapModule.Add(self, item.Item1, item.Item2); } return SetRoot(self); } /// /// Atomically adds a range of items to the map. If any of the keys exist already /// then they're ignored. /// /// Null is not allowed for a Key or a Value /// Range of tuples to add /// Throws ArgumentNullException the keys or values are null /// New Map with the items added [Pure] public MapInternal TryAddRange(IEnumerable> range) { if (Count == 0) { return new MapInternal(range, MapModuleM.AddOpt.TryAdd); } var self = Root; foreach (var item in range) { if (isnull(item.Item1)) throw new ArgumentNullException(nameof(item.Item1)); self = MapModule.TryAdd(self, item.Item1, item.Item2); } return SetRoot(self); } /// /// Atomically adds a range of items to the map. If any of the keys exist already /// then they're ignored. /// /// Null is not allowed for a Key or a Value /// Range of tuples to add /// Throws ArgumentNullException the keys or values are null /// New Map with the items added [Pure] public MapInternal TryAddRange(IEnumerable<(K, V)> range) { if (Count == 0) { return new MapInternal(range, MapModuleM.AddOpt.TryAdd); } var self = Root; foreach (var item in range) { if (isnull(item.Item1)) throw new ArgumentNullException(nameof(item.Item1)); self = MapModule.TryAdd(self, item.Item1, item.Item2); } return SetRoot(self); } /// /// Atomically adds a range of items to the map. If any of the keys exist already /// then they're ignored. /// /// Null is not allowed for a Key or a Value /// Range of KeyValuePairs to add /// Throws ArgumentNullException the keys or values are null /// New Map with the items added [Pure] public MapInternal TryAddRange(IEnumerable> range) { if (Count == 0) { return new MapInternal(range, MapModuleM.AddOpt.TryAdd); } var self = Root; foreach (var item in range) { if (isnull(item.Key)) throw new ArgumentNullException(nameof(item.Key)); self = MapModule.TryAdd(self, item.Key, item.Value); } return SetRoot(self); } /// /// Atomically adds a range of items to the map. If any of the keys exist already /// then they're replaced. /// /// Null is not allowed for a Key or a Value /// Range of tuples to add /// Throws ArgumentNullException the keys or values are null /// New Map with the items added [Pure] public MapInternal AddOrUpdateRange(IEnumerable> range) { if (Count == 0) { return new MapInternal(range, MapModuleM.AddOpt.TryUpdate); } var self = Root; foreach (var item in range) { if (isnull(item.Item1)) throw new ArgumentNullException(nameof(item.Item1)); self = MapModule.AddOrUpdate(self, item.Item1, item.Item2); } return SetRoot(self); } /// /// Atomically adds a range of items to the map. If any of the keys exist already /// then they're replaced. /// /// Null is not allowed for a Key or a Value /// Range of tuples to add /// Throws ArgumentNullException the keys or values are null /// New Map with the items added [Pure] public MapInternal AddOrUpdateRange(IEnumerable<(K, V)> range) { if (Count == 0) { return new MapInternal(range, MapModuleM.AddOpt.TryUpdate); } var self = Root; foreach (var item in range) { if (isnull(item.Item1)) throw new ArgumentNullException(nameof(item.Item1)); self = MapModule.AddOrUpdate(self, item.Item1, item.Item2); } return SetRoot(self); } /// /// Atomically adds a range of items to the map. If any of the keys exist already /// then they're replaced. /// /// Null is not allowed for a Key or a Value /// Range of KeyValuePairs to add /// Throws ArgumentNullException the keys or values are null /// New Map with the items added [Pure] public MapInternal AddOrUpdateRange(IEnumerable> range) { if (Count == 0) { return new MapInternal(range, MapModuleM.AddOpt.TryUpdate); } var self = Root; foreach (var item in range) { if (isnull(item.Key)) throw new ArgumentNullException(nameof(item.Key)); self = MapModule.AddOrUpdate(self, item.Key, item.Value); } return SetRoot(self); } /// /// Atomically removes an item from the map /// If the key doesn't exists, the request is ignored. /// /// Key /// New map with the item removed [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public MapInternal Remove(K key) => isnull(key) ? this : SetRoot(MapModule.Remove(Root, key)); /// /// Retrieve a value from the map by key /// /// Key to find /// Found value [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Option Find(K key) => isnull(key) ? None : MapModule.TryFind(Root, key); /// /// Retrieve a value from the map by key as an enumerable /// /// Key to find /// Found value [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Seq FindSeq(K key) => Find(key).ToSeq(); /// /// Retrieve a value from the map by key and pattern match the /// result. /// /// Key to find /// Found value [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public R Find(K key, Func Some, Func None) => isnull(key) ? None() : match(MapModule.TryFind(Root, key), Some, None); /// /// Retrieve the value from predecessor item to specified key /// /// Key to find /// Found key [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Option<(K, V)> FindPredecessor(K key) => MapModule.TryFindPredecessor(Root, key); /// /// Retrieve the value from exact key, or if not found, the predecessor item /// /// Key to find /// Found key [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Option<(K, V)> FindOrPredecessor(K key) => MapModule.TryFindOrPredecessor(Root, key); /// /// Retrieve the value from next item to specified key /// /// Key to find /// Found key [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Option<(K, V)> FindSuccessor(K key) => MapModule.TryFindSuccessor(Root, key); /// /// Retrieve the value from exact key, or if not found, the next item /// /// Key to find /// Found key [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Option<(K, V)> FindOrSuccessor(K key) => MapModule.TryFindOrSuccessor(Root, key); /// /// Try to find the key in the map, if it doesn't exist, add a new /// item by invoking the delegate provided. /// /// Key to find /// Delegate to get the value /// Updated map and added value [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public (MapInternal Map, V Value) FindOrAdd(K key, Func None) => Find(key).Match( Some: x => (this, x), None: () => { var v = None(); return (Add(key, v), v); }); /// /// Try to find the key in the map, if it doesn't exist, add a new /// item provided. /// /// Key to find /// Delegate to get the value /// Updated map and added value [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public (MapInternal, V Value) FindOrAdd(K key, V value) => Find(key).Match( Some: x => (this, x), None: () => (Add(key, value), value)); /// /// Try to find the key in the map, if it doesn't exist, add a new /// item provided. /// /// Key to find /// Delegate to get the value /// Updated map and added value [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public (MapInternal, Option Value) FindOrMaybeAdd(K key, Func> value) => Find(key).Match( Some: x => (this, Some(x)), None: () => value().Map(v => (Add(key, v), Some(v))) .IfNone((this, None))); /// /// Try to find the key in the map, if it doesn't exist, add a new /// item provided. /// /// Key to find /// Delegate to get the value /// Updated map and added value [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public (MapInternal, Option Value) FindOrMaybeAdd(K key, Option value) => Find(key).Match( Some: x => (this, Some(x)), None: () => value.Map(v => (Add(key, v), Some(v))) .IfNone((this, None))); /// /// Atomically updates an existing item /// /// Null is not allowed for a Key or a Value /// Key /// Value /// Throws ArgumentNullException the key or value are null /// New Map with the item added [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public MapInternal SetItem(K key, V value) { if (isnull(key)) throw new ArgumentNullException(nameof(key)); return SetRoot(MapModule.SetItem(Root, key, value)); } /// /// Retrieve a value from the map by key, map it to a new value, /// put it back. /// /// Key to set /// Throws ArgumentException if the item isn't found /// Throws Exception if Some returns null /// New map with the mapped value [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public MapInternal SetItem(K key, Func Some) => isnull(key) ? this : match(MapModule.TryFind(Root, key), Some: x => SetItem(key, Some(x)), None: () => throw new ArgumentException("Key not found in Map")); /// /// Atomically updates an existing item, unless it doesn't exist, in which case /// it is ignored /// /// Null is not allowed for a Key or a Value /// Key /// Value /// Throws ArgumentNullException the value is null /// New Map with the item added [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public MapInternal TrySetItem(K key, V value) { if (isnull(key)) return this; return SetRoot(MapModule.TrySetItem(Root, key, value)); } /// /// Atomically sets an item by first retrieving it, applying a map, and then putting it back. /// Silently fails if the value doesn't exist /// /// Key to set /// delegate to map the existing value to a new one before setting /// Throws Exception if Some returns null /// Throws ArgumentNullException the key or value are null /// New map with the item set [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public MapInternal TrySetItem(K key, Func Some) => isnull(key) ? this : match(MapModule.TryFind(Root, key), Some: x => SetItem(key, Some(x)), None: () => this); /// /// Atomically sets an item by first retrieving it, applying a map, and then putting it back. /// Calls the None delegate to return a new map if the item can't be found /// /// Null is not allowed for a Key or a Value /// Key /// delegate to map the existing value to a new one before setting /// delegate to return a new map if the item can't be found /// Throws Exception if Some returns null /// Throws Exception if None returns null /// New map with the item set [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public MapInternal TrySetItem(K key, Func Some, Func, Map> None) => isnull(key) ? this : match(MapModule.TryFind(Root, key), Some: x => SetItem(key, Some(x)), None: () => this); /// /// Atomically adds a new item to the map. /// If the key already exists, the new item replaces it. /// /// Null is not allowed for a Key or a Value /// Key /// Value /// Throws ArgumentNullException the key or value are null /// New Map with the item added [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public MapInternal AddOrUpdate(K key, V value) { if (isnull(key)) throw new ArgumentNullException(nameof(key)); return SetRoot(MapModule.AddOrUpdate(Root, key, value)); } /// /// Retrieve a value from the map by key, map it to a new value, /// put it back. If it doesn't exist, add a new one based on None result. /// /// Key to find /// Throws Exception if None returns null /// Throws Exception if Some returns null /// New map with the mapped value [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public MapInternal AddOrUpdate(K key, Func Some, Func None) => isnull(key) ? this : match(MapModule.TryFind(Root, key), Some: x => SetItem(key, Some(x)), None: () => Add(key, None())); /// /// Retrieve a value from the map by key, map it to a new value, /// put it back. If it doesn't exist, add a new one based on None result. /// /// Key to find /// Throws ArgumentNullException if None is null /// Throws Exception if Some returns null /// New map with the mapped value [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public MapInternal AddOrUpdate(K key, Func Some, V None) { if (isnull(None)) throw new ArgumentNullException(nameof(None)); return isnull(key) ? this : match(MapModule.TryFind(Root, key), Some: x => SetItem(key, Some(x)), None: () => Add(key, None)); } /// /// Retrieve a range of values /// /// Range start (inclusive) /// Range to (inclusive) /// Throws ArgumentNullException the keyFrom or keyTo are null /// Range of values [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Iterable FindRange(K keyFrom, K keyTo) { if (isnull(keyFrom)) throw new ArgumentNullException(nameof(keyFrom)); if (isnull(keyTo)) throw new ArgumentNullException(nameof(keyTo)); return OrdK.Compare(keyFrom, keyTo) > 0 ? MapModule.FindRange(Root, keyTo, keyFrom).AsIterable() : MapModule.FindRange(Root, keyFrom, keyTo).AsIterable(); } /// /// Retrieve a range of values /// /// Range start (inclusive) /// Range to (inclusive) /// Throws ArgumentNullException the keyFrom or keyTo are null /// Range of values [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Iterable<(K, V)> FindRangePairs(K keyFrom, K keyTo) { if (isnull(keyFrom)) throw new ArgumentNullException(nameof(keyFrom)); if (isnull(keyTo)) throw new ArgumentNullException(nameof(keyTo)); return OrdK.Compare(keyFrom, keyTo) > 0 ? MapModule.FindRangePairs(Root, keyTo, keyFrom).AsIterable() : MapModule.FindRangePairs(Root, keyFrom, keyTo).AsIterable(); } /// /// Skips 'amount' values and returns a new tree without the /// skipped values. /// /// Amount to skip /// New tree [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Iterable<(K Key, V Value)> Skip(int amount) { return Iterable.createRange(Go()); IEnumerable<(K, V)> Go() { using var enumer = new MapEnumerator(Root, Rev, amount); while (enumer.MoveNext()) { yield return enumer.Current; } } } /// /// Checks for existence of a key in the map /// /// Key to check /// True if an item with the key supplied is in the map [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool ContainsKey(K key) => !isnull(key) && (Find(key) ? true : false); /// /// Checks for existence of a key in the map /// /// Key to check /// True if an item with the key supplied is in the map [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Contains(K key, V value) => match(Find(key), Some: v => ReferenceEquals(v, value), None: () => false ); /// /// Clears all items from the map /// /// Functionally equivalent to calling Map.empty as the original structure is untouched /// Empty map [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public MapInternal Clear() => Empty; /// /// Atomically adds a range of items to the map /// /// Range of KeyValuePairs to add /// Throws ArgumentException if any of the keys already exist /// New Map with the items added [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public MapInternal AddRange(IEnumerable> pairs) => AddRange(pairs.AsIterable().Map(kv => (kv.Key, kv.Value))); /// /// Atomically sets a series of items using the KeyValuePairs provided /// /// Items to set /// Throws ArgumentException if any of the keys aren't in the map /// New map with the items set [Pure] public MapInternal SetItems(IEnumerable> items) { if (Count == 0) { return new MapInternal(items, MapModuleM.AddOpt.ThrowOnDuplicate); } var self = Root; foreach (var item in items) { if (isnull(item.Key)) continue; self = MapModule.SetItem(self, item.Key, item.Value); } return SetRoot(self); } /// /// Atomically sets a series of items using the Tuples provided. /// /// Items to set /// Throws ArgumentException if any of the keys aren't in the map /// New map with the items set [Pure] public MapInternal SetItems(IEnumerable> items) { if (Count == 0) { return new MapInternal(items, MapModuleM.AddOpt.ThrowOnDuplicate); } var self = Root; foreach (var item in items) { if (isnull(item.Item1)) continue; self = MapModule.SetItem(self, item.Item1, item.Item2); } return SetRoot(self); } /// /// Atomically sets a series of items using the Tuples provided. /// /// Items to set /// Throws ArgumentException if any of the keys aren't in the map /// New map with the items set [Pure] public MapInternal SetItems(IEnumerable<(K, V)> items) { if (Count == 0) { return new MapInternal(items, MapModuleM.AddOpt.ThrowOnDuplicate); } var self = Root; foreach (var item in items) { if (isnull(item.Item1)) continue; self = MapModule.SetItem(self, item.Item1, item.Item2); } return SetRoot(self); } /// /// Atomically sets a series of items using the KeyValuePairs provided. If any of the /// items don't exist then they're silently ignored. /// /// Items to set /// New map with the items set [Pure] public MapInternal TrySetItems(IEnumerable> items) { if (Count == 0) { return new MapInternal(items, MapModuleM.AddOpt.TryAdd); } var self = Root; foreach (var item in items) { if (isnull(item.Key)) continue; self = MapModule.TrySetItem(self, item.Key, item.Value); } return SetRoot(self); } /// /// Atomically sets a series of items using the Tuples provided If any of the /// items don't exist then they're silently ignored. /// /// Items to set /// New map with the items set [Pure] public MapInternal TrySetItems(IEnumerable> items) { if (Count == 0) { return new MapInternal(items, MapModuleM.AddOpt.TryAdd); } var self = Root; foreach (var item in items) { if (isnull(item.Item1)) continue; self = MapModule.TrySetItem(self, item.Item1, item.Item2); } return SetRoot(self); } /// /// Atomically sets a series of items using the Tuples provided If any of the /// items don't exist then they're silently ignored. /// /// Items to set /// New map with the items set [Pure] public MapInternal TrySetItems(IEnumerable<(K, V)> items) { if (Count == 0) { return new MapInternal(items, MapModuleM.AddOpt.TryAdd); } var self = Root; foreach (var item in items) { if (isnull(item.Item1)) continue; self = MapModule.TrySetItem(self, item.Item1, item.Item2); } return SetRoot(self); } /// /// Atomically sets a series of items using the keys provided to find the items /// and the Some delegate maps to a new value. If the items don't exist then /// they're silently ignored. /// /// Keys of items to set /// Function map the existing item to a new one /// New map with the items set [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public MapInternal TrySetItems(IEnumerable keys, Func Some) { var self = this; foreach (var key in keys) { if (isnull(key)) continue; self = TrySetItem(key, Some); } return self; } /// /// Atomically removes a set of keys from the map /// /// Keys to remove /// New map with the items removed [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public MapInternal RemoveRange(IEnumerable keys) { var self = Root; foreach (var key in keys) { self = MapModule.Remove(self, key); } return SetRoot(self); } /// /// Returns true if a Key/Value pair exists in the map /// /// Pair to find /// True if exists, false otherwise [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Contains(KeyValuePair pair) => match(MapModule.TryFind(Root, pair.Key), Some: v => ReferenceEquals(v, pair.Value), None: () => false); /// /// TryGetValue /// /// /// /// [EditorBrowsable(EditorBrowsableState.Never)] [Obsolete("TryGetValue is obsolete, use TryFind instead")] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool TryGetValue(K key, out V value) { var res = match(Find(key), Some: x => (x, true), None: () => (default(V)!, false)); value = res.Item1; return res.Item2; } /// /// Equivalent to map and filter but the filtering is done based on whether the returned /// Option is Some or None. If the item is None then it's filtered out, if not the the /// mapped Some value is used. /// /// Predicate /// Filtered map [Pure] public MapInternal Choose(Func> selector) { IEnumerable<(K, U)> Yield() { foreach (var item in this) { var opt = selector(item.Key, item.Value); if (opt.IsNone) continue; yield return (item.Key, (U)opt); } } return new MapInternal(Yield(), MapModuleM.AddOpt.TryAdd); } /// /// Equivalent to map and filter but the filtering is done based on whether the returned /// Option is Some or None. If the item is None then it's filtered out, if not the the /// mapped Some value is used. /// /// Predicate /// Filtered map [Pure] public MapInternal Choose(Func> selector) { IEnumerable<(K, U)> Yield() { foreach (var item in this) { var opt = selector(item.Value); if (opt.IsNone) continue; yield return (item.Key, (U)opt); } } return new MapInternal(Yield(), MapModuleM.AddOpt.TryAdd); } /// /// Enumerable of map keys /// [Pure] public Iterable Keys { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return Iterable.createRange(Go()); IEnumerable Go() { using var iter = new MapKeyEnumerator(Root, Rev, 0); while (iter.MoveNext()) { yield return iter.Current; } } } } /// /// Enumerable of map values /// [Pure] public Iterable Values { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return Iterable.createRange(Go()); IEnumerable Go() { using var iter = new MapValueEnumerator(Root, Rev, 0); while (iter.MoveNext()) { yield return iter.Current; } } } } /// /// Map to a dictionary /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public IDictionary ToDictionary(Func<(K Key, V Value), KR> keySelector, Func<(K Key, V Value), VR> valueSelector) where KR : notnull => AsIterable().ToDictionary(keySelector, valueSelector); /// /// Enumerable of in-order tuples that make up the map /// /// Tuples [Pure] public Iterable<(K Key, V Value)> Pairs { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => AsIterable().Map(kv => (kv.Key, kv.Value)); } /// /// GetEnumerator - IEnumerable interface /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public MapEnumerator GetEnumerator() => new(Root, Rev, 0); /// /// GetEnumerator - IEnumerable interface /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] IEnumerator<(K Key, V Value)> IEnumerable<(K Key, V Value)>.GetEnumerator() => new MapEnumerator(Root, Rev, 0); /// /// GetEnumerator - IEnumerable interface /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] IEnumerator IEnumerable.GetEnumerator() => new MapEnumerator(Root, Rev, 0); [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Seq<(K Key, V Value)> ToSeq() => toSeq(AsIterable()); [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Iterable<(K Key, V Value)> AsIterable() { return Iterable.createRange(Go()); IEnumerable<(K, V)> Go() { using var iter = new MapEnumerator(Root, Rev, 0); while (iter.MoveNext()) { yield return iter.Current; } } } [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public IEnumerable<(K Key, V Value)> AsEnumerable() { using var iter = new MapEnumerator(Root, Rev, 0); while (iter.MoveNext()) { yield return iter.Current; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] static KeyValuePair ToKeyValuePair((K Key, V Value) kv) => new(kv.Key, kv.Value); [MethodImpl(MethodImplOptions.AggressiveInlining)] internal MapInternal SetRoot(MapItem root) => new(root, Rev); [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static MapInternal operator +(MapInternal lhs, MapInternal rhs) => lhs.Append(rhs); /// /// Union two maps. The merge function is called keys are /// present in both map. /// [Pure] public MapInternal Union(MapInternal other, WhenMissing MapLeft, WhenMissing MapRight, WhenMatched Merge) { if (MapLeft == null) throw new ArgumentNullException(nameof(MapLeft)); if (MapRight == null) throw new ArgumentNullException(nameof(MapRight)); if (Merge == null) throw new ArgumentNullException(nameof(Merge)); var root = MapItem.Empty; foreach (var right in other) { var key = right.Key; var left = Find(key); if (left.IsSome) { root = MapModuleM.Add( root, key, Merge(key, left.Value!, right.Value), MapModuleM.AddOpt.TryAdd); } else { root = MapModuleM.Add( root, key, MapRight(key, right.Value), MapModuleM.AddOpt.TryAdd); } } foreach (var left in this) { var key = left.Key; var right = other.Find(key); if (right.IsNone) { root = MapModuleM.Add( root, key, MapLeft(key, left.Value), MapModuleM.AddOpt.TryAdd); } } return new MapInternal(root, Rev); } /// /// Intersect two maps. Only keys that are in both maps are /// returned. The merge function is called for every resulting /// key. /// [Pure] public MapInternal Intersect(MapInternal other, WhenMatched Merge) { if (Merge == null) throw new ArgumentNullException(nameof(Merge)); var root = MapItem.Empty; foreach (var right in other) { var left = Find(right.Key); if (left.IsSome) { root = MapModuleM.Add( root, right.Key, Merge(right.Key, left.Value!, right.Value), MapModuleM.AddOpt.TryAdd); } } return new MapInternal(root, Rev); } /// /// Map differencing based on key. this - other. /// [Pure] public MapInternal Except(MapInternal other) { var root = MapItem.Empty; foreach(var item in this) { if (!other.ContainsKey(item.Key)) { root = MapModuleM.Add( root, item.Key, item.Value, MapModuleM.AddOpt.ThrowOnDuplicate); } } return new MapInternal(root, Rev); } /// /// Keys that are in both maps are dropped and the remaining /// items are merged and returned. /// [Pure] public MapInternal SymmetricExcept(MapInternal other) { var root = MapItem.Empty; foreach (var left in this) { if (!other.ContainsKey(left.Key)) { root = MapModuleM.Add( root, left.Key, left.Value, MapModuleM.AddOpt.ThrowOnDuplicate); } } foreach (var right in other) { if (!ContainsKey(right.Key)) { //map = map.Add(right.Key, right.Value); root = MapModuleM.Add( root, right.Key, right.Value, MapModuleM.AddOpt.ThrowOnDuplicate); } } return new MapInternal(root, Rev); } [Pure] public MapInternal Append(MapInternal rhs) { if (Count == 0) { return rhs; } if (rhs.Count == 0) { return this; } var self = this; foreach (var item in rhs) { if (!self.ContainsKey(item.Key)) { self = self.Add(item.Key, item.Value); } } return self; } [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static MapInternal operator -(MapInternal lhs, MapInternal rhs) => lhs.Subtract(rhs); [Pure] public MapInternal Subtract(MapInternal rhs) { if(Count == 0) { return Empty; } if (rhs.Count == 0) { return this; } if (rhs.Count < Count) { var self = this; foreach (var item in rhs) { self = self.Remove(item.Key); } return self; } else { var root = MapItem.Empty; foreach (var item in this) { if (!rhs.Contains(item)) { root = MapModuleM.Add(root, item.Key, item.Value, MapModuleM.AddOpt.TryAdd); } } return new MapInternal(root, Rev); } } [Pure] public bool Equals(MapInternal rhs) where EqV : Eq { if (ReferenceEquals(this, rhs)) return true; if (Count != rhs.Count) return false; if (hashCode != 0 && rhs.hashCode != 0 && hashCode != rhs.hashCode) return false; using var iterA = GetEnumerator(); using var iterB = rhs.GetEnumerator(); var count = Count; for (int i = 0; i < count; i++) { iterA.MoveNext(); iterB.MoveNext(); if (!OrdK.Equals(iterA.Current.Key, iterB.Current.Key)) return false; if (!EqV.Equals(iterA.Current.Value, iterB.Current.Value)) return false; } return true; } [Pure] public int CompareTo(MapInternal other) where OrdV : Ord { var cmp = Count.CompareTo(other.Count); if (cmp != 0) return cmp; using var iterA = GetEnumerator(); using var iterB = other.GetEnumerator(); while (iterA.MoveNext() && iterB.MoveNext()) { cmp = OrdK.Compare(iterA.Current.Key, iterB.Current.Key); if (cmp != 0) return cmp; cmp = OrdV.Compare(iterA.Current.Value, iterB.Current.Value); if (cmp != 0) return cmp; } return 0; } [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public MapInternal Filter(Func f) => new(AsIterable().Filter(mi => f(mi.Key, mi.Value)), MapModuleM.AddOpt.ThrowOnDuplicate); [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public MapInternal Filter(Func f) => new(AsIterable().Filter(mi => f(mi.Value)), MapModuleM.AddOpt.ThrowOnDuplicate); /// /// Left/Node/Right traversal in stepped form /// public Fold<(K, V), S> FoldStep(S initialState) { if(IsEmpty) return Fold.Done<(K, V), S>(initialState); var nstack = new MapItem[32]; var fstack = new int[32]; var top = 1; nstack[0] = Root; fstack[0] = 0; return node(initialState); Fold<(K, V), S> node(S state) { while (true) { if (top == 0) return Fold.Done<(K, V), S>(state); var t = top - 1; var n = nstack[t]; var f = fstack[t]; if (n.IsEmpty) { top--; continue; } fstack[t]++; switch (f) { case 0: nstack[top] = n.Left; fstack[top] = 0; top++; continue; case 1: return Fold.Loop(state, n.KeyValue, node); case 2: nstack[top] = n.Right; fstack[top] = 0; top++; continue; default: top--; continue; } } } } /// /// Left/Node/Right traversal (in reverse order) in stepped form /// public Fold<(K, V), S> FoldStepBack(S initialState) { if(IsEmpty) return Fold.Done<(K, V), S>(initialState); var nstack = new MapItem[32]; var fstack = new int[32]; var top = 1; nstack[0] = Root; fstack[0] = 0; return node(initialState); Fold<(K, V), S> node(S state) { while (true) { if (top == 0) return Fold.Done<(K, V), S>(state); var t = top - 1; var n = nstack[t]; var f = fstack[t]; if (n.IsEmpty) { top--; continue; } fstack[t]++; switch (f) { case 0: nstack[top] = n.Right; fstack[top] = 0; top++; continue; case 1: return Fold.Loop(state, n.KeyValue, node); case 2: nstack[top] = n.Left; fstack[top] = 0; top++; continue; default: top--; continue; } } } } /// /// Left/Node/Right traversal in stepped form /// public Fold FoldStepKeys(S initialState) { if(IsEmpty) return Fold.Done(initialState); var nstack = new MapItem[32]; var fstack = new int[32]; var top = 1; nstack[0] = Root; fstack[0] = 0; return node(initialState); Fold node(S state) { while (true) { if (top == 0) return Fold.Done(state); var t = top - 1; var n = nstack[t]; var f = fstack[t]; if (n.IsEmpty) { top--; continue; } fstack[t]++; switch (f) { case 0: nstack[top] = n.Left; fstack[top] = 0; top++; continue; case 1: return Fold.Loop(state, n.KeyValue.Key, node); case 2: nstack[top] = n.Right; fstack[top] = 0; top++; continue; default: top--; continue; } } } } /// /// Left/Node/Right traversal (in reverse order) in stepped form /// public Fold FoldStepBackKeys(S initialState) { if(IsEmpty) return Fold.Done(initialState); var nstack = new MapItem[32]; var fstack = new int[32]; var top = 1; nstack[0] = Root; fstack[0] = 0; return node(initialState); Fold node(S state) { while (true) { if (top == 0) return Fold.Done(state); var t = top - 1; var n = nstack[t]; var f = fstack[t]; if (n.IsEmpty) { top--; continue; } fstack[t]++; switch (f) { case 0: nstack[top] = n.Right; fstack[top] = 0; top++; continue; case 1: return Fold.Loop(state, n.KeyValue.Key, node); case 2: nstack[top] = n.Left; fstack[top] = 0; top++; continue; default: top--; continue; } } } } /// /// Left/Node/Right traversal in stepped form /// public Fold FoldStepValues(S initialState) { if(IsEmpty) return Fold.Done(initialState); var nstack = new MapItem[32]; var fstack = new byte[32]; var top = 1; nstack[0] = Root; fstack[0] = 0; return node(initialState); Fold node(S state) { while (true) { if (top == 0) return Fold.Done(state); var t = top - 1; var n = nstack[t]; var f = fstack[t]; if (n.IsEmpty) { top--; continue; } fstack[t]++; switch (f) { case 0: nstack[top] = n.Left; fstack[top] = 0; top++; continue; case 1: return Fold.Loop(state, n.KeyValue.Value, node); case 2: nstack[top] = n.Right; fstack[top] = 0; top++; continue; default: top--; continue; } } } } /// /// Left/Node/Right traversal (in reverse order) in stepped form /// public Fold FoldStepBackValues(S initialState) { if(IsEmpty) return Fold.Done(initialState); var nstack = new MapItem[32]; var fstack = new byte[32]; var top = 1; nstack[0] = Root; fstack[0] = 0; return node(initialState); Fold node(S state) { while (true) { if (top == 0) return Fold.Done(state); var t = top - 1; var n = nstack[t]; var f = fstack[t]; if (n.IsEmpty) { top--; continue; } fstack[t]++; switch (f) { case 0: nstack[top] = n.Right; fstack[top] = 0; top++; continue; case 1: return Fold.Loop(state, n.KeyValue.Value, node); case 2: nstack[top] = n.Left; fstack[top] = 0; top++; continue; default: top--; continue; } } } } } internal interface IMapItem { (K Key, V Value) KeyValue { get; } } [Serializable] class MapItem : ISerializable, IMapItem { internal static readonly MapItem Empty = new (0, 0, (default!, default!), default!, default!); internal bool IsEmpty => Count == 0; internal int Count; internal byte Height; internal MapItem Left; internal MapItem Right; /// /// Ctor /// internal MapItem(byte height, int count, (K Key, V Value) keyValue, MapItem left, MapItem right) { Count = count; Height = height; KeyValue = keyValue; Left = left; Right = right; } /// /// Deserialisation constructor /// MapItem(SerializationInfo info, StreamingContext context) { var key = (K?)info.GetValue("Key", typeof(K)) ?? throw new SerializationException(); var value = (V?)info.GetValue("Value", typeof(V)) ?? throw new SerializationException(); KeyValue = (key, value); Count = 1; Height = 1; Left = Empty; Right = Empty; } /// /// Serialisation support /// public void GetObjectData(SerializationInfo info, StreamingContext context) { info.AddValue("Key", KeyValue.Key, typeof(K)); info.AddValue("Value", KeyValue.Value, typeof(V)); } internal int BalanceFactor { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => Count == 0 ? 0 : Right.Height - Left.Height; } public (K Key, V Value) KeyValue { get; internal set; } } internal static class MapModuleM { public enum AddOpt { ThrowOnDuplicate, TryAdd, TryUpdate } public static MapItem Add(MapItem node, K key, V value, AddOpt option) where OrdK : Ord { if (node.IsEmpty) { return new MapItem(1, 1, (key, value), MapItem.Empty, MapItem.Empty); } var cmp = OrdK.Compare(key, node.KeyValue.Key); if (cmp < 0) { node.Left = Add(node.Left, key, value, option); return Balance(node); } else if (cmp > 0) { node.Right = Add(node.Right, key, value, option); return Balance(node); } else if(option == AddOpt.TryAdd) { // Already exists, but we don't care return node; } else if (option == AddOpt.TryUpdate) { // Already exists, and we want to update the content node.KeyValue = (key, value); return node; } else { throw new ArgumentException("An element with the same key already exists in the Map"); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static MapItem Balance(MapItem node) { node.Height = (byte)(1 + Math.Max(node.Left.Height, node.Right.Height)); node.Count = 1 + node.Left.Count + node.Right.Count; return node.BalanceFactor >= 2 ? node.Right.BalanceFactor < 0 ? DblRotLeft(node) : RotLeft(node) : node.BalanceFactor <= -2 ? node.Left.BalanceFactor > 0 ? DblRotRight(node) : RotRight(node) : node; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static MapItem DblRotRight(MapItem node) { node.Left = RotLeft(node.Left); return RotRight(node); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static MapItem DblRotLeft(MapItem node) { node.Right = RotRight(node.Right); return RotLeft(node); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static MapItem RotRight(MapItem node) { if (node.IsEmpty || node.Left.IsEmpty) return node; var y = node; var x = y.Left; var t2 = x.Right; x.Right = y; y.Left = t2; y.Height = (byte)(1 + Math.Max(y.Left.Height, y.Right.Height)); x.Height = (byte)(1 + Math.Max(x.Left.Height, x.Right.Height)); y.Count = 1 + y.Left.Count + y.Right.Count; x.Count = 1 + x.Left.Count + x.Right.Count; return x; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static MapItem RotLeft(MapItem node) { if (node.IsEmpty || node.Right.IsEmpty) return node; var x = node; var y = x.Right; var t2 = y.Left; y.Left = x; x.Right = t2; x.Height = (byte)(1 + Math.Max(x.Left.Height, x.Right.Height)); y.Height = (byte)(1 + Math.Max(y.Left.Height, y.Right.Height)); x.Count = 1 + x.Left.Count + x.Right.Count; y.Count = 1 + y.Left.Count + y.Right.Count; return y; } } static class MapModule { public static S Fold(MapItem node, S state, Func folder) { if (node.IsEmpty) { return state; } state = Fold(node.Left, state, folder); state = folder(state, node.KeyValue.Key, node.KeyValue.Value); state = Fold(node.Right, state, folder); return state; } public static S Fold(MapItem node, S state, Func folder) { if (node.IsEmpty) { return state; } state = Fold(node.Left, state, folder); state = folder(state, node.KeyValue.Value); state = Fold(node.Right, state, folder); return state; } public static bool ForAll(MapItem node, Func pred) => node.IsEmpty || pred(node.KeyValue.Key, node.KeyValue.Value) && ForAll(node.Left, pred) && ForAll(node.Right, pred); public static bool Exists(MapItem node, Func pred) => !node.IsEmpty && (pred(node.KeyValue.Key, node.KeyValue.Value) || Exists(node.Left, pred) || Exists(node.Right, pred)); public static MapItem Add(MapItem node, K key, V value) where OrdK : Ord { if (node.IsEmpty) { return new MapItem(1, 1, (key, value), MapItem.Empty, MapItem.Empty); } var cmp = OrdK.Compare(key, node.KeyValue.Key); if (cmp < 0) { return Balance(Make(node.KeyValue, Add(node.Left, key, value), node.Right)); } else if (cmp > 0) { return Balance(Make(node.KeyValue, node.Left, Add(node.Right, key, value))); } else { throw new ArgumentException("An element with the same key already exists in the Map"); } } public static MapItem SetItem(MapItem node, K key, V value) where OrdK : Ord { if (node.IsEmpty) { throw new ArgumentException("Key not found in Map"); } var cmp = OrdK.Compare(key, node.KeyValue.Key); if (cmp < 0) { return Balance(Make(node.KeyValue, SetItem(node.Left, key, value), node.Right)); } else if (cmp > 0) { return Balance(Make(node.KeyValue, node.Left, SetItem(node.Right, key, value))); } else { return new MapItem(node.Height, node.Count, (key, value), node.Left, node.Right); } } public static MapItem TrySetItem(MapItem node, K key, V value) where OrdK : Ord { if (node.IsEmpty) { return node; } var cmp = OrdK.Compare(key, node.KeyValue.Key); if (cmp < 0) { return Balance(Make(node.KeyValue, TrySetItem(node.Left, key, value), node.Right)); } else if (cmp > 0) { return Balance(Make(node.KeyValue, node.Left, TrySetItem(node.Right, key, value))); } else { return new MapItem(node.Height, node.Count, (key, value), node.Left, node.Right); } } public static MapItem TryAdd(MapItem node, K key, V value) where OrdK : Ord { if (node.IsEmpty) { return new MapItem(1, 1, (key, value), MapItem.Empty, MapItem.Empty); } var cmp = OrdK.Compare(key, node.KeyValue.Key); if (cmp < 0) { return Balance(Make(node.KeyValue, TryAdd(node.Left, key, value), node.Right)); } else if (cmp > 0) { return Balance(Make(node.KeyValue, node.Left, TryAdd(node.Right, key, value))); } else { return node; } } public static MapItem AddOrUpdate(MapItem node, K key, V value) where OrdK : Ord { if (node.IsEmpty) { return new MapItem(1, 1, (key, value), MapItem.Empty, MapItem.Empty); } var cmp = OrdK.Compare(key, node.KeyValue.Key); if (cmp < 0) { return Balance(Make(node.KeyValue, AddOrUpdate(node.Left, key, value), node.Right)); } else if (cmp > 0) { return Balance(Make(node.KeyValue, node.Left, AddOrUpdate(node.Right, key, value))); } else { return new MapItem(node.Height, node.Count, (node.KeyValue.Key, value), node.Left, node.Right); } } public static MapItem Remove(MapItem node, K key) where OrdK : Ord { if (node.IsEmpty) { return node; } var cmp = OrdK.Compare(key, node.KeyValue.Key); if (cmp < 0) { return Balance(Make(node.KeyValue, Remove(node.Left, key), node.Right)); } else if (cmp > 0) { return Balance(Make(node.KeyValue, node.Left, Remove(node.Right, key))); } else { // If this is a leaf, just remove it // by returning Empty. If we have only one child, // replace the node with the child. if (node.Right.IsEmpty && node.Left.IsEmpty) { return MapItem.Empty; } else if (node.Right.IsEmpty && !node.Left.IsEmpty) { return node.Left; } else if (!node.Right.IsEmpty && node.Left.IsEmpty) { return node.Right; } else { // We have two children. Remove the next-highest node and replace // this node with it. var successor = node.Right; while (!successor.Left.IsEmpty) { successor = successor.Left; } var newRight = Remove(node.Right, successor.KeyValue.Key); return Balance(Make(successor.KeyValue, node.Left, newRight)); } } } public static V Find(MapItem node, K key) where OrdK : Ord { if (node.IsEmpty) { throw new ArgumentException("Key not found in Map"); } var cmp = OrdK.Compare(key, node.KeyValue.Key); if (cmp < 0) { return Find(node.Left, key); } else if (cmp > 0) { return Find(node.Right, key); } else { return node.KeyValue.Value; } } /// /// TODO: I suspect this is suboptimal, it would be better with a custom Enumerator /// that maintains a stack of nodes to retrace. /// public static IEnumerable FindRange(MapItem node, K a, K b) where OrdK : Ord { if (node.IsEmpty) { yield break; } if (OrdK.Compare(node.KeyValue.Key, a) < 0) { foreach (var item in FindRange(node.Right, a, b)) { yield return item; } } else if (OrdK.Compare(node.KeyValue.Key, b) > 0) { foreach (var item in FindRange(node.Left, a, b)) { yield return item; } } else { foreach (var item in FindRange(node.Left, a, b)) { yield return item; } yield return node.KeyValue.Value; foreach (var item in FindRange(node.Right, a, b)) { yield return item; } } } /// /// TODO: I suspect this is suboptimal, it would be better with a custom Enumerator /// that maintains a stack of nodes to retrace. /// public static IEnumerable<(K, V)> FindRangePairs(MapItem node, K a, K b) where OrdK : Ord { if (node.IsEmpty) { yield break; } if (OrdK.Compare(node.KeyValue.Key, a) < 0) { foreach (var item in FindRangePairs(node.Right, a, b)) { yield return item; } } else if (OrdK.Compare(node.KeyValue.Key, b) > 0) { foreach (var item in FindRangePairs(node.Left, a, b)) { yield return item; } } else { foreach (var item in FindRangePairs(node.Left, a, b)) { yield return item; } yield return node.KeyValue; foreach (var item in FindRangePairs(node.Right, a, b)) { yield return item; } } } public static Option TryFind(MapItem node, K key) where OrdK : Ord { if (node.IsEmpty) { return None; } var cmp = OrdK.Compare(key, node.KeyValue.Key); if (cmp < 0) { return TryFind(node.Left, key); } else if (cmp > 0) { return TryFind(node.Right, key); } else { return Some(node.KeyValue.Value); } } public static MapItem Skip(MapItem node, int amount) { if (amount == 0 || node.IsEmpty) { return node; } if (amount > node.Count) { return MapItem.Empty; } if (!node.Left.IsEmpty && node.Left.Count == amount) { return Balance(Make(node.KeyValue, MapItem.Empty, node.Right)); } if (!node.Left.IsEmpty && node.Left.Count == amount - 1) { return node.Right; } if (node.Left.IsEmpty) { return Skip(node.Right, amount - 1); } var newleft = Skip(node.Left, amount); var remaining = amount - node.Left.Count - newleft.Count; if (remaining > 0) { return Skip(Balance(Make(node.KeyValue, newleft, node.Right)), remaining); } else { return Balance(Make(node.KeyValue, newleft, node.Right)); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static MapItem Make((K,V) kv, MapItem l, MapItem r) => new ((byte)(1 + Math.Max(l.Height, r.Height)), l.Count + r.Count + 1, kv, l, r); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static MapItem Make(K k, V v, MapItem l, MapItem r) => new ((byte)(1 + Math.Max(l.Height, r.Height)), l.Count + r.Count + 1, (k, v), l, r); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static MapItem Balance(MapItem node) => node.BalanceFactor >= 2 ? node.Right.BalanceFactor < 0 ? DblRotLeft(node) : RotLeft(node) : node.BalanceFactor <= -2 ? node.Left.BalanceFactor > 0 ? DblRotRight(node) : RotRight(node) : node; [MethodImpl(MethodImplOptions.AggressiveInlining)] public static MapItem RotRight(MapItem node) => node.IsEmpty || node.Left.IsEmpty ? node : Make(node.Left.KeyValue, node.Left.Left, Make(node.KeyValue, node.Left.Right, node.Right)); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static MapItem RotLeft(MapItem node) => node.IsEmpty || node.Right.IsEmpty ? node : Make(node.Right.KeyValue, Make(node.KeyValue, node.Left, node.Right.Left), node.Right.Right); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static MapItem DblRotRight(MapItem node) => node.IsEmpty || node.Left.IsEmpty ? node : RotRight(Make(node.KeyValue, RotLeft(node.Left), node.Right)); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static MapItem DblRotLeft(MapItem node) => node.IsEmpty || node.Right.IsEmpty ? node : RotLeft(Make(node.KeyValue, node.Left, RotRight(node.Right))); internal static Option<(K, V)> Max(MapItem node) => node.Right.IsEmpty ? node.KeyValue : Max(node.Right); internal static Option<(K, V)> Min(MapItem node) => node.Left.IsEmpty ? node.KeyValue : Min(node.Left); internal static Option<(K, V)> TryFindPredecessor(MapItem root, K key) where OrdK : Ord { Option<(K, V)> predecessor = None; var current = root; if (root.IsEmpty) { return None; } do { var cmp = OrdK.Compare(key, current.KeyValue.Key); if (cmp < 0) { current = current.Left; } else if (cmp > 0) { predecessor = current.KeyValue; current = current.Right; } else { break; } } while (!current.IsEmpty); if (!current.IsEmpty && !current.Left.IsEmpty) { predecessor = Max(current.Left); } return predecessor; } internal static Option<(K, V)> TryFindOrPredecessor(MapItem root, K key) where OrdK : Ord { Option<(K, V)> predecessor = None; var current = root; if (root.IsEmpty) { return None; } do { var cmp = OrdK.Compare(key, current.KeyValue.Key); if (cmp < 0) { current = current.Left; } else if (cmp > 0) { predecessor = current.KeyValue; current = current.Right; } else { return current.KeyValue; } } while (!current.IsEmpty); if (!current.IsEmpty && !current.Left.IsEmpty) { predecessor = Max(current.Left); } return predecessor; } internal static Option<(K, V)> TryFindSuccessor(MapItem root, K key) where OrdK : Ord { Option<(K, V)> successor = None; var current = root; if (root.IsEmpty) { return None; } do { var cmp = OrdK.Compare(key, current.KeyValue.Key); if (cmp < 0) { successor = current.KeyValue; current = current.Left; } else if (cmp > 0) { current = current.Right; } else { break; } } while (!current.IsEmpty); if (!current.IsEmpty && !current.Right.IsEmpty) { successor = Min(current.Right); } return successor; } internal static Option<(K, V)> TryFindOrSuccessor(MapItem root, K key) where OrdK : Ord { Option<(K, V)> successor = None; var current = root; if (root.IsEmpty) { return None; } do { var cmp = OrdK.Compare(key, current.KeyValue.Key); if (cmp < 0) { successor = current.KeyValue; current = current.Left; } else if (cmp > 0) { current = current.Right; } else { return current.KeyValue; } } while (!current.IsEmpty); if (!current.IsEmpty && !current.Right.IsEmpty) { successor = Min(current.Right); } return successor; } } ================================================ FILE: LanguageExt.Core/Immutable Collections/Map/Map.Module.Ord.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using static LanguageExt.Prelude; using System.Diagnostics.Contracts; using LanguageExt.Traits; #pragma warning disable CS0693 // Type parameter has the same name as the type parameter from outer type namespace LanguageExt; /// /// Immutable map module /// AVL tree implementation /// AVL tree is a self-balancing binary search tree. /// [en.wikipedia.org/wiki/AVL_tree](http://en.wikipedia.org/wiki/AVL_tree) /// public static partial class Map { /// /// Creates a new empty Map /// [Pure] public static Map empty() where OrdK : Ord => Map.Empty; /// /// Creates a new empty Map /// [Pure] public static Map singleton((K, V) value) where OrdK : Ord => [value]; /// /// Creates a new empty Map /// [Pure] public static Map singleton(K key, V value) where OrdK : Ord => [(key, value)]; /// /// Creates a new empty Map /// [Pure] public static Map create() where OrdK : Ord => Map.Empty; /// /// Creates a new Map seeded with the keyValues provided /// [Pure] public static Map create(Tuple head, params Tuple[] tail) where OrdK : Ord => empty().AddRange(head.Cons(tail)); /// /// Creates a new Map seeded with the keyValues provided /// [Pure] public static Map create((K, V) head, params (K, V)[] tail) where OrdK : Ord => empty().AddRange(head.Cons(tail)); /// /// Creates a new Map seeded with the keyValues provided /// [Pure] public static Map createRange(IEnumerable> keyValues) where OrdK : Ord => empty().AddRange(keyValues); /// /// Creates a new Map seeded with the keyValues provided /// [Pure] public static Map createRange(IEnumerable<(K, V)> keyValues) where OrdK : Ord => new(keyValues); /// /// Creates a new Map seeded with the keyValues provided /// [Pure] public static Map createRange(ReadOnlySpan<(K, V)> keyValues) where OrdK : Ord => keyValues.IsEmpty ? Map.Empty : new(keyValues); /// /// Atomically adds a new item to the map /// /// Null is not allowed for a Key or a Value /// Key /// Value /// Throws ArgumentException if the key already exists /// Throws ArgumentNullException the key or value are null /// New Map with the item added [Pure] public static Map add(Map map, K key, V value) where OrdK : Ord => map.Add(key, value); /// /// Atomically adds a new item to the map. /// If the key already exists, then the new item is ignored /// /// Null is not allowed for a Key or a Value /// Key /// Value /// Throws ArgumentNullException the key or value are null /// New Map with the item added [Pure] public static Map tryAdd(Map map, K key, V value) where OrdK : Ord => map.TryAdd(key, value); /// /// Atomically adds a new item to the map. /// If the key already exists then the Fail handler is called with the unaltered map /// and the value already set for the key, it expects a new map returned. /// /// Null is not allowed for a Key or a Value /// Key /// Value /// Delegate to handle failure, you're given the unaltered map /// and the value already set for the key /// Throws ArgumentNullException the key or value are null /// New Map with the item added [Pure] public static Map tryAdd(Map map, K key, V value, Func, V, Map> Fail) where OrdK : Ord => map.TryAdd(key, value, Fail); /// /// Atomically adds a new item to the map. /// If the key already exists, the new item replaces it. /// /// Null is not allowed for a Key or a Value /// Key /// Value /// Throws ArgumentNullException the key or value are null /// New Map with the item added [Pure] public static Map addOrUpdate(Map map, K key, V value) where OrdK : Ord => map.AddOrUpdate(key, value); /// /// Retrieve a value from the map by key, map it to a new value, /// put it back. If it doesn't exist, add a new one based on None result. /// /// Key to find /// Throws Exception if None returns null /// Throws Exception if Some returns null /// New map with the mapped value [Pure] public static Map addOrUpdate(Map map, K key, Func Some, Func None) where OrdK : Ord => map.AddOrUpdate(key, Some, None); /// /// Retrieve a value from the map by key, map it to a new value, /// put it back. If it doesn't exist, add a new one based on None result. /// /// Key to find /// Throws ArgumentNullException if None is null /// Throws Exception if Some returns null /// New map with the mapped value [Pure] public static Map addOrUpdate(Map map, K key, Func Some, V None) where OrdK : Ord => map.AddOrUpdate(key, Some, None); /// /// Atomically adds a range of items to the map. /// /// Null is not allowed for a Key or a Value /// Range of tuples to add /// Throws ArgumentException if any of the keys already exist /// Throws ArgumentNullException the keys or values are null /// New Map with the items added [Pure] public static Map addRange(Map map, IEnumerable> keyValues) where OrdK : Ord => map.AddRange(keyValues); /// /// Atomically adds a range of items to the map. /// /// Null is not allowed for a Key or a Value /// Range of tuples to add /// Throws ArgumentException if any of the keys already exist /// Throws ArgumentNullException the keys or values are null /// New Map with the items added [Pure] public static Map addRange(Map map, IEnumerable<(K, V)> keyValues) where OrdK : Ord => map.AddRange(keyValues); /// /// Atomically adds a range of items to the map. If any of the keys exist already /// then they're ignored. /// /// Null is not allowed for a Key or a Value /// Range of tuples to add /// Throws ArgumentNullException the keys or values are null /// New Map with the items added [Pure] public static Map tryAddRange(Map map, IEnumerable> keyValues) where OrdK : Ord => map.TryAddRange(keyValues); /// /// Atomically adds a range of items to the map. If any of the keys exist already /// then they're ignored. /// /// Null is not allowed for a Key or a Value /// Range of tuples to add /// Throws ArgumentNullException the keys or values are null /// New Map with the items added [Pure] public static Map tryAddRange(Map map, IEnumerable<(K, V)> keyValues) where OrdK : Ord => map.TryAddRange(keyValues); /// /// Atomically adds a range of items to the map. If any of the keys exist already /// then they're ignored. /// /// Null is not allowed for a Key or a Value /// Range of KeyValuePairs to add /// Throws ArgumentNullException the keys or values are null /// New Map with the items added [Pure] public static Map tryAddRange(Map map, IEnumerable> keyValues) where OrdK : Ord => map.TryAddRange(keyValues); /// /// Atomically adds a range of items to the map. If any of the keys exist already /// then they're replaced. /// /// Range of tuples to add /// New Map with the items added [Pure] public static Map addOrUpdateRange(Map map, IEnumerable> range) where OrdK : Ord => map.AddOrUpdateRange(range); /// /// Atomically adds a range of items to the map. If any of the keys exist already /// then they're replaced. /// /// Range of tuples to add /// New Map with the items added [Pure] public static Map addOrUpdateRange(Map map, IEnumerable<(K, V)> range) where OrdK : Ord => map.AddOrUpdateRange(range); /// /// Atomically adds a range of items to the map. If any of the keys exist already /// then they're replaced. /// /// Null is not allowed for a Key or a Value /// Range of KeyValuePairs to add /// Throws ArgumentNullException the keys or values are null /// New Map with the items added [Pure] public static Map addOrUpdateRange(Map map, IEnumerable> range) where OrdK : Ord => map.AddOrUpdateRange(range); /// /// Atomically removes an item from the map /// If the key doesn't exists, the request is ignored. /// /// Key /// New map with the item removed [Pure] public static Map remove(Map map, K key) where OrdK : Ord => map.Remove(key); /// /// Checks for existence of a key in the map /// /// Key to check /// True if an item with the key supplied is in the map [Pure] public static bool containsKey(Map map, K key) where OrdK : Ord => map.ContainsKey(key); /// /// Checks for existence of a key in the map /// /// Key to check /// True if an item with the key supplied is in the map [Pure] public static bool contains(Map map, KeyValuePair kv) where OrdK : Ord => map.Contains(kv.Key, kv.Value); /// /// Checks for existence of a key in the map /// /// Key to check /// True if an item with the key supplied is in the map [Pure] public static bool contains(Map map, Tuple kv) where OrdK : Ord => map.Contains(kv.Item1, kv.Item2); /// /// Checks for existence of a key in the map /// /// Key to check /// True if an item with the key supplied is in the map [Pure] public static bool contains(Map map, (K, V) kv) where OrdK : Ord => map.Contains(kv.Item1, kv.Item2); /// /// Atomically updates an existing item /// /// Null is not allowed for a Key or a Value /// Key /// Value /// Throws ArgumentNullException the key or value are null /// New Map with the item added [Pure] public static Map setItem(Map map, K key, V value) where OrdK : Ord => map.SetItem(key, value); /// /// Atomically updates an existing item, unless it doesn't exist, in which case /// it is ignored /// /// Null is not allowed for a Key or a Value /// Key /// Value /// Throws ArgumentNullException the value is null /// New Map with the item added [Pure] public static Map trySetItem(Map map, K key, V value) where OrdK : Ord => map.TrySetItem(key, value); /// /// Atomically sets an item by first retrieving it, applying a map (Some), and then putting /// it back. Silently fails if the value doesn't exist. /// /// Key to set /// Throws Exception if Some returns null /// delegate to map the existing value to a new one before setting /// New map with the item set [Pure] public static Map trySetItem(Map map, K key, Func Some) where OrdK : Ord => map.TrySetItem(key, Some); /// /// Atomically sets an item by first retrieving it, applying a map, and then putting it back. /// Calls the None delegate to return a new map if the item can't be found /// /// Null is not allowed for a Key or a Value /// Key /// delegate to map the existing value to a new one before setting /// delegate to return a new map if the item can't be found /// Throws Exception if Some returns null /// Throws Exception if None returns null /// New map with the item set [Pure] public static Map trySetItem(Map map, K key, Func Some, Func, Map> None) where OrdK : Ord => map.TrySetItem(key, Some, None); /// /// Atomically sets a series of items using the Tuples provided /// /// Items to set /// Throws ArgumentException if any of the keys aren't in the map /// New map with the items set [Pure] public static Map setItems(Map map, IEnumerable> items) where OrdK : Ord => map.SetItems(items); /// /// Atomically sets a series of items using the Tuples provided /// /// Items to set /// Throws ArgumentException if any of the keys aren't in the map /// New map with the items set [Pure] public static Map setItems(Map map, IEnumerable<(K, V)> items) where OrdK : Ord => map.SetItems(items); /// /// Atomically sets a series of items using the KeyValuePairs provided /// /// Items to set /// Throws ArgumentException if any of the keys aren't in the map /// New map with the items set [Pure] public static Map setItems(Map map, IEnumerable> items) where OrdK : Ord => map.SetItems(items); /// /// Atomically sets a series of items using the Tuples provided. /// /// Items to set /// Throws ArgumentException if any of the keys aren't in the map /// New map with the items set [Pure] public static Map trySetItems(Map map, IEnumerable> items) where OrdK : Ord => map.SetItems(items); /// /// Atomically sets a series of items using the Tuples provided. /// /// Items to set /// Throws ArgumentException if any of the keys aren't in the map /// New map with the items set [Pure] public static Map trySetItems(Map map, IEnumerable<(K, V)> items) where OrdK : Ord => map.SetItems(items); /// /// Atomically sets a series of items using the KeyValuePairs provided. If any of the /// items don't exist then they're silently ignored. /// /// Items to set /// New map with the items set [Pure] public static Map trySetItems(Map map, IEnumerable> items) where OrdK : Ord => map.TrySetItems(items); /// /// Atomically sets a series of items using the keys provided to find the items /// and the Some delegate maps to a new value. If the items don't exist then /// they're silently ignored. /// /// Keys of items to set /// Function map the existing item to a new one /// New map with the items set [Pure] public static Map trySetItems(Map map, IEnumerable keys, Func Some) where OrdK : Ord => map.TrySetItems(keys, Some); /// /// Retrieve a value from the map by key /// /// Key to find /// Found value [Pure] public static Option find(Map map, K key) where OrdK : Ord => map.Find(key); /// /// Retrieve a value from the map by key as an enumerable /// /// Key to find /// Found value [Pure] public static IEnumerable findSeq(Map map, K key) where OrdK : Ord => map.FindSeq(key); /// /// Retrieve a value from the map by key and pattern match the /// result. /// /// Key to find /// Found value [Pure] public static R find(Map map, K key, Func Some, Func None) where OrdK : Ord => map.Find(key, Some, None); /// /// Retrieve a value from the map by key, map it to a new value, /// put it back. /// /// Key to find /// New map with the mapped value [Pure] public static Map setItem(Map map, K key, Func mapper) where OrdK : Ord => map.SetItem(key, mapper); /// /// Retrieve a range of values /// /// Range start (inclusive) /// Range to (inclusive) /// Throws ArgumentNullException the keyFrom or keyTo are null /// Range of values [Pure] public static IEnumerable findRange(Map map, K keyFrom, K keyTo) where OrdK : Ord => map.FindRange(keyFrom, keyTo); /// /// Skips 'amount' values and returns a new tree without the /// skipped values. /// /// Amount to skip /// Enumerable of map items [Pure] public static IEnumerable<(K Key, V Value)> skip(Map map, int amount) where OrdK : Ord => map.Skip(amount); /// /// Atomically iterate through all key/value pairs in the map (in order) and execute an /// action on each /// /// Action to execute /// Unit public static Unit iter(Map map, Action action) where OrdK : Ord => map.Iter(action); /// /// Atomically iterate through all key/value pairs in the map (in order) and execute an /// action on each /// /// Action to execute /// Unit public static Unit iter(Map map, Action action) where OrdK : Ord => map.Iter(action); /// /// Return true if all items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] public static bool forall(Map map, Func pred) where OrdK : Ord => map.ForAll(pred); /// /// Return true if all items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] public static bool forall(Map map, Func pred) where OrdK : Ord => map.ForAll(pred); /// /// Return true if all items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] public static bool forall(Map map, Func, bool> pred) where OrdK : Ord => map.ForAll(pred); /// /// Return true if all items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] public static bool forall(Map map, Func<(K Key, V Value), bool> pred) where OrdK : Ord => map.ForAll(pred); /// /// Return true if all items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] public static bool forall(Map map, Func, bool> pred) where OrdK : Ord => map.ForAll(pred); /// /// Atomically maps the map to a new map /// /// Mapped items in a new map [Pure] public static Map map(Map map, Func f) where OrdK : Ord => map.Select(f); /// /// Atomically maps the map to a new map /// /// Mapped items in a new map [Pure] public static Map map(Map map, Func f) where OrdK : Ord => map.Select(f); /// /// Atomically filter out items that return false when a predicate is applied /// /// Predicate /// New map with items filtered [Pure] public static Map filter(Map map, Func predicate) where OrdK : Ord => map.Filter(predicate); /// /// Atomically filter out items that return false when a predicate is applied /// /// Predicate /// New map with items filtered [Pure] public static Map filter(Map map, Func predicate) where OrdK : Ord => map.Filter(predicate); /// /// Equivalent to map and filter but the filtering is done based on whether the returned /// Option is Some or None. If the item is None then it's filtered out, if not the the /// mapped Some value is used. /// /// Predicate /// Filtered map [Pure] public static Map choose(Map map, Func> selector) where OrdK : Ord => map.Choose(selector); /// /// Equivalent to map and filter but the filtering is done based on whether the returned /// Option is Some or None. If the item is None then it's filtered out, if not the the /// mapped Some value is used. /// /// Predicate /// Filtered map [Pure] public static Map choose(Map map, Func> selector) where OrdK : Ord => map.Choose(selector); /// /// Number of items in the map /// [Pure] public static int length(Map map) where OrdK : Ord => map.Count; /// /// Atomically folds all items in the map (in order) using the folder function provided. /// /// State type /// Initial state /// Fold function /// Folded state [Pure] public static S fold(Map map, S state, Func folder) where OrdK : Ord => map.Fold(state, folder); /// /// Atomically folds all items in the map (in order) using the folder function provided. /// /// State type /// Initial state /// Fold function /// Folded state [Pure] public static S fold(Map map, S state, Func folder) where OrdK : Ord => map.Fold(state, folder); /// /// Return true if *any* items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] public static bool exists(Map map, Func pred) where OrdK : Ord => map.Exists(pred); /// /// Return true if *any* items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] public static bool exists(Map map, Func, bool> pred) where OrdK : Ord => map.Exists(pred); /// /// Return true if *any* items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] public static bool exists(Map map, Func<(K Key, V Value), bool> pred) where OrdK : Ord => map.Exists(pred); /// /// Return true if *any* items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] public static bool exists(Map map, Func, bool> pred) where OrdK : Ord => map.Exists(pred); /// /// Return true if *any* items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] public static bool exists(Map map, Func pred) where OrdK : Ord => map.Exists(pred); /// /// Convert any IDictionary into an immutable Map K V /// [Pure] public static Map toMap(IDictionary dict) where OrdK : Ord => Prelude.toMap(dict.AsIterable().Map(kv => (kv.Key, kv.Value))); /// /// Convert any IDictionary into an immutable Map K V /// [Pure] public static HashMap toHashMap(IDictionary dict) where OrdK : Ord => Prelude.toHashMap(dict.AsIterable().Map(kv => (kv.Key, kv.Value))); /// /// Convert any IDictionary into an immutable Map K V /// [Pure] public static HashMap ToHashMap(this IDictionary dict) where OrdK : Ord => Prelude.toHashMap(dict.AsIterable().Map(kv => (kv.Key, kv.Value))); /// /// Union two maps. The merge function is called keys are /// present in both map. /// [Pure] public static Map union(Map left, Map right, WhenMatched Merge) where OrdK : Ord => left.Union(right, (_, v) => v, (_, v) => v, Merge); /// /// Union two maps. The merge function is called keys are /// present in both map. /// [Pure] public static Map union(Map left, Map right, WhenMissing MapRight, WhenMatched Merge) where OrdK : Ord => left.Union(right, (_, v) => v, MapRight, Merge); /// /// Union two maps. The merge function is called keys are /// present in both map. /// [Pure] public static Map union(Map left, Map right, WhenMissing MapLeft, WhenMatched Merge) where OrdK : Ord => left.Union(right, MapLeft, (_, v) => v, Merge); /// /// Union two maps. The merge function is called keys are /// present in both map. /// [Pure] public static Map union(Map left, Map right, WhenMissing MapLeft, WhenMissing MapRight, WhenMatched Merge) where OrdK : Ord => left.Union(right, MapLeft, MapRight, Merge); /// /// Intersect two maps. Only keys that are in both maps are /// returned. The merge function is called for every resulting /// key. /// [Pure] public static Map intersect(Map left, Map right, WhenMatched merge) where OrdK : Ord => left.Intersect(right, merge); /// /// Map differencing based on key. this - other. /// [Pure] public static Map except(Map left, Map right) where OrdK : Ord => left.Except(right); /// /// Keys that are in both maps are dropped and the remaining /// items are merged and returned. /// [Pure] public static Map symmetricExcept(Map left, Map right) where OrdK : Ord => left.SymmetricExcept(right); } ================================================ FILE: LanguageExt.Core/Immutable Collections/Map/Map.Module.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics.Contracts; namespace LanguageExt; /// /// Immutable map module /// AVL tree implementation /// AVL tree is a self-balancing binary search tree. /// [wikipedia.org/wiki/AVL_tree](http://en.wikipedia.org/wiki/AVL_tree) /// public static partial class Map { /// /// Creates a new empty `Map` /// [Pure] public static Map empty() => Map.Empty; /// /// Creates a new singleton `Map` /// [Pure] public static Map singleton((K, V) value) => [value]; /// /// Creates a new singleton `Map` /// [Pure] public static Map singleton(K key, V value) => [(key, value)]; /// /// Creates a new Map seeded with the keyValues provided /// [Pure] public static Map create() => Map.Empty; /// /// Creates a new Map seeded with the keyValues provided /// [Pure] public static Map create(Tuple head, params Tuple[] tail) => empty().AddRange(head.Cons(tail)); /// /// Creates a new Map seeded with the keyValues provided /// [Pure] public static Map create(KeyValuePair head, params KeyValuePair[] tail) => empty().AddRange(head.Cons(tail)); /// /// Creates a new Map seeded with the keyValues provided /// [Pure] public static Map create((K, V) head, params (K, V)[] tail) => empty().AddRange(head.Cons(tail)); /// /// Creates a new Map seeded with the keyValues provided /// [Pure] public static Map createRange(IEnumerable> keyValues) => empty().AddRange(keyValues); /// /// Creates a new Map seeded with the keyValues provided /// [Pure] public static Map createRange(IEnumerable<(K, V)> keyValues) => new (keyValues); /// /// Creates a new Map seeded with the keyValues provided /// [Pure] public static Map createRange(ReadOnlySpan<(K, V)> keyValues) => keyValues.IsEmpty ? Map.Empty : new (keyValues); /// /// Creates a new Map seeded with the keyValues provided /// [Pure] public static Map createRange(IEnumerable> keyValues) => empty().AddRange(keyValues); /// /// Atomically adds a new item to the map /// /// Null is not allowed for a Key or a Value /// Key /// Value /// Throws ArgumentException if the key already exists /// Throws ArgumentNullException the key or value are null /// New Map with the item added [Pure] public static Map add(Map map, K key, V value) => map.Add(key, value); /// /// Atomically adds a new item to the map. /// If the key already exists, then the new item is ignored /// /// Null is not allowed for a Key or a Value /// Key /// Value /// Throws ArgumentNullException the key or value are null /// New Map with the item added [Pure] public static Map tryAdd(Map map, K key, V value) => map.TryAdd(key, value); /// /// Atomically adds a new item to the map. /// If the key already exists then the Fail handler is called with the unaltered map /// and the value already set for the key, it expects a new map returned. /// /// Null is not allowed for a Key or a Value /// Key /// Value /// Delegate to handle failure, you're given the unaltered map /// and the value already set for the key /// Throws ArgumentNullException the key or value are null /// New Map with the item added [Pure] public static Map tryAdd(Map map, K key, V value, Func, V, Map> Fail) => map.TryAdd(key, value, Fail); /// /// Atomically adds a new item to the map. /// If the key already exists, the new item replaces it. /// /// Null is not allowed for a Key or a Value /// Key /// Value /// Throws ArgumentNullException the key or value are null /// New Map with the item added [Pure] public static Map addOrUpdate(Map map, K key, V value) => map.AddOrUpdate(key, value); /// /// Retrieve a value from the map by key, map it to a new value, /// put it back. If it doesn't exist, add a new one based on None result. /// /// Key to find /// Throws Exception if None returns null /// Throws Exception if Some returns null /// New map with the mapped value [Pure] public static Map addOrUpdate(Map map, K key, Func Some, Func None) => map.AddOrUpdate(key, Some, None); /// /// Retrieve a value from the map by key, map it to a new value, /// put it back. If it doesn't exist, add a new one based on None result. /// /// Key to find /// Throws ArgumentNullException if None is null /// Throws Exception if Some returns null /// New map with the mapped value [Pure] public static Map addOrUpdate(Map map, K key, Func Some, V None) => map.AddOrUpdate(key, Some, None); /// /// Atomically adds a range of items to the map. /// /// Null is not allowed for a Key or a Value /// Range of tuples to add /// Throws ArgumentException if any of the keys already exist /// Throws ArgumentNullException the keys or values are null /// New Map with the items added [Pure] public static Map addRange(Map map, IEnumerable> keyValues) => map.AddRange(keyValues); /// /// Atomically adds a range of items to the map. /// /// Null is not allowed for a Key or a Value /// Range of tuples to add /// Throws ArgumentException if any of the keys already exist /// Throws ArgumentNullException the keys or values are null /// New Map with the items added [Pure] public static Map addRange(Map map, IEnumerable<(K, V)> keyValues) => map.AddRange(keyValues); /// /// Atomically adds a range of items to the map. /// /// Null is not allowed for a Key or a Value /// Range of tuples to add /// Throws ArgumentException if any of the keys already exist /// Throws ArgumentNullException the keys or values are null /// New Map with the items added [Pure] public static Map addRange(Map map, IEnumerable> keyValues) => map.AddRange(keyValues); /// /// Atomically adds a range of items to the map. If any of the keys exist already /// then they're ignored. /// /// Null is not allowed for a Key or a Value /// Range of tuples to add /// Throws ArgumentNullException the keys or values are null /// New Map with the items added [Pure] public static Map tryAddRange(Map map, IEnumerable> keyValues) => map.TryAddRange(keyValues); /// /// Atomically adds a range of items to the map. If any of the keys exist already /// then they're ignored. /// /// Null is not allowed for a Key or a Value /// Range of tuples to add /// Throws ArgumentNullException the keys or values are null /// New Map with the items added [Pure] public static Map tryAddRange(Map map, IEnumerable<(K, V)> keyValues) => map.TryAddRange(keyValues); /// /// Atomically adds a range of items to the map. If any of the keys exist already /// then they're ignored. /// /// Null is not allowed for a Key or a Value /// Range of KeyValuePairs to add /// Throws ArgumentNullException the keys or values are null /// New Map with the items added [Pure] public static Map tryAddRange(Map map, IEnumerable> keyValues) => map.TryAddRange(keyValues); /// /// Atomically adds a range of items to the map. If any of the keys exist already /// then they're replaced. /// /// Range of tuples to add /// New Map with the items added [Pure] public static Map addOrUpdateRange(Map map, IEnumerable> range) => map.AddOrUpdateRange(range); /// /// Atomically adds a range of items to the map. If any of the keys exist already /// then they're replaced. /// /// Range of tuples to add /// New Map with the items added [Pure] public static Map addOrUpdateRange(Map map, IEnumerable<(K, V)> range) => map.AddOrUpdateRange(range); /// /// Atomically adds a range of items to the map. If any of the keys exist already /// then they're replaced. /// /// Null is not allowed for a Key or a Value /// Range of KeyValuePairs to add /// Throws ArgumentNullException the keys or values are null /// New Map with the items added [Pure] public static Map addOrUpdateRange(Map map, IEnumerable> range) => map.AddOrUpdateRange(range); /// /// Atomically removes an item from the map /// If the key doesn't exists, the request is ignored. /// /// Key /// New map with the item removed [Pure] public static Map remove(Map map, K key) => map.Remove(key); /// /// Checks for existence of a key in the map /// /// Key to check /// True if an item with the key supplied is in the map [Pure] public static bool containsKey(Map map, K key) => map.ContainsKey(key); /// /// Checks for existence of a key in the map /// /// Key to check /// True if an item with the key supplied is in the map [Pure] public static bool contains(Map map, KeyValuePair kv) => map.Contains(kv.Key, kv.Value); /// /// Checks for existence of a key in the map /// /// Key to check /// True if an item with the key supplied is in the map [Pure] public static bool contains(Map map, Tuple kv) => map.Contains(kv.Item1, kv.Item2); /// /// Checks for existence of a key in the map /// /// Key to check /// True if an item with the key supplied is in the map [Pure] public static bool contains(Map map, (K, V) kv) => map.Contains(kv.Item1, kv.Item2); /// /// Atomically updates an existing item /// /// Null is not allowed for a Key or a Value /// Key /// Value /// Throws ArgumentNullException the key or value are null /// New Map with the item added [Pure] public static Map setItem(Map map, K key, V value) => map.SetItem(key, value); /// /// Atomically updates an existing item, unless it doesn't exist, in which case /// it is ignored /// /// Null is not allowed for a Key or a Value /// Key /// Value /// Throws ArgumentNullException the value is null /// New Map with the item added [Pure] public static Map trySetItem(Map map, K key, V value) => map.TrySetItem(key, value); /// /// Atomically sets an item by first retrieving it, applying a map (Some), and then putting /// it back. Silently fails if the value doesn't exist. /// /// Key to set /// Throws Exception if Some returns null /// delegate to map the existing value to a new one before setting /// New map with the item set [Pure] public static Map trySetItem(Map map, K key, Func Some) => map.TrySetItem(key, Some); /// /// Atomically sets an item by first retrieving it, applying a map, and then putting it back. /// Calls the None delegate to return a new map if the item can't be found /// /// Null is not allowed for a Key or a Value /// Key /// delegate to map the existing value to a new one before setting /// delegate to return a new map if the item can't be found /// Throws Exception if Some returns null /// Throws Exception if None returns null /// New map with the item set [Pure] public static Map trySetItem(Map map, K key, Func Some, Func, Map> None) => map.TrySetItem(key, Some, None); /// /// Atomically sets a series of items using the Tuples provided /// /// Items to set /// Throws ArgumentException if any of the keys aren't in the map /// New map with the items set [Pure] public static Map setItems(Map map, IEnumerable> items) => map.SetItems(items); /// /// Atomically sets a series of items using the Tuples provided /// /// Items to set /// Throws ArgumentException if any of the keys aren't in the map /// New map with the items set [Pure] public static Map setItems(Map map, IEnumerable<(K, V)> items) => map.SetItems(items); /// /// Atomically sets a series of items using the KeyValuePairs provided /// /// Items to set /// Throws ArgumentException if any of the keys aren't in the map /// New map with the items set [Pure] public static Map setItems(Map map, IEnumerable> items) => map.SetItems(items); /// /// Atomically sets a series of items using the Tuples provided. /// /// Items to set /// Throws ArgumentException if any of the keys aren't in the map /// New map with the items set [Pure] public static Map trySetItems(Map map, IEnumerable> items) => map.SetItems(items); /// /// Atomically sets a series of items using the Tuples provided. /// /// Items to set /// Throws ArgumentException if any of the keys aren't in the map /// New map with the items set [Pure] public static Map trySetItems(Map map, IEnumerable<(K, V)> items) => map.SetItems(items); /// /// Atomically sets a series of items using the KeyValuePairs provided. If any of the /// items don't exist then they're silently ignored. /// /// Items to set /// New map with the items set [Pure] public static Map trySetItems(Map map, IEnumerable> items) => map.TrySetItems(items); /// /// Atomically sets a series of items using the keys provided to find the items /// and the Some delegate maps to a new value. If the items don't exist then /// they're silently ignored. /// /// Keys of items to set /// Function map the existing item to a new one /// New map with the items set [Pure] public static Map trySetItems(Map map, IEnumerable keys, Func Some) => map.TrySetItems(keys, Some); /// /// Retrieve a value from the map by key /// /// Key to find /// Found value [Pure] public static Option find(Map map, K key) => map.Find(key); /// /// Retrieve a value from the map by key as an enumerable /// /// Key to find /// Found value [Pure] public static IEnumerable findSeq(Map map, K key) => map.FindSeq(key); /// /// Retrieve a value from the map by key and pattern match the /// result. /// /// Key to find /// Found value [Pure] public static R find(Map map, K key, Func Some, Func None) => map.Find(key, Some, None); /// /// Retrieve a value from the map by key, map it to a new value, /// put it back. /// /// Key to find /// New map with the mapped value [Pure] public static Map setItem(Map map, K key, Func mapper) => map.SetItem(key, mapper); /// /// Retrieve a range of values /// /// Range start (inclusive) /// Range to (inclusive) /// Throws ArgumentNullException the keyFrom or keyTo are null /// Range of values [Pure] public static IEnumerable findRange(Map map, K keyFrom, K keyTo) => map.FindRange(keyFrom, keyTo); /// /// Skips 'amount' values and returns a new tree without the /// skipped values. /// /// Amount to skip /// Enumerable of map items [Pure] public static IEnumerable<(K Key, V Value)> skip(Map map, int amount) => map.Skip(amount); /// /// Atomically iterate through all key/value pairs in the map (in order) and execute an /// action on each /// /// Action to execute /// Unit public static Unit iter(Map map, Action action) => map.Iter(action); /// /// Atomically iterate through all key/value pairs in the map (in order) and execute an /// action on each /// /// Action to execute /// Unit public static Unit iter(Map map, Action action) => map.Iter(action); /// /// Return true if all items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] public static bool forall(Map map, Func pred) => map.ForAll(pred); /// /// Return true if all items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] public static bool forall(Map map, Func pred) => map.ForAll(pred); /// /// Return true if all items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] public static bool forall(Map map, Func, bool> pred) => map.ForAll(pred); /// /// Return true if all items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] public static bool forall(Map map, Func<(K Key, V Value), bool> pred) => map.ForAll(pred); /// /// Return true if all items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] public static bool forall(Map map, Func, bool> pred) => map.ForAll(pred); /// /// Atomically maps the map to a new map /// /// Mapped items in a new map [Pure] public static Map map(Map map, Func f) => map.Select(f); /// /// Atomically maps the map to a new map /// /// Mapped items in a new map [Pure] public static Map map(Map map, Func f) => map.Select(f); /// /// Atomically filter out items that return false when a predicate is applied /// /// Predicate /// New map with items filtered [Pure] public static Map filter(Map map, Func predicate) => map.Filter(predicate); /// /// Atomically filter out items that return false when a predicate is applied /// /// Predicate /// New map with items filtered [Pure] public static Map filter(Map map, Func predicate) => map.Filter(predicate); /// /// Equivalent to map and filter but the filtering is done based on whether the returned /// Option is Some or None. If the item is None then it's filtered out, if not the the /// mapped Some value is used. /// /// Predicate /// Filtered map [Pure] public static Map choose(Map map, Func> selector) => map.Choose(selector); /// /// Equivalent to map and filter but the filtering is done based on whether the returned /// Option is Some or None. If the item is None then it's filtered out, if not the the /// mapped Some value is used. /// /// Predicate /// Filtered map [Pure] public static Map choose(Map map, Func> selector) => map.Choose(selector); /// /// Number of items in the map /// [Pure] public static int length(Map map) => map.Count; /// /// Atomically folds all items in the map (in order) using the folder function provided. /// /// State type /// Initial state /// Fold function /// Folded state [Pure] public static S fold(Map map, S state, Func folder) => map.Fold(state, folder); /// /// Atomically folds all items in the map (in order) using the folder function provided. /// /// State type /// Initial state /// Fold function /// Folded state [Pure] public static S fold(Map map, S state, Func folder) => map.Fold(state, folder); /// /// Return true if *any* items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] public static bool exists(Map map, Func pred) => map.Exists(pred); /// /// Return true if *any* items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] public static bool exists(Map map, Func, bool> pred) => map.Exists(pred); /// /// Return true if *any* items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] public static bool exists(Map map, Func<(K Key, V Value), bool> pred) => map.Exists(pred); /// /// Return true if *any* items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] public static bool exists(Map map, Func, bool> pred) => map.Exists(pred); /// /// Return true if *any* items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] public static bool exists(Map map, Func pred) => map.Exists(pred); /// /// Convert any IDictionary into an immutable Map K V /// [Pure] public static Map toMap(IDictionary dict) => Prelude.toMap(dict.AsIterable().Map(kv => (kv.Key, kv.Value))); /// /// Convert any IDictionary into an immutable Map K V /// [Pure] public static Map ToMap(this IDictionary dict) => Prelude.toMap(dict.AsIterable().Map(kv => (kv.Key, kv.Value))); /// /// Convert any IDictionary into an immutable Map K V /// [Pure] public static HashMap toHashMap(IDictionary dict) => Prelude.toHashMap(dict.AsIterable().Map(kv => (kv.Key, kv.Value))); /// /// Convert any IDictionary into an immutable Map K V /// [Pure] public static HashMap ToHashMap(this IDictionary dict) => Prelude.toHashMap(dict.AsIterable().Map(kv => (kv.Key, kv.Value))); /// /// Union two maps. The merge function is called keys are /// present in both map. /// [Pure] public static Map union(Map left, Map right, WhenMatched Merge) => left.Union(right, (_, v) => v, (_, v) => v, Merge); /// /// Union two maps. The merge function is called keys are /// present in both map. /// [Pure] public static Map union(Map left, Map right, WhenMissing MapRight, WhenMatched Merge) => left.Union(right, (_, v) => v, MapRight, Merge); /// /// Union two maps. The merge function is called keys are /// present in both map. /// [Pure] public static Map union(Map left, Map right, WhenMissing MapLeft, WhenMatched Merge) => left.Union(right, MapLeft, (_, v) => v, Merge); /// /// Union two maps. The merge function is called keys are /// present in both map. /// [Pure] public static Map union(Map left, Map right, WhenMissing MapLeft, WhenMissing MapRight, WhenMatched Merge) => left.Union(right, MapLeft, MapRight, Merge); /// /// Intersect two maps. Only keys that are in both maps are /// returned. The merge function is called for every resulting /// key. /// [Pure] public static Map intersect(Map left, Map right, WhenMatched merge) => left.Intersect(right, merge); /// /// Map differencing based on key. this - other. /// [Pure] public static Map except(Map left, Map right) => left.Except(right); /// /// Keys that are in both maps are dropped and the remaining /// items are merged and returned. /// [Pure] public static Map symmetricExcept(Map left, Map right) => left.SymmetricExcept(right); } ================================================ FILE: LanguageExt.Core/Immutable Collections/Map/Map.Ord.cs ================================================ #pragma warning disable CS0693 // Type parameter has the same name as the type parameter from outer type using LanguageExt.Traits; using static LanguageExt.Prelude; using System; using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics.Contracts; using System.Linq; using System.Numerics; using System.Runtime.CompilerServices; using LanguageExt.ClassInstances; namespace LanguageExt; /// /// Immutable map /// AVL tree implementation /// AVL tree is a self-balancing binary search tree. /// [wikipedia.org/wiki/AVL_tree](http://en.wikipedia.org/wiki/AVL_tree) /// /// Key type /// Value type [Serializable] [CollectionBuilder(typeof(Map), nameof(Map.createRange))] public readonly struct Map : IReadOnlyDictionary, IEnumerable<(K Key, V Value)>, IEquatable>, IComparable>, IComparisonOperators, Map, bool>, IAdditionOperators, Map, Map>, ISubtractionOperators, Map, Map>, IAdditiveIdentity, Map>, Monoid>, IComparable where OrdK : Ord { public static Map Empty { get; } = new(MapInternal, K, V>.Empty); readonly MapInternal value; internal static Map Wrap(MapInternal map) => new (map); public Map(IEnumerable<(K Key, V Value)> items) : this(items, true) { } public Map(ReadOnlySpan<(K Key, V Value)> items) : this(items, true) { } public Map(IEnumerable<(K Key, V Value)> items, bool tryAdd) => value = new MapInternal( items, tryAdd ? MapModuleM.AddOpt.TryAdd : MapModuleM.AddOpt.ThrowOnDuplicate); public Map(ReadOnlySpan<(K Key, V Value)> items, bool tryAdd) => value = new MapInternal( items, tryAdd ? MapModuleM.AddOpt.TryAdd : MapModuleM.AddOpt.ThrowOnDuplicate); internal Map(MapInternal value) => this.value = value; internal Map(MapItem root, bool rev) => this.value = new MapInternal(root, rev); internal MapInternal Value => value ?? MapInternal.Empty; /// /// Reference version for use in pattern-matching /// /// /// /// Empty collection = null /// Singleton collection = (K, V) /// More = ((K, V), Seq〈(K, V)〉) -- head and tail /// /// var res = list.Case switch /// { /// /// (var x, var xs) => ..., /// A value => ..., /// _ => ... /// } /// /// [Pure] public object? Case => IsEmpty ? null : AsIterable().ToSeq().Case; /// /// 'this' accessor /// /// Key /// Optional value [Pure] public V this[K key] => Value[key]; /// /// Is the map empty /// [Pure] public bool IsEmpty { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => value?.IsEmpty ?? true; } /// /// Number of items in the map /// [Pure] public int Count { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => value?.Count ?? 0; } /// /// Alias of Count /// [Pure] public int Length { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => value?.Count ?? 0; } /// /// Atomically adds a new item to the map /// /// Null is not allowed for a Key or a Value /// Key /// Value /// Throws ArgumentException if the key already exists /// Throws ArgumentNullException the key or value are null /// New Map with the item added [Pure] public Map Add(K key, V value) => Wrap(Value.Add(key,value)); /// /// Atomically adds a new item to the map. /// If the key already exists, then the new item is ignored /// /// Null is not allowed for a Key or a Value /// Key /// Value /// Throws ArgumentNullException the key or value are null /// New Map with the item added [Pure] public Map TryAdd(K key, V value) => Wrap(Value.TryAdd(key, value)); /// /// Atomically adds a new item to the map. /// If the key already exists then the Fail handler is called with the unaltered map /// and the value already set for the key, it expects a new map returned. /// /// Null is not allowed for a Key or a Value /// Key /// Value /// Delegate to handle failure, you're given the unaltered map /// and the value already set for the key /// Throws ArgumentNullException the key or value are null /// New Map with the item added [Pure] public Map TryAdd(K key, V value, Func, V, Map> Fail) => Wrap(Value.TryAdd(key, value, (m,v) => Fail(Wrap(m),v).Value)); /// /// Atomically adds a range of items to the map. /// /// Null is not allowed for a Key or a Value /// Range of tuples to add /// Throws ArgumentException if any of the keys already exist /// Throws ArgumentNullException the keys or values are null /// New Map with the items added [Pure] public Map AddRange(IEnumerable> range) => Wrap(Value.AddRange(range)); /// /// Atomically adds a range of items to the map. /// /// Null is not allowed for a Key or a Value /// Range of tuples to add /// Throws ArgumentException if any of the keys already exist /// Throws ArgumentNullException the keys or values are null /// New Map with the items added [Pure] public Map AddRange(IEnumerable<(K, V)> range) => Wrap(Value.AddRange(range)); /// /// Atomically adds a range of items to the map. If any of the keys exist already /// then they're ignored. /// /// Null is not allowed for a Key or a Value /// Range of tuples to add /// Throws ArgumentNullException the keys or values are null /// New Map with the items added [Pure] public Map TryAddRange(IEnumerable> range) => Wrap(Value.TryAddRange(range)); /// /// Atomically adds a range of items to the map. If any of the keys exist already /// then they're ignored. /// /// Null is not allowed for a Key or a Value /// Range of tuples to add /// Throws ArgumentNullException the keys or values are null /// New Map with the items added [Pure] public Map TryAddRange(IEnumerable<(K, V)> range) => Wrap(Value.TryAddRange(range)); /// /// Atomically adds a range of items to the map. If any of the keys exist already /// then they're ignored. /// /// Null is not allowed for a Key or a Value /// Range of KeyValuePairs to add /// Throws ArgumentNullException the keys or values are null /// New Map with the items added [Pure] public Map TryAddRange(IEnumerable> range) => Wrap(Value.TryAddRange(range)); /// /// Atomically adds a range of items to the map. If any of the keys exist already /// then they're replaced. /// /// Null is not allowed for a Key or a Value /// Range of tuples to add /// Throws ArgumentNullException the keys or values are null /// New Map with the items added [Pure] public Map AddOrUpdateRange(IEnumerable> range) => Wrap(Value.AddOrUpdateRange(range)); /// /// Atomically adds a range of items to the map. If any of the keys exist already /// then they're replaced. /// /// Null is not allowed for a Key or a Value /// Range of tuples to add /// Throws ArgumentNullException the keys or values are null /// New Map with the items added [Pure] public Map AddOrUpdateRange(IEnumerable<(K, V)> range) => Wrap(Value.AddOrUpdateRange(range)); /// /// Atomically adds a range of items to the map. If any of the keys exist already /// then they're replaced. /// /// Null is not allowed for a Key or a Value /// Range of KeyValuePairs to add /// Throws ArgumentNullException the keys or values are null /// New Map with the items added [Pure] public Map AddOrUpdateRange(IEnumerable> range) => Wrap(Value.AddOrUpdateRange(range)); /// /// Atomically removes an item from the map /// If the key doesn't exists, the request is ignored. /// /// Key /// New map with the item removed [Pure] public Map Remove(K key) => Wrap(Value.Remove(key)); /// /// Retrieve a value from the map by key /// /// Key to find /// Found value [Pure] public Option Find(K key) => Value.Find(key); /// /// Retrieve a value from the map by key as an enumerable /// /// Key to find /// Found value [Pure] public Seq FindSeq(K key) => Value.FindSeq(key); /// /// Retrieve a value from the map by key and pattern match the /// result. /// /// Key to find /// Found value [Pure] public R Find(K key, Func Some, Func None) => Value.Find(key, Some, None); /// /// Retrieve the value from previous item to specified key /// /// Key to find /// Found key/value [Pure] public Option<(K Key, V Value)> FindPredecessor(K key) => Value.FindPredecessor(key); /// /// Retrieve the value from exact key, or if not found, the previous item /// /// Key to find /// Found key/value [Pure] public Option<(K Key, V Value)> FindExactOrPredecessor(K key) => Value.FindOrPredecessor(key); /// /// Retrieve the value from next item to specified key /// /// Key to find /// Found key/value [Pure] public Option<(K Key, V Value)> FindSuccessor(K key) => Value.FindSuccessor(key); /// /// Retrieve the value from exact key, or if not found, the next item /// /// Key to find /// Found key/value [Pure] public Option<(K Key, V Value)> FindExactOrSuccessor(K key) => Value.FindOrSuccessor(key); /// /// Try to find the key in the map, if it doesn't exist, add a new /// item by invoking the delegate provided. /// /// Key to find /// Delegate to get the value /// Updated map and added value [Pure] public (Map Map, V Value) FindOrAdd(K key, Func None) => Value.FindOrAdd(key, None).Map((x, y) => (Wrap(x), y)); /// /// Try to find the key in the map, if it doesn't exist, add a new /// item provided. /// /// Key to find /// Delegate to get the value /// Updated map and added value [Pure] public (Map, V Value) FindOrAdd(K key, V value) => Value.FindOrAdd(key, value).Map((x, y) => (Wrap(x), y)); /// /// Try to find the key in the map, if it doesn't exist, add a new /// item by invoking the delegate provided. /// /// Key to find /// Delegate to get the value /// Updated map and added value [Pure] public (Map Map, Option Value) FindOrMaybeAdd(K key, Func> None) => Value.FindOrMaybeAdd(key, None).Map((x, y) => (Wrap(x), y)); /// /// Try to find the key in the map, if it doesn't exist, add a new /// item by invoking the delegate provided. /// /// Key to find /// Delegate to get the value /// Updated map and added value [Pure] public (Map Map, Option Value) FindOrMaybeAdd(K key, Option None) => Value.FindOrMaybeAdd(key, None).Map((x, y) => (Wrap(x), y)); /// /// Atomically updates an existing item /// /// Null is not allowed for a Key or a Value /// Key /// Value /// Throws ArgumentNullException the key or value are null /// New Map with the item added [Pure] public Map SetItem(K key, V value) => Wrap(Value.SetItem(key, value)); /// /// Retrieve a value from the map by key, map it to a new value, /// put it back. /// /// Key to set /// Throws ArgumentException if the item isn't found /// Throws Exception if Some returns null /// New map with the mapped value [Pure] public Map SetItem(K key, Func Some) => Wrap(Value.SetItem(key, Some)); /// /// Atomically updates an existing item, unless it doesn't exist, in which case /// it is ignored /// /// Null is not allowed for a Key or a Value /// Key /// Value /// Throws ArgumentNullException the value is null /// New Map with the item added [Pure] public Map TrySetItem(K key, V value) => Wrap(Value.TrySetItem(key, value)); /// /// Atomically sets an item by first retrieving it, applying a map, and then putting it back. /// Silently fails if the value doesn't exist /// /// Key to set /// delegate to map the existing value to a new one before setting /// Throws Exception if Some returns null /// Throws ArgumentNullException the key or value are null /// New map with the item set [Pure] public Map TrySetItem(K key, Func Some) => Wrap(Value.TrySetItem(key, Some)); /// /// Atomically sets an item by first retrieving it, applying a map, and then putting it back. /// Calls the None delegate to return a new map if the item can't be found /// /// Null is not allowed for a Key or a Value /// Key /// delegate to map the existing value to a new one before setting /// delegate to return a new map if the item can't be found /// Throws Exception if Some returns null /// Throws Exception if None returns null /// New map with the item set [Pure] public Map TrySetItem(K key, Func Some, Func, Map> None) => Wrap(Value.TrySetItem(key, Some, None)); /// /// Atomically adds a new item to the map. /// If the key already exists, the new item replaces it. /// /// Null is not allowed for a Key or a Value /// Key /// Value /// Throws ArgumentNullException the key or value are null /// New Map with the item added [Pure] public Map AddOrUpdate(K key, V value) => Wrap(Value.AddOrUpdate(key,value)); /// /// Retrieve a value from the map by key, map it to a new value, /// put it back. If it doesn't exist, add a new one based on None result. /// /// Key to find /// Throws Exception if None returns null /// Throws Exception if Some returns null /// New map with the mapped value [Pure] public Map AddOrUpdate(K key, Func Some, Func None) => Wrap(Value.AddOrUpdate(key, Some, None)); /// /// Retrieve a value from the map by key, map it to a new value, /// put it back. If it doesn't exist, add a new one based on None result. /// /// Key to find /// Throws ArgumentNullException if None is null /// Throws Exception if Some returns null /// New map with the mapped value [Pure] public Map AddOrUpdate(K key, Func Some, V None) => Wrap(Value.AddOrUpdate(key, Some, None)); /// /// Retrieve a range of values /// /// Range start (inclusive) /// Range to (inclusive) /// Throws ArgumentNullException the keyFrom or keyTo are null /// Range of values [Pure] public Iterable FindRange(K keyFrom, K keyTo) => Value.FindRange(keyFrom, keyTo); /// /// Retrieve a range of values /// /// Range start (inclusive) /// Range to (inclusive) /// Throws ArgumentNullException the keyFrom or keyTo are null /// Range of values [Pure] public Iterable<(K Key, V Value)> FindRangePairs(K keyFrom, K keyTo) => Value.FindRangePairs(keyFrom, keyTo); /// /// Skips 'amount' values and returns a new tree without the /// skipped values. /// /// Amount to skip /// New tree [Pure] public Iterable<(K Key, V Value)> Skip(int amount) => Value.Skip(amount); /// /// Checks for existence of a key in the map /// /// Key to check /// True if an item with the key supplied is in the map [Pure] public bool ContainsKey(K key) => Value.ContainsKey(key); /// /// Checks for existence of a key in the map /// /// Key to check /// True if an item with the key supplied is in the map [Pure] public bool Contains(K key, V value) => Value.Contains(key, value); /// /// Clears all items from the map /// /// Functionally equivalent to calling Map.empty as the original structure is untouched /// Empty map [Pure] public Map Clear() => Wrap(Value.Clear()); /// /// Atomically sets a series of items using the KeyValuePairs provided /// /// Items to set /// Throws ArgumentException if any of the keys aren't in the map /// New map with the items set [Pure] public Map SetItems(IEnumerable> items) => Wrap(Value.SetItems(items)); /// /// Atomically sets a series of items using the Tuples provided. /// /// Items to set /// Throws ArgumentException if any of the keys aren't in the map /// New map with the items set [Pure] public Map SetItems(IEnumerable> items) => Wrap(Value.SetItems(items)); /// /// Atomically sets a series of items using the Tuples provided. /// /// Items to set /// Throws ArgumentException if any of the keys aren't in the map /// New map with the items set [Pure] public Map SetItems(IEnumerable<(K, V)> items) => Wrap(Value.SetItems(items)); /// /// Atomically sets a series of items using the KeyValuePairs provided. If any of the /// items don't exist then they're silently ignored. /// /// Items to set /// New map with the items set [Pure] public Map TrySetItems(IEnumerable> items) => Wrap(Value.TrySetItems(items)); /// /// Atomically sets a series of items using the Tuples provided If any of the /// items don't exist then they're silently ignored. /// /// Items to set /// New map with the items set [Pure] public Map TrySetItems(IEnumerable> items) => Wrap(Value.TrySetItems(items)); /// /// Atomically sets a series of items using the Tuples provided If any of the /// items don't exist then they're silently ignored. /// /// Items to set /// New map with the items set [Pure] public Map TrySetItems(IEnumerable<(K, V)> items) => Wrap(Value.TrySetItems(items)); /// /// Atomically sets a series of items using the keys provided to find the items /// and the Some delegate maps to a new value. If the items don't exist then /// they're silently ignored. /// /// Keys of items to set /// Function map the existing item to a new one /// New map with the items set [Pure] public Map TrySetItems(IEnumerable keys, Func Some) => Wrap(Value.TrySetItems(keys, Some)); /// /// Atomically removes a set of keys from the map /// /// Keys to remove /// New map with the items removed [Pure] public Map RemoveRange(IEnumerable keys) => Wrap(Value.RemoveRange(keys)); /// /// Returns true if a Key/Value pair exists in the map /// /// Pair to find /// True if exists, false otherwise [Pure] public bool Contains(KeyValuePair pair) => Value.Contains(pair); /// /// Enumerable of map keys /// [Pure] public Iterable Keys => Value.Keys; /// /// Enumerable of map values /// [Pure] public Iterable Values => Value.Values; /// /// Map the map the a dictionary /// [Pure] public IDictionary ToDictionary(Func<(K Key, V Value), KR> keySelector, Func<(K Key, V Value), VR> valueSelector) where KR : notnull => Value.ToDictionary(keySelector, valueSelector); /// /// Enumerable of in-order tuples that make up the map /// /// Tuples [Pure] public Iterable<(K Key, V Value)> Pairs => Value.Pairs; /// /// Enumerable of in-order tuples that make up the map /// /// Tuples [Pure] [Obsolete("Use `Pairs` instead")] public Iterable<(K Key, V Value)> ValueTuples => Value.Pairs; /// /// GetEnumerator - IEnumerable interface /// [Pure] public IEnumerator<(K Key, V Value)> GetEnumerator() => Value.GetEnumerator(); /// /// GetEnumerator - IEnumerable interface /// [Pure] IEnumerator IEnumerable.GetEnumerator() => Value.GetEnumerator(); [Pure] public Seq<(K Key, V Value)> ToSeq() => AsIterable().ToSeq(); /// /// Format the collection as `[(key: value), (key: value), (key: value), ...]` /// The elipsis is used for collections over 50 items /// To get a formatted string with all the items, use `ToFullString` /// or `ToFullArrayString`. /// [Pure] public override string ToString() => CollectionFormat.ToShortArrayString(Pairs.Map(kv => $"({kv.Key}: {kv.Value})"), Count); /// /// Format the collection as `(key: value), (key: value), (key: value), ...` /// [Pure] public string ToFullString(string separator = ", ") => CollectionFormat.ToFullString(Pairs.Map(kv => $"({kv.Key}: {kv.Value})"), separator); /// /// Format the collection as `[(key: value), (key: value), (key: value), ...]` /// [Pure] public string ToFullArrayString(string separator = ", ") => CollectionFormat.ToFullArrayString(Pairs.Map(kv => $"({kv.Key}: {kv.Value})"), separator); [Pure] public Iterable<(K Key, V Value)> AsIterable() => Value.AsIterable(); [Pure] public IEnumerable<(K Key, V Value)> AsEnumerable() => Value.AsEnumerable(); internal Map SetRoot(MapItem root) => new(new MapInternal(root, Value.Rev)); [Pure] public static bool operator ==(Map lhs, Map rhs) => lhs.Value == rhs.Value; [Pure] public static bool operator !=(Map lhs, Map rhs) => !(lhs == rhs); [Pure] public static bool operator <(Map lhs, Map rhs) => lhs.CompareTo(rhs) < 0; [Pure] public static bool operator <=(Map lhs, Map rhs) => lhs.CompareTo(rhs) <= 0; [Pure] public static bool operator >(Map lhs, Map rhs) => lhs.CompareTo(rhs) > 0; [Pure] public static bool operator >=(Map lhs, Map rhs) => lhs.CompareTo(rhs) >= 0; [Pure] public Map Combine(Map y) => this + y; [Pure] public static Map operator +(Map lhs, Map rhs) => new(lhs.Value + rhs.Value); [Pure] public static Map operator -(Map lhs, Map rhs) => new(lhs.Value - rhs.Value); /// /// Equality of keys and values with `EqDefault〈V〉` used for values /// [Pure] public override bool Equals(object? obj) => obj is Map m && Equals(m); /// /// Equality of keys and values with `EqDefault〈V〉` used for values /// [Pure] public bool Equals(Map y) => Value.Equals>(y.Value); /// /// Equality of keys and values /// [Pure] public bool Equals(Map y) where EqV : Eq => Value.Equals(y.Value); /// /// Equality of keys only /// [Pure] public bool EqualsKeys(Map y) => Value.Equals>(y.Value); [Pure] public override int GetHashCode() => Value.GetHashCode(); [Pure] public int CompareTo(object? obj) => obj is Map t ? CompareTo(t) : 1; /// /// Impure iteration of the bound values in the structure /// /// /// Returns the original unmodified structure /// public Map Do(Action f) { Iter(f); return this; } /// /// Atomically maps the map to a new map /// /// Mapped items in a new map [Pure] [EditorBrowsable(EditorBrowsableState.Never)] public Map Select(Func mapper) => new (AsEnumerable().Select(kv => (kv.Key, mapper(kv.Value)))); /// /// Atomically maps the map to a new map /// /// Mapped items in a new map [Pure] [EditorBrowsable(EditorBrowsableState.Never)] public Map Select(Func mapper) => new (AsEnumerable().Select(kv => (kv.Key, mapper(kv.Key, kv.Value)))); /// /// Atomically filter out items that return false when a predicate is applied /// /// Predicate /// New map with items filtered [Pure] [EditorBrowsable(EditorBrowsableState.Never)] public Map Where(Func valuePred) => new (Value.Filter(valuePred)); /// /// Atomically filter out items that return false when a predicate is applied /// /// Predicate /// New map with items filtered [Pure] [EditorBrowsable(EditorBrowsableState.Never)] public Map Where(Func keyValuePred) => new (Value.Filter(keyValuePred)); /// /// Atomically filter out items that return false when a predicate is applied /// /// Predicate /// New map with items filtered [Pure] public Map Filter(Func valuePred) => new(Value.Filter(valuePred)); /// /// Atomically filter out items that return false when a predicate is applied /// /// Predicate /// New map with items filtered [Pure] public Map Filter(Func keyValuePred) => new(Value.Filter(keyValuePred)); /// /// Return true if all items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] public bool ForAll(Func pred) => MapModule.ForAll(Value.Root, pred); /// /// Return true if all items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] public bool ForAll(Func, bool> pred) => MapModule.ForAll(Value.Root, (k, v) => pred(new Tuple(k, v))); /// /// Return true if all items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] public bool ForAll(Func<(K Key, V Value), bool> pred) => MapModule.ForAll(Value.Root, (k, v) => pred((k, v))); /// /// Return true if *all* items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] public bool ForAll(Func, bool> pred) => MapModule.ForAll(Value.Root, (k, v) => pred(new KeyValuePair(k, v))); /// /// Return true if all items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] public bool ForAll(Func pred) => MapModule.ForAll(Value.Root, (_, v) => pred(v)); /// /// Return true if *any* items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] public bool Exists(Func pred) => MapModule.Exists(Value.Root, pred); /// /// Return true if *any* items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] public bool Exists(Func, bool> pred) => MapModule.Exists(Value.Root, (k, v) => pred(new Tuple(k, v))); /// /// Return true if *any* items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] public bool Exists(Func<(K, V), bool> pred) => MapModule.Exists(Value.Root, (k, v) => pred((k, v))); /// /// Return true if *any* items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] public bool Exists(Func, bool> pred) => MapModule.Exists(Value.Root, (k, v) => pred(new KeyValuePair(k, v))); /// /// Return true if *any* items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] public bool Exists(Func pred) => MapModule.Exists(Value.Root, (_, v) => pred(v)); /// /// Atomically iterate through all key/value pairs in the map (in order) and execute an /// action on each /// /// Action to execute /// Unit public Unit Iter(Action action) { foreach (var item in this) { action(item.Key, item.Value); } return unit; } /// /// Atomically iterate through all values in the map (in order) and execute an /// action on each /// /// Action to execute /// Unit public Unit Iter(Action action) { foreach (var item in this) { action(item.Value); } return unit; } /// /// Atomically iterate through all key/value pairs (as tuples) in the map (in order) /// and execute an action on each /// /// Action to execute /// Unit public Unit Iter(Action> action) { foreach (var item in this) { action(new Tuple(item.Key, item.Value)); } return unit; } /// /// Atomically iterate through all key/value pairs (as tuples) in the map (in order) /// and execute an action on each /// /// Action to execute /// Unit public Unit Iter(Action<(K, V)> action) { foreach (var item in this) { action(item); } return unit; } /// /// Atomically iterate through all key/value pairs in the map (in order) and execute an /// action on each /// /// Action to execute /// Unit public Unit Iter(Action> action) { foreach (var item in this) { action(new KeyValuePair(item.Key, item.Value)); } return unit; } /// /// Equivalent to map and filter but the filtering is done based on whether the returned /// Option is Some or None. If the item is None then it's filtered out, if not the the /// mapped Some value is used. /// /// Predicate /// Filtered map [Pure] public Map Choose(Func> selector) => new(Value.Choose(selector)); /// /// Equivalent to map and filter but the filtering is done based on whether the returned /// Option is Some or None. If the item is None then it's filtered out, if not the the /// mapped Some value is used. /// /// Predicate /// Filtered map [Pure] public Map Choose(Func> selector) => new(Value.Choose(selector)); /// /// Atomically folds all items in the map (in order) using the folder function provided. /// /// State type /// Initial state /// Fold function /// Folded state [Pure] public S Fold(S state, Func folder) => MapModule.Fold(Value.Root, state, folder); /// /// Atomically folds all items in the map (in order) using the folder function provided. /// /// State type /// Initial state /// Fold function /// Folded state [Pure] public S Fold(S state, Func folder) => MapModule.Fold(Value.Root, state, folder); [Pure] [Obsolete(Change.UseCollectionIntialiser)] public static implicit operator Map(ValueTuple<(K, V)> items) => [items.Item1]; [Pure] [Obsolete(Change.UseCollectionIntialiser)] public static implicit operator Map(((K, V), (K, V)) items) => [items.Item1, items.Item2]; [Pure] [Obsolete(Change.UseCollectionIntialiser)] public static implicit operator Map(((K, V), (K, V), (K, V)) items) => [items.Item1, items.Item2, items.Item3]; [Pure] [Obsolete(Change.UseCollectionIntialiser)] public static implicit operator Map(((K, V), (K, V), (K, V), (K, V)) items) => [items.Item1, items.Item2, items.Item3, items.Item4]; [Pure] [Obsolete(Change.UseCollectionIntialiser)] public static implicit operator Map(((K, V), (K, V), (K, V), (K, V), (K, V)) items) => [items.Item1, items.Item2, items.Item3, items.Item4, items.Item5]; [Pure] [Obsolete(Change.UseCollectionIntialiser)] public static implicit operator Map(((K, V), (K, V), (K, V), (K, V), (K, V), (K, V)) items) => [items.Item1, items.Item2, items.Item3, items.Item4, items.Item5, items.Item6]; [Pure] [Obsolete(Change.UseCollectionIntialiser)] public static implicit operator Map(((K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V)) items) => [items.Item1, items.Item2, items.Item3, items.Item4, items.Item5, items.Item6, items.Item7]; [Pure] [Obsolete(Change.UseCollectionIntialiser)] public static implicit operator Map(((K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V)) items) => [items.Item1, items.Item2, items.Item3, items.Item4, items.Item5, items.Item6, items.Item7, items.Item8]; [Pure] [Obsolete(Change.UseCollectionIntialiser)] public static implicit operator Map(((K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V)) items) => [items.Item1, items.Item2, items.Item3, items.Item4, items.Item5, items.Item6, items.Item7, items.Item8, items.Item9]; [Pure] [Obsolete(Change.UseCollectionIntialiser)] public static implicit operator Map(((K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V)) items) => [items.Item1, items.Item2, items.Item3, items.Item4, items.Item5, items.Item6, items.Item7, items.Item8, items.Item9, items.Item10]; [Pure] [Obsolete(Change.UseCollectionIntialiser)] public static implicit operator Map(((K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V)) items) => [items.Item1, items.Item2, items.Item3, items.Item4, items.Item5, items.Item6, items.Item7, items.Item8, items.Item9, items.Item10, items.Item11]; [Pure] [Obsolete(Change.UseCollectionIntialiser)] public static implicit operator Map(((K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V)) items) => [items.Item1, items.Item2, items.Item3, items.Item4, items.Item5, items.Item6, items.Item7, items.Item8, items.Item9, items.Item10, items.Item11, items.Item12]; [Pure] [Obsolete(Change.UseCollectionIntialiser)] public static implicit operator Map(((K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V)) items) => [items.Item1, items.Item2, items.Item3, items.Item4, items.Item5, items.Item6, items.Item7, items.Item8, items.Item9, items.Item10, items.Item11, items.Item12, items.Item13]; [Pure] [Obsolete(Change.UseCollectionIntialiser)] public static implicit operator Map(((K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V)) items) => [items.Item1, items.Item2, items.Item3, items.Item4, items.Item5, items.Item6, items.Item7, items.Item8, items.Item9, items.Item10, items.Item11, items.Item12, items.Item13, items.Item14]; [Pure] [Obsolete(Change.UseCollectionIntialiser)] public static implicit operator Map(((K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V)) items) => [items.Item1, items.Item2, items.Item3, items.Item4, items.Item5, items.Item6, items.Item7, items.Item8, items.Item9, items.Item10, items.Item11, items.Item12, items.Item13, items.Item14, items.Item15]; [Pure] [Obsolete(Change.UseCollectionIntialiser)] public static implicit operator Map(((K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V)) items) => [items.Item1, items.Item2, items.Item3, items.Item4, items.Item5, items.Item6, items.Item7, items.Item8, items.Item9, items.Item10, items.Item11, items.Item12, items.Item13, items.Item14, items.Item15, items.Item16]; /// /// Union two maps. The merge function is called keys are /// present in both map. /// [Pure] public Map Union(Map other, WhenMatched Merge) => Union(other, (_, v) => v, (_, v) => v, Merge); /// /// Union two maps. The merge function is called keys are /// present in both map. /// [Pure] public Map Union(Map other, WhenMissing MapRight, WhenMatched Merge) => Union(other, (_, v) => v, MapRight, Merge); /// /// Union two maps. The merge function is called keys are /// present in both map. /// [Pure] public Map Union(Map other, WhenMissing MapLeft, WhenMatched Merge) => Union(other, MapLeft, (_, v) => v, Merge); /// /// Union two maps. The merge function is called keys are /// present in both map. /// [Pure] public Map Union(Map other, WhenMissing MapLeft, WhenMissing MapRight, WhenMatched Merge) => new (Value.Union(other.Value, MapLeft, MapRight, Merge)); /// /// Intersect two maps. Only keys that are in both maps are /// returned. The merge function is called for every resulting /// key. /// [Pure] public Map Intersect(Map other, WhenMatched Merge) => new (Value.Intersect(other.Value, Merge)); /// /// Map differencing based on key. this - other. /// [Pure] public Map Except(Map other) => new (Value.Except(other.Value)); /// /// Keys that are in both maps are dropped and the remaining /// items are merged and returned. /// [Pure] public Map SymmetricExcept(Map other) => new (Value.SymmetricExcept(other.Value)); /// /// Compare keys and values (values use `OrdDefault〈V〉` for ordering) /// [Pure] public int CompareTo(Map other) => Value.CompareTo>(other.Value); /// /// Compare keys and values (values use `OrdV` for ordering) /// [Pure] public int CompareTo(Map other) where OrdV : Ord => Value.CompareTo(other.Value); /// /// Compare keys only /// [Pure] public int CompareKeysTo(Map other) => Value.CompareTo>(other.Value); /// /// Implicit conversion from an untyped empty list /// public static implicit operator Map(SeqEmpty _) => Empty; /// /// Creates a new map from a range/slice of this map /// /// Range start (inclusive) /// Range to (inclusive) /// [Pure] public Map Slice(K keyFrom, K keyTo) => new (FindRangePairs(keyFrom, keyTo)); /// /// Find the lowest ordered item in the map /// [Pure] public Option<(K Key, V Value)> Min => Value.Min; /// /// Find the highest ordered item in the map /// [Pure] public Option<(K Key, V Value)> Max => Value.Max; [Pure] IEnumerable IReadOnlyDictionary.Keys => Keys; [Pure] IEnumerable IReadOnlyDictionary.Values => Values; [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool TryGetValue(K key, out V value) { var v = Find(key); if (v.IsSome) { value = (V)v; return true; } else { value = default!; return false; } } [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] IEnumerator> IEnumerable>.GetEnumerator() => AsIterable().Map(p => new KeyValuePair(p.Key, p.Value)).GetEnumerator(); public static Map AdditiveIdentity => Empty; } ================================================ FILE: LanguageExt.Core/Immutable Collections/Map/Map.Prelude.mapapply.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public static partial class Prelude { /// /// Functor map operation /// /// /// Unwraps the value within the functor, passes it to the map function `f` provided, and /// then takes the mapped value and wraps it back up into a new functor. /// /// Functor to map /// Mapping function /// Mapped functor public static Map map(Func f, K, A> ma) => Functor.map(f, ma).As(); } ================================================ FILE: LanguageExt.Core/Immutable Collections/Map/Map.Trait.Implementations.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using LanguageExt.Traits; namespace LanguageExt; public partial class Map : Foldable>, Functor>, MonoidK> { static K, B> Functor>.Map(Func f, K, A> ma) { return new Map(Go()); IEnumerable<(Key, B)> Go() { foreach (var x in ma.As()) { yield return (x.Key, f(x.Value)); } } } static S Foldable>.FoldWhile(Func> f, Func<(S State, A Value), bool> predicate, S state, K, A> ta) { foreach (var x in ta.As()) { if (!predicate((state, x.Value))) return state; state = f(x.Value)(state); } return state; } static S Foldable>.FoldBackWhile(Func> f, Func<(S State, A Value), bool> predicate, S state, K, A> ta) { foreach (var x in ta.As().Value.Reverse()) { if (!predicate((state, x.Value))) return state; state = f(state)(x.Value); } return state; } static int Foldable>.Count(K, A> ta) => ta.As().Count; static bool Foldable>.IsEmpty(K, A> ta) => ta.As().IsEmpty; static Option Foldable>.Head(K, A> ta) => ta.As().Min.Map(kv => kv.Value); static Option Foldable>.Last(K, A> ta) => ta.As().Max.Map(kv => kv.Value); static Option Foldable>.Min(K, A> ta) => ta.As().Min.Map(kv => kv.Value); static Option Foldable>.Max(K, A> ta) => ta.As().Max.Map(kv => kv.Value); static K, A> SemigroupK>.Combine(K, A> lhs, K, A> rhs) => lhs.As() + rhs.As(); static K, A> MonoidK>.Empty() => Map.Empty; static Fold Foldable>.FoldStep(K, A> ta, S initialState) => ta.As().FoldStepValues(initialState); static Fold Foldable>.FoldStepBack(K, A> ta, S initialState) => ta.As().FoldStepBackValues(initialState); } ================================================ FILE: LanguageExt.Core/Immutable Collections/Map/Map.cs ================================================ #pragma warning disable CS0693 // Type parameter has the same name as the type parameter from outer type using LanguageExt.Traits; using static LanguageExt.Prelude; using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics.Contracts; using System.Numerics; using LanguageExt.ClassInstances; using System.Runtime.CompilerServices; namespace LanguageExt; public delegate B WhenMissing(K key, A value); public delegate C WhenMatched(K key, A left, B right); /// /// Immutable map /// AVL tree implementation /// AVL tree is a self-balancing binary search tree. /// [wikipedia.org/wiki/AVL_tree](http://en.wikipedia.org/wiki/AVL_tree) /// /// Key type /// Value type [Serializable] [CollectionBuilder(typeof(Map), nameof(Map.createRange))] public readonly struct Map : IReadOnlyDictionary, IEnumerable<(K Key, V Value)>, IComparable>, IComparable, IEquatable>, IComparisonOperators, Map, bool>, IAdditionOperators, Map, Map>, ISubtractionOperators, Map, Map>, IAdditiveIdentity, Map>, Monoid>, K, V> { public static Map Empty { get; } = new(MapInternal, K, V>.Empty); readonly MapInternal, K, V> value; [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static Map Wrap(MapInternal, K, V> map) => new (map); [MethodImpl(MethodImplOptions.AggressiveInlining)] public Map(IEnumerable<(K Key, V Value)> items) : this(items, true) { } [MethodImpl(MethodImplOptions.AggressiveInlining)] public Map(IEnumerable<(K Key, V Value)> items, bool tryAdd) => value = new MapInternal, K, V>( items, tryAdd ? MapModuleM.AddOpt.TryAdd : MapModuleM.AddOpt.ThrowOnDuplicate); [MethodImpl(MethodImplOptions.AggressiveInlining)] public Map(ReadOnlySpan<(K Key, V Value)> items) : this(items, true) { } [MethodImpl(MethodImplOptions.AggressiveInlining)] public Map(ReadOnlySpan<(K Key, V Value)> items, bool tryAdd) => value = new MapInternal, K, V>( items, tryAdd ? MapModuleM.AddOpt.TryAdd : MapModuleM.AddOpt.ThrowOnDuplicate); [MethodImpl(MethodImplOptions.AggressiveInlining)] internal Map(MapInternal, K, V> value) => this.value = value; [MethodImpl(MethodImplOptions.AggressiveInlining)] internal Map(MapItem root, bool rev) => value = new MapInternal, K, V>(root, rev); internal MapInternal, K, V> Value { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => value ?? MapInternal, K, V>.Empty; } /// /// Reference version for use in pattern-matching /// /// /// /// Empty collection = null /// Singleton collection = (K, V) /// More = ((K, V), Seq〈(K, V)〉) -- head and tail /// /// var res = list.Case switch /// { /// /// (var x, var xs) => ..., /// A value => ..., /// _ => ... /// } /// /// [Pure] public object? Case => IsEmpty ? null : AsIterable().ToSeq().Case; /// /// Item at index lens /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Lens, V> item(K key) => Lens, V>.New( Get: la => la[key], Set: a => la => la.AddOrUpdate(key, a) ); /// /// Item or none at index lens /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Lens, Option> itemOrNone(K key) => Lens, Option>.New( Get: la => la.Find(key), Set: a => la => a.Match(Some: x => la.AddOrUpdate(key, x), None: () => la.Remove(key)) ); /// /// Lens map /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Lens, Map> map(Lens lens) => Lens, Map>.New( Get: la => la.Map(lens.Get), Set: lb => la => { foreach (var item in lb) { la = la.AddOrUpdate(item.Key, lens.Set(item.Value, la[item.Key])); } return la; }); /// /// 'this' accessor /// /// Key /// value [Pure] public V this[K key] { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => Value[key]; } /// /// Is the map empty /// [Pure] public bool IsEmpty { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => value?.IsEmpty ?? true; } /// /// Number of items in the map /// [Pure] public int Count { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => value?.Count ?? 0; } /// /// Alias of Count /// [Pure] public int Length { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => value?.Count ?? 0; } /// /// Atomically adds a new item to the map /// /// Null is not allowed for a Key or a Value /// Key /// Value /// Throws ArgumentException if the key already exists /// Throws ArgumentNullException the key or value are null /// New Map with the item added [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Map Add(K key, V value) => Wrap(Value.Add(key,value)); /// /// Atomically adds a new item to the map. /// If the key already exists, then the new item is ignored /// /// Null is not allowed for a Key or a Value /// Key /// Value /// Throws ArgumentNullException the key or value are null /// New Map with the item added [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Map TryAdd(K key, V value) => Wrap(Value.TryAdd(key, value)); /// /// Atomically adds a new item to the map. /// If the key already exists then the Fail handler is called with the unaltered map /// and the value already set for the key, it expects a new map returned. /// /// Null is not allowed for a Key or a Value /// Key /// Value /// Delegate to handle failure, you're given the unaltered map /// and the value already set for the key /// Throws ArgumentNullException the key or value are null /// New Map with the item added [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Map TryAdd(K key, V value, Func, V, Map> Fail) => Wrap(Value.TryAdd(key, value, (m, v) => Fail(Wrap(m), v).Value)); /// /// Atomically adds a range of items to the map. /// /// Null is not allowed for a Key or a Value /// Range of tuples to add /// Throws ArgumentException if any of the keys already exist /// Throws ArgumentNullException the keys or values are null /// New Map with the items added [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Map AddRange(IEnumerable> range) => Wrap(Value.AddRange(range)); /// /// Atomically adds a range of items to the map. /// /// Null is not allowed for a Key or a Value /// Range of tuples to add /// Throws ArgumentException if any of the keys already exist /// Throws ArgumentNullException the keys or values are null /// New Map with the items added [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Map AddRange(IEnumerable<(K, V)> range) => Wrap(Value.AddRange(range)); /// /// Atomically adds a range of items to the map. If any of the keys exist already /// then they're ignored. /// /// Null is not allowed for a Key or a Value /// Range of tuples to add /// Throws ArgumentNullException the keys or values are null /// New Map with the items added [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Map TryAddRange(IEnumerable> range) => Wrap(Value.TryAddRange(range)); /// /// Atomically adds a range of items to the map. If any of the keys exist already /// then they're ignored. /// /// Null is not allowed for a Key or a Value /// Range of tuples to add /// Throws ArgumentNullException the keys or values are null /// New Map with the items added [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Map TryAddRange(IEnumerable<(K, V)> range) => Wrap(Value.TryAddRange(range)); /// /// Atomically adds a range of items to the map. If any of the keys exist already /// then they're ignored. /// /// Null is not allowed for a Key or a Value /// Range of KeyValuePairs to add /// Throws ArgumentNullException the keys or values are null /// New Map with the items added [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Map TryAddRange(IEnumerable> range) => Wrap(Value.TryAddRange(range)); /// /// Atomically adds a range of items to the map. If any of the keys exist already /// then they're replaced. /// /// Null is not allowed for a Key or a Value /// Range of tuples to add /// Throws ArgumentNullException the keys or values are null /// New Map with the items added [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Map AddOrUpdateRange(IEnumerable> range) => Wrap(Value.AddOrUpdateRange(range)); /// /// Atomically adds a range of items to the map. If any of the keys exist already /// then they're replaced. /// /// Null is not allowed for a Key or a Value /// Range of tuples to add /// Throws ArgumentNullException the keys or values are null /// New Map with the items added [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Map AddOrUpdateRange(IEnumerable<(K, V)> range) => Wrap(Value.AddOrUpdateRange(range)); /// /// Atomically adds a range of items to the map. If any of the keys exist already /// then they're replaced. /// /// Null is not allowed for a Key or a Value /// Range of KeyValuePairs to add /// Throws ArgumentNullException the keys or values are null /// New Map with the items added [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Map AddOrUpdateRange(IEnumerable> range) => Wrap(Value.AddOrUpdateRange(range)); /// /// Atomically removes an item from the map /// If the key doesn't exists, the request is ignored. /// /// Key /// New map with the item removed [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Map Remove(K key) => Wrap(Value.Remove(key)); /// /// Retrieve a value from the map by key /// /// Key to find /// Found value [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Option Find(K key) => Value.Find(key); /// /// Retrieve a value from the map by key as an enumerable /// /// Key to find /// Found value [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Seq FindSeq(K key) => Value.FindSeq(key); /// /// Retrieve a value from the map by key and pattern match the /// result. /// /// Key to find /// Found value [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public R Find(K key, Func Some, Func None) => Value.Find(key, Some, None); /// /// Retrieve the value from previous item to specified key /// /// Key to find /// Found key/value [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Option<(K Key, V Value)> FindPredecessor(K key) => Value.FindPredecessor(key); /// /// Retrieve the value from exact key, or if not found, the previous item /// /// Key to find /// Found key/value [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Option<(K Key, V Value)> FindExactOrPredecessor(K key) => Value.FindOrPredecessor(key); /// /// Retrieve the value from next item to specified key /// /// Key to find /// Found key/value [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Option<(K Key, V Value)> FindSuccessor(K key) => Value.FindSuccessor(key); /// /// Retrieve the value from exact key, or if not found, the next item /// /// Key to find /// Found key/value [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Option<(K Key, V Value)> FindExactOrSuccessor(K key) => Value.FindOrSuccessor(key); /// /// Try to find the key in the map, if it doesn't exist, add a new /// item by invoking the delegate provided. /// /// Key to find /// Delegate to get the value /// Updated map and added value [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public (Map Map, V Value) FindOrAdd(K key, Func None) => Value.FindOrAdd(key, None).Map((x, y) => (Wrap(x), y)); /// /// Try to find the key in the map, if it doesn't exist, add a new /// item provided. /// /// Key to find /// Delegate to get the value /// Updated map and added value [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public (Map, V Value) FindOrAdd(K key, V value) => Value.FindOrAdd(key, value).Map((x, y) => (Wrap(x), y)); /// /// Try to find the key in the map, if it doesn't exist, add a new /// item by invoking the delegate provided. /// /// Key to find /// Delegate to get the value /// Updated map and added value [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public (Map Map, Option Value) FindOrMaybeAdd(K key, Func> None) => Value.FindOrMaybeAdd(key, None).Map((x, y) => (Wrap(x), y)); /// /// Try to find the key in the map, if it doesn't exist, add a new /// item by invoking the delegate provided. /// /// Key to find /// Delegate to get the value /// Updated map and added value [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public (Map Map, Option Value) FindOrMaybeAdd(K key, Option None) => Value.FindOrMaybeAdd(key, None).Map((x, y) => (Wrap(x), y)); /// /// Atomically updates an existing item /// /// Null is not allowed for a Key or a Value /// Key /// Value /// Throws ArgumentNullException the key or value are null /// New Map with the item added [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Map SetItem(K key, V value) => Wrap(Value.SetItem(key, value)); /// /// Retrieve a value from the map by key, map it to a new value, /// put it back. /// /// Key to set /// Throws ArgumentException if the item isn't found /// Throws Exception if Some returns null /// New map with the mapped value [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Map SetItem(K key, Func Some) => Wrap(Value.SetItem(key, Some)); /// /// Atomically updates an existing item, unless it doesn't exist, in which case /// it is ignored /// /// Null is not allowed for a Key or a Value /// Key /// Value /// Throws ArgumentNullException the value is null /// New Map with the item added [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Map TrySetItem(K key, V value) => Wrap(Value.TrySetItem(key, value)); /// /// Atomically sets an item by first retrieving it, applying a map, and then putting it back. /// Silently fails if the value doesn't exist /// /// Key to set /// delegate to map the existing value to a new one before setting /// Throws Exception if Some returns null /// Throws ArgumentNullException the key or value are null /// New map with the item set [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Map TrySetItem(K key, Func Some) => Wrap(Value.TrySetItem(key, Some)); /// /// Atomically sets an item by first retrieving it, applying a map, and then putting it back. /// Calls the None delegate to return a new map if the item can't be found /// /// Null is not allowed for a Key or a Value /// Key /// delegate to map the existing value to a new one before setting /// delegate to return a new map if the item can't be found /// Throws Exception if Some returns null /// Throws Exception if None returns null /// New map with the item set [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Map TrySetItem(K key, Func Some, Func, Map> None) => Wrap(Value.TrySetItem(key, Some, None)); /// /// Atomically adds a new item to the map. /// If the key already exists, the new item replaces it. /// /// Null is not allowed for a Key or a Value /// Key /// Value /// Throws ArgumentNullException the key or value are null /// New Map with the item added [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Map AddOrUpdate(K key, V value) => Wrap(Value.AddOrUpdate(key,value)); /// /// Retrieve a value from the map by key, map it to a new value, /// put it back. If it doesn't exist, add a new one based on None result. /// /// Key to find /// Throws Exception if None returns null /// Throws Exception if Some returns null /// New map with the mapped value [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Map AddOrUpdate(K key, Func Some, Func None) => Wrap(Value.AddOrUpdate(key, Some, None)); /// /// Retrieve a value from the map by key, map it to a new value, /// put it back. If it doesn't exist, add a new one based on None result. /// /// Key to find /// Throws ArgumentNullException if None is null /// Throws Exception if Some returns null /// New map with the mapped value [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Map AddOrUpdate(K key, Func Some, V None) => Wrap(Value.AddOrUpdate(key, Some, None)); /// /// Retrieve a range of values /// /// Range start (inclusive) /// Range to (inclusive) /// Throws ArgumentNullException the keyFrom or keyTo are null /// Range of values [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Iterable FindRange(K keyFrom, K keyTo) => Value.FindRange(keyFrom, keyTo); /// /// Retrieve a range of values /// /// Range start (inclusive) /// Range to (inclusive) /// Throws ArgumentNullException the keyFrom or keyTo are null /// Range of key, values [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Iterable<(K Key, V Value)> FindRangePairs(K keyFrom, K keyTo) => Value.FindRangePairs(keyFrom, keyTo); /// /// Skips 'amount' values and returns a new tree without the /// skipped values. /// /// Amount to skip /// New tree [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Iterable<(K Key, V Value)> Skip(int amount) => Value.Skip(amount); /// /// Checks for existence of a key in the map /// /// Key to check /// True if an item with the key supplied is in the map [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool ContainsKey(K key) => Value.ContainsKey(key); /// /// Checks for existence of a key in the map /// /// Key to check /// True if an item with the key supplied is in the map [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Contains(K key, V value) => Value.Contains(key, value); /// /// Clears all items from the map /// /// Functionally equivalent to calling Map.empty as the original structure is untouched /// Empty map [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Map Clear() => Wrap(Value.Clear()); /// /// Atomically adds a range of items to the map /// /// Range of KeyValuePairs to add /// Throws ArgumentException if any of the keys already exist /// New Map with the items added [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Map AddRange(IEnumerable> pairs) => Wrap(Value.AddRange(pairs)); /// /// Atomically sets a series of items using the KeyValuePairs provided /// /// Items to set /// Throws ArgumentException if any of the keys aren't in the map /// New map with the items set [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Map SetItems(IEnumerable> items) => Wrap(Value.SetItems(items)); /// /// Atomically sets a series of items using the Tuples provided. /// /// Items to set /// Throws ArgumentException if any of the keys aren't in the map /// New map with the items set [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Map SetItems(IEnumerable> items) => Wrap(Value.SetItems(items)); /// /// Atomically sets a series of items using the Tuples provided. /// /// Items to set /// Throws ArgumentException if any of the keys aren't in the map /// New map with the items set [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Map SetItems(IEnumerable<(K, V)> items) => Wrap(Value.SetItems(items)); /// /// Atomically sets a series of items using the KeyValuePairs provided. If any of the /// items don't exist then they're silently ignored. /// /// Items to set /// New map with the items set [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Map TrySetItems(IEnumerable> items) => Wrap(Value.TrySetItems(items)); /// /// Atomically sets a series of items using the Tuples provided If any of the /// items don't exist then they're silently ignored. /// /// Items to set /// New map with the items set [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Map TrySetItems(IEnumerable> items) => Wrap(Value.TrySetItems(items)); /// /// Atomically sets a series of items using the Tuples provided If any of the /// items don't exist then they're silently ignored. /// /// Items to set /// New map with the items set [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Map TrySetItems(IEnumerable<(K, V)> items) => Wrap(Value.TrySetItems(items)); /// /// Atomically sets a series of items using the keys provided to find the items /// and the Some delegate maps to a new value. If the items don't exist then /// they're silently ignored. /// /// Keys of items to set /// Function map the existing item to a new one /// New map with the items set [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Map TrySetItems(IEnumerable keys, Func Some) => Wrap(Value.TrySetItems(keys, Some)); /// /// Atomically removes a set of keys from the map /// /// Keys to remove /// New map with the items removed [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Map RemoveRange(IEnumerable keys) => Wrap(Value.RemoveRange(keys)); /// /// Returns true if a Key/Value pair exists in the map /// /// Pair to find /// True if exists, false otherwise [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Contains(KeyValuePair pair) => Value.Contains(pair); /// /// Left/Node/Right traversal in stepped form /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Fold<(K, V), S> FoldStep(S initialState) => Value.FoldStep(initialState); /// /// Left/Node/Right traversal in stepped form /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Fold<(K, V), S> FoldStepBack(S initialState) => Value.FoldStepBack(initialState); /// /// Left/Node/Right traversal in stepped form /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Fold FoldStepKeys(S initialState) => Value.FoldStepKeys(initialState); /// /// Left/Node/Right traversal in stepped form /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Fold FoldStepBackKeys(S initialState) => Value.FoldStepBackKeys(initialState); /// /// Left/Node/Right traversal in stepped form /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Fold FoldStepValues(S initialState) => Value.FoldStepValues(initialState); /// /// Left/Node/Right traversal in stepped form /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Fold FoldStepBackValues(S initialState) => Value.FoldStepBackValues(initialState); /// /// Enumerable of map keys /// [Pure] public Iterable Keys { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => Value.Keys; } /// /// Enumerable of map values /// [Pure] public Iterable Values { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => Value.Values; } /// /// Map the map the a dictionary /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public IDictionary ToDictionary(Func<(K Key, V Value), KR> keySelector, Func<(K Key, V Value), VR> valueSelector) where KR : notnull => Value.ToDictionary(keySelector, valueSelector); /// /// Enumerable of in-order tuples that make up the map /// /// Tuples [Pure] public Iterable<(K Key, V Value)> Pairs { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => Value.Pairs; } /// /// Enumerable of in-order tuples that make up the map /// /// Tuples [Pure] [Obsolete("Use Pairs instead")] public Iterable<(K Key, V Value)> ValueTuples => Value.Pairs; /// /// GetEnumerator - IEnumerable interface /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public MapEnumerator GetEnumerator() => Value.GetEnumerator(); /// /// GetEnumerator - IEnumerable interface /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] IEnumerator<(K Key, V Value)> IEnumerable<(K Key, V Value)>.GetEnumerator() => Value.GetEnumerator(); /// /// GetEnumerator - IEnumerable interface /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] IEnumerator IEnumerable.GetEnumerator() => Value.GetEnumerator(); [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Seq<(K Key, V Value)> ToSeq() => AsIterable().ToSeq(); /// /// Format the collection as `[(key: value), (key: value), (key: value), ...]` /// The elipsis is used for collections over 50 items /// To get a formatted string with all the items, use `ToFullString` /// or `ToFullArrayString`. /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public override string ToString() => CollectionFormat.ToShortArrayString(AsIterable().Map(kv => $"({kv.Key}: {kv.Value})"), Count); /// /// Format the collection as `(key: value), (key: value), (key: value), ...` /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public string ToFullString(string separator = ", ") => CollectionFormat.ToFullString(AsIterable().Map(kv => $"({kv.Key}: {kv.Value})"), separator); /// /// Format the collection as `[(key: value), (key: value), (key: value), ...]` /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public string ToFullArrayString(string separator = ", ") => CollectionFormat.ToFullArrayString(AsIterable().Map(kv => $"({kv.Key}: {kv.Value})"), separator); [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Iterable<(K Key, V Value)> AsIterable() => Value.AsIterable(); [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public IEnumerable<(K Key, V Value)> AsEnumerable() => Value.AsEnumerable(); [MethodImpl(MethodImplOptions.AggressiveInlining)] internal Map SetRoot(MapItem root) => new (new MapInternal, K, V>(root, Value.Rev)); [Pure] [Obsolete(Change.UseCollectionIntialiser)] public static implicit operator Map(ValueTuple<(K, V)> items) => [items.Item1]; [Pure] [Obsolete(Change.UseCollectionIntialiser)] public static implicit operator Map(((K, V), (K, V)) items) => [items.Item1, items.Item2]; [Pure] [Obsolete(Change.UseCollectionIntialiser)] public static implicit operator Map(((K, V), (K, V), (K, V)) items) => [items.Item1, items.Item2, items.Item3]; [Pure] [Obsolete(Change.UseCollectionIntialiser)] public static implicit operator Map(((K, V), (K, V), (K, V), (K, V)) items) => [items.Item1, items.Item2, items.Item3, items.Item4]; [Pure] [Obsolete(Change.UseCollectionIntialiser)] public static implicit operator Map(((K, V), (K, V), (K, V), (K, V), (K, V)) items) => [items.Item1, items.Item2, items.Item3, items.Item4, items.Item5]; [Pure] [Obsolete(Change.UseCollectionIntialiser)] public static implicit operator Map(((K, V), (K, V), (K, V), (K, V), (K, V), (K, V)) items) => [items.Item1, items.Item2, items.Item3, items.Item4, items.Item5, items.Item6]; [Pure] [Obsolete(Change.UseCollectionIntialiser)] public static implicit operator Map(((K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V)) items) => [items.Item1, items.Item2, items.Item3, items.Item4, items.Item5, items.Item6, items.Item7]; [Pure] [Obsolete(Change.UseCollectionIntialiser)] public static implicit operator Map(((K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V)) items) => [items.Item1, items.Item2, items.Item3, items.Item4, items.Item5, items.Item6, items.Item7, items.Item8]; [Pure] [Obsolete(Change.UseCollectionIntialiser)] public static implicit operator Map(((K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V)) items) => [items.Item1, items.Item2, items.Item3, items.Item4, items.Item5, items.Item6, items.Item7, items.Item8, items.Item9]; [Pure] [Obsolete(Change.UseCollectionIntialiser)] public static implicit operator Map(((K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V)) items) => [items.Item1, items.Item2, items.Item3, items.Item4, items.Item5, items.Item6, items.Item7, items.Item8, items.Item9, items.Item10]; [Pure] [Obsolete(Change.UseCollectionIntialiser)] public static implicit operator Map(((K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V)) items) => [items.Item1, items.Item2, items.Item3, items.Item4, items.Item5, items.Item6, items.Item7, items.Item8, items.Item9, items.Item10, items.Item11]; [Pure] [Obsolete(Change.UseCollectionIntialiser)] public static implicit operator Map(((K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V)) items) => [items.Item1, items.Item2, items.Item3, items.Item4, items.Item5, items.Item6, items.Item7, items.Item8, items.Item9, items.Item10, items.Item11, items.Item12]; [Pure] [Obsolete(Change.UseCollectionIntialiser)] public static implicit operator Map(((K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V)) items) => [items.Item1, items.Item2, items.Item3, items.Item4, items.Item5, items.Item6, items.Item7, items.Item8, items.Item9, items.Item10, items.Item11, items.Item12, items.Item13]; [Pure] [Obsolete(Change.UseCollectionIntialiser)] public static implicit operator Map(((K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V)) items) => [items.Item1, items.Item2, items.Item3, items.Item4, items.Item5, items.Item6, items.Item7, items.Item8, items.Item9, items.Item10, items.Item11, items.Item12, items.Item13, items.Item14]; [Pure] [Obsolete(Change.UseCollectionIntialiser)] public static implicit operator Map(((K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V)) items) => [items.Item1, items.Item2, items.Item3, items.Item4, items.Item5, items.Item6, items.Item7, items.Item8, items.Item9, items.Item10, items.Item11, items.Item12, items.Item13, items.Item14, items.Item15]; [Pure] [Obsolete(Change.UseCollectionIntialiser)] public static implicit operator Map(((K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V)) items) => [items.Item1, items.Item2, items.Item3, items.Item4, items.Item5, items.Item6, items.Item7, items.Item8, items.Item9, items.Item10, items.Item11, items.Item12, items.Item13, items.Item14, items.Item15, items.Item16]; [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(Map lhs, Map rhs) => lhs.Equals(rhs); [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(Map lhs, Map rhs) => !(lhs == rhs); [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator <(Map lhs, Map rhs) => lhs.CompareTo(rhs) < 0; [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator <=(Map lhs, Map rhs) => lhs.CompareTo(rhs) <= 0; [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator >(Map lhs, Map rhs) => lhs.CompareTo(rhs) > 0; [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator >=(Map lhs, Map rhs) => lhs.CompareTo(rhs) >= 0; [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Map Combine(Map y) => this + y; [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Map operator +(Map lhs, Map rhs) => new(lhs.Value + rhs.Value); [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Map operator -(Map lhs, Map rhs) => new(lhs.Value - rhs.Value); /// /// Equality of keys and values with `EqDefault〈V〉` used for values /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public override bool Equals(object? obj) => obj is Map m && Equals(m); /// /// Equality of keys and values with `EqDefault〈V〉` used for values /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(Map y) => Value.Equals>(y.Value); /// /// Equality of keys and values /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(Map y) where EqV : Eq => Value.Equals(y.Value); /// /// Equality of keys only /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool EqualsKeys(Map y) => Value.Equals>(y.Value); [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public override int GetHashCode() => Value.GetHashCode(); [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public int CompareTo(object? obj) => obj is Map t ? CompareTo(t) : 1; /// /// Impure iteration of the bound values in the structure /// /// /// Returns the original unmodified structure /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public Map Do(Action f) { Iter(f); return this; } /// /// Atomically maps the map to a new map /// /// Mapped items in a new map [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Map Select(Func mapper) => new (AsIterable().Select(kv => (kv.Key, mapper(kv.Value)))); /// /// Atomically maps the map to a new map /// /// Mapped items in a new map [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Map Select(Func mapper) => new (AsIterable().Select(kv => (kv.Key, mapper(kv.Key, kv.Value)))); /// /// Atomically filter out items that return false when a predicate is applied /// /// Predicate /// New map with items filtered [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Map Where(Func valuePred) => new(Value.Filter(valuePred)); /// /// Atomically filter out items that return false when a predicate is applied /// /// Predicate /// New map with items filtered [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Map Where(Func keyValuePred) => new(Value.Filter(keyValuePred)); /// /// Atomically filter out items that return false when a predicate is applied /// /// Predicate /// New map with items filtered [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Map Filter(Func valuePred) => new(Value.Filter(valuePred)); /// /// Atomically filter out items that return false when a predicate is applied /// /// Predicate /// New map with items filtered [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Map Filter(Func keyValuePred) => new(Value.Filter(keyValuePred)); /// /// Return true if all items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool ForAll(Func pred) => MapModule.ForAll(Value.Root, pred); /// /// Return true if all items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool ForAll(Func, bool> pred) => MapModule.ForAll(Value.Root, (k, v) => pred(new Tuple(k, v))); /// /// Return true if all items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool ForAll(Func<(K Key, V Value), bool> pred) => MapModule.ForAll(Value.Root, (k, v) => pred((k, v))); /// /// Return true if *all* items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool ForAll(Func, bool> pred) => MapModule.ForAll(Value.Root, (k, v) => pred(new KeyValuePair(k, v))); /// /// Return true if all items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool ForAll(Func pred) => MapModule.ForAll(Value.Root, (_, v) => pred(v)); /// /// Return true if *any* items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Exists(Func pred) => MapModule.Exists(Value.Root, pred); /// /// Return true if *any* items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Exists(Func, bool> pred) => MapModule.Exists(Value.Root, (k, v) => pred(new Tuple(k, v))); /// /// Return true if *any* items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Exists(Func<(K, V), bool> pred) => MapModule.Exists(Value.Root, (k, v) => pred((k, v))); /// /// Return true if *any* items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Exists(Func, bool> pred) => MapModule.Exists(Value.Root, (k, v) => pred(new KeyValuePair(k, v))); /// /// Return true if *any* items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Exists(Func pred) => MapModule.Exists(Value.Root, (_, v) => pred(v)); /// /// Atomically iterate through all key/value pairs in the map (in order) and execute an /// action on each /// /// Action to execute /// Unit [MethodImpl(MethodImplOptions.AggressiveInlining)] public Unit Iter(Action action) { foreach (var item in this) { action(item.Key, item.Value); } return unit; } /// /// Atomically iterate through all values in the map (in order) and execute an /// action on each /// /// Action to execute /// Unit [MethodImpl(MethodImplOptions.AggressiveInlining)] public Unit Iter(Action action) { foreach (var item in this) { action(item.Value); } return unit; } /// /// Atomically iterate through all key/value pairs (as tuples) in the map (in order) /// and execute an action on each /// /// Action to execute /// Unit [MethodImpl(MethodImplOptions.AggressiveInlining)] public Unit Iter(Action> action) { foreach (var item in this) { action(new Tuple(item.Key, item.Value)); } return unit; } /// /// Atomically iterate through all key/value pairs (as tuples) in the map (in order) /// and execute an action on each /// /// Action to execute /// Unit [MethodImpl(MethodImplOptions.AggressiveInlining)] public Unit Iter(Action<(K, V)> action) { foreach (var item in this) { action(item); } return unit; } /// /// Atomically iterate through all key/value pairs in the map (in order) and execute an /// action on each /// /// Action to execute /// Unit [MethodImpl(MethodImplOptions.AggressiveInlining)] public Unit Iter(Action> action) { foreach (var item in this) { action(new KeyValuePair(item.Key, item.Value)); } return unit; } /// /// Equivalent to map and filter but the filtering is done based on whether the returned /// Option is Some or None. If the item is None then it's filtered out, if not the the /// mapped Some value is used. /// /// Predicate /// Filtered map [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Map Choose(Func> selector) => new (Value.Choose(selector)); /// /// Equivalent to map and filter but the filtering is done based on whether the returned /// Option is Some or None. If the item is None then it's filtered out, if not the the /// mapped Some value is used. /// /// Predicate /// Filtered map [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Map Choose(Func> selector) => new (Value.Choose(selector)); /// /// Atomically folds all items in the map (in order) using the folder function provided. /// /// State type /// Initial state /// Fold function /// Folded state [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public S Fold(S state, Func folder) => MapModule.Fold(Value.Root, state, folder); /// /// Atomically folds all items in the map (in order) using the folder function provided. /// /// State type /// Initial state /// Fold function /// Folded state [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public S Fold(S state, Func folder) => MapModule.Fold(Value.Root, state, folder); /// /// Union two maps. The merge function is called when keys are /// present in both map. /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Map Union(Map other, WhenMatched Merge) => Union(other, (_, v) => v, (_, v) => v, Merge); /// /// Union two maps. The merge function is called when keys are /// present in both map. /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Map Union(Map other, WhenMissing MapRight, WhenMatched Merge) => Union(other, (_, v) => v, MapRight, Merge); /// /// Union two maps. The merge function is called when keys are /// present in both map. /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Map Union(Map other, WhenMissing MapLeft, WhenMatched Merge) => Union(other, MapLeft, (_, v) => v, Merge); /// /// Union two maps. The merge function is called when keys are /// present in both map. /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Map Union(Map other, WhenMissing MapLeft, WhenMissing MapRight, WhenMatched Merge) => new (Value.Union(other.Value, MapLeft, MapRight, Merge)); /// /// Intersect two maps. Only keys that are in both maps are /// returned. The merge function is called for every resulting /// key. /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Map Intersect(Map other, WhenMatched Merge) => new (Value.Intersect(other.Value, Merge)); /// /// Map differencing based on key. this - other. /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Map Except(Map other) => Wrap(Value.Except(other.Value)); /// /// Keys that are in both maps are dropped and the remaining /// items are merged and returned. /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Map SymmetricExcept(Map other) => Wrap(Value.SymmetricExcept(other.Value)); /// /// Compare keys and values (values use `OrdDefault〈V〉` for ordering) /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public int CompareTo(Map other) => Value.CompareTo>(other.Value); /// /// Compare keys and values (values use `OrdV` for ordering) /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public int CompareTo(Map other) where OrdV : Ord => Value.CompareTo(other.Value); /// /// Compare keys only /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public int CompareKeysTo(Map other) => Value.CompareTo>(other.Value); /// /// Implicit conversion from an untyped empty list /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator Map(SeqEmpty _) => Empty; /// /// Creates a new map from a range/slice of this map /// /// Range start (inclusive) /// Range to (inclusive) /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Map Slice(K keyFrom, K keyTo) => new(FindRangePairs(keyFrom, keyTo)); /// /// Find the lowest ordered item in the map /// [Pure] public Option<(K Key, V Value)> Min => Value.Min; /// /// Find the highest ordered item in the map /// [Pure] public Option<(K Key, V Value)> Max => Value.Max; [Pure] IEnumerable IReadOnlyDictionary.Keys => Keys; [Pure] IEnumerable IReadOnlyDictionary.Values => Values; [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool TryGetValue(K key, out V value) { var v = Find(key); if (v.IsSome) { value = (V)v; return true; } else { value = default!; return false; } } [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] IEnumerator> IEnumerable>.GetEnumerator() => AsIterable().Map(p => new KeyValuePair(p.Key, p.Value)).GetEnumerator(); public static Map AdditiveIdentity => Empty; } ================================================ FILE: LanguageExt.Core/Immutable Collections/Prelude.Collections.cs ================================================ using System; using System.Linq; using System.Collections.Generic; using System.Diagnostics.Contracts; using LanguageExt.Traits; using LSeq = LanguageExt.Seq; #pragma warning disable CS0693 // Type parameter has the same name as the type parameter from outer type namespace LanguageExt; public static partial class Prelude { /// /// Represents an empty sequence /// public static readonly SeqEmpty Empty = new(); /// /// Construct a sequence from any value /// /// Type of the items in the sequence /// Head item in the sequence /// [Pure] public static Seq Cons(this A head) => LSeq.FromSingleValue(head); /// /// Construct a sequence from any value /// /// Type of the items in the sequence /// Head item in the sequence /// [Pure] public static Seq Cons(this A head, SeqEmpty empty) => LSeq.FromSingleValue(head); /// /// Construct a list from head and tail; head becomes the first item in /// the list. /// /// Type of the items in the sequence /// Head item in the sequence /// Tail of the sequence /// [Pure] public static Seq Cons(this A head, Seq tail) => tail.Cons(head); /// /// Construct a list from head and tail; head becomes the first item in /// the list. /// /// Type of the items in the sequence /// Head item in the sequence /// Tail of the sequence /// [Pure] public static Seq Cons(this A head, A[] tail) { if (tail.Length == 0) { return LSeq.FromSingleValue(head); } else { var data = new A[tail.Length + 1]; System.Array.Copy(tail, 0, data, 1, tail.Length); data[0] = head; return LSeq.FromArray(data); } } /// /// Construct a list from head and tail; head becomes the first item in /// the list. /// /// Type of the items in the sequence /// Head item in the sequence /// Tail of the sequence /// [Pure] public static Seq Cons(this A head, IEnumerable tail) => tail is Seq seq ? head.Cons(seq) : new Seq(tail).Cons(head); /// /// Construct a list from head and tail; head becomes the first item in /// the list. /// /// Type of the items in the sequence /// Head item in the sequence /// Tail of the sequence /// [Pure] public static Iterable Cons(this A head, Iterable tail) => tail.Cons(head); /// /// Construct a list from head and tail; head becomes the first item in /// the list. /// /// Type of the items in the sequence /// Head item in the sequence /// Tail of the sequence /// [Pure] public static Iterator Cons(this A head, Iterator tail) => Iterator.Cons(head, tail); /// /// Construct a list from head and tail; head becomes the first item in /// the list. /// /// Type of the items in the sequence /// Head item in the sequence /// Tail of the sequence /// [Pure] public static Iterator Cons(this A head, Func> tail) => Iterator.Cons(head, tail); /// /// Construct a list from head and tail; head becomes the first item in /// the list. /// /// Type of the items in the sequence /// Head item in the sequence /// Tail of the sequence /// [Pure] public static IteratorAsync Cons(this A head, IteratorAsync tail) => IteratorAsync.Cons(head, tail); /// /// Construct a list from head and tail; head becomes the first item in /// the list. /// /// Type of the items in the sequence /// Head item in the sequence /// Tail of the sequence /// [Pure] public static IteratorAsync Cons(this A head, Func> tail) => IteratorAsync.Cons(head, tail); /// /// Construct a list from head and tail; head becomes the first item in /// the list. /// /// Type of the items in the sequence /// Head item in the sequence /// Tail of the sequence /// [Pure] public static Lst Cons(this A head, Lst tail) => tail.Insert(0, head); /// /// Provide a sorted enumerable /// [Pure] public static IEnumerable Sort(this IEnumerable xs) where OrdA : Ord => xs.OrderBy(identity, OrdComparer.Default); /// /// Provide a sorted Seq /// [Pure] public static Seq Sort(this Seq xs) where OrdA : Ord => xs.OrderBy(identity, OrdComparer.Default).AsIterable().ToSeq(); /// /// Provide a sorted Lst /// [Pure] public static Lst Sort(this Lst xs) where OrdA : Ord => xs.OrderBy(identity, OrdComparer.Default).AsIterable().ToLst(); /// /// Provide a sorted Arr /// [Pure] public static Arr Sort(this Arr xs) where OrdA : Ord => xs.OrderBy(identity, OrdComparer.Default).AsIterable().ToArr(); /// /// Provide a sorted array /// [Pure] public static A[] Sort(this A[] xs) where OrdA : Ord => xs.OrderBy(identity, OrdComparer.Default).ToArray(); /// /// Forever sequence of units /// [Pure] public static IterableNE Units { get { return IterableNE.create(unit, Go().AsIterable()); IEnumerable Go() { while (true) yield return default; } } } /// /// Lazy sequence of natural numbers up to Int32.MaxValue /// [Pure] public static Iterable Naturals { get { return Go().AsIterable(); IEnumerable Go() { for (var i = 0; i < int.MaxValue; i++) { yield return i; } } } } /// /// Lazy sequence of natural numbers up to Int64.MaxValue /// [Pure] public static Iterable LongNaturals { get { return Go().AsIterable(); IEnumerable Go() { for (var i = 0L; i < long.MaxValue; i++) { yield return i; } } } } /// /// Lazily generate a range of integers. /// [Pure] public static Range Range(long from, long count, long step = 1) => LanguageExt.Range.fromCount(from, count, step); /// /// Lazily generate a range of integers. /// [Pure] public static Range Range(int from, int count, int step = 1) => LanguageExt.Range.fromCount(from, count, step); /// /// Lazily generate a range of chars. /// /// Remarks: /// Can go in a positive direction ('a'..'z') as well as negative ('z'..'a') /// [Pure] public static Range Range(char from, char to) => to >= from ? LanguageExt.Range.fromMinMax(from, to, (char)1) : LanguageExt.Range.fromMinMax(to, from, (char)1) switch { var r => r with { runRange = r.runRange.Reverse() } }; /// /// Create an immutable map /// [Pure] public static Map Map() => LanguageExt.Map.empty(); /// /// Create an immutable map /// [Pure] public static Map Map((K, V) head, params (K, V)[] tail) => LanguageExt.Map.create(head, tail); /// /// Create an immutable map /// [Pure] public static Map toMap(IEnumerable<(K, V)> items) => LanguageExt.Map.createRange(items); /// /// Create an immutable map /// [Pure] public static Map toMap(ReadOnlySpan<(K, V)> items) => LanguageExt.Map.createRange(items); /// /// Create an immutable map /// [Pure] public static Map Map() where OrdK : Ord => LanguageExt.Map.empty(); /// /// Create an immutable map /// [Pure] public static Map Map((K, V) head, params (K, V)[] tail) where OrdK : Ord => LanguageExt.Map.create(head, tail); /// /// Create an immutable map /// [Pure] public static Map toMap(IEnumerable<(K, V)> items) where OrdK : Ord => LanguageExt.Map.createRange(items); /// /// Create an immutable map /// [Pure] public static Map toMap(ReadOnlySpan<(K, V)> items) where OrdK : Ord => LanguageExt.Map.createRange(items); /// /// Create an immutable hash-map /// [Pure] public static HashMap HashMap() => LanguageExt.HashMap.empty(); /// /// Create an immutable hash-map /// [Pure] public static HashMap HashMap((K, V) head, params (K, V)[] tail) => LanguageExt.HashMap.create(head, tail); /// /// Create an immutable hash-map /// [Pure] public static HashMap toHashMap(IEnumerable<(K, V)> items) => LanguageExt.HashMap.createRange(items); /// /// Create an immutable hash-map /// [Pure] public static HashMap toHashMap(ReadOnlySpan<(K, V)> items) => LanguageExt.HashMap.createRange(items); /// /// Create an immutable hash-map /// [Pure] public static HashMap HashMap() where EqK : Eq => LanguageExt.HashMap.empty(); /// /// Create an immutable hash-map /// [Pure] public static HashMap HashMap((K, V) head, params (K, V)[] tail) where EqK : Eq => LanguageExt.HashMap.create(head, tail); /// /// Create an immutable hash-map /// [Pure] public static HashMap toHashMap(IEnumerable<(K, V)> items) where EqK : Eq => LanguageExt.HashMap.createRange(items); /// /// Create an immutable hash-map /// [Pure] public static HashMap toHashMap(ReadOnlySpan<(K, V)> items) where EqK : Eq => LanguageExt.HashMap.createRange(items); /// /// Create an immutable tracking hash-map /// [Pure] public static TrackingHashMap TrackingHashMap() => LanguageExt.TrackingHashMap.empty(); /// /// Create an immutable tracking hash-map /// [Pure] public static TrackingHashMap TrackingHashMap((K, V) head, params (K, V)[] tail) => LanguageExt.TrackingHashMap.create(head, tail); /// /// Create an immutable tracking hash-map /// [Pure] public static TrackingHashMap toTrackingHashMap(IEnumerable<(K, V)> items) => LanguageExt.TrackingHashMap.createRange(items); /// /// Create an immutable tracking hash-map /// [Pure] public static TrackingHashMap toTrackingHashMap(ReadOnlySpan<(K, V)> items) => LanguageExt.TrackingHashMap.createRange(items); /// /// Create an immutable tracking hash-map /// [Pure] public static TrackingHashMap TrackingHashMap() where EqK : Eq => LanguageExt.TrackingHashMap.empty(); /// /// Create an immutable tracking hash-map /// [Pure] public static TrackingHashMap TrackingHashMap((K, V) head, params (K, V)[] tail) where EqK : Eq => LanguageExt.TrackingHashMap.create(head, tail); /// /// Create an immutable tracking hash-map /// [Pure] public static TrackingHashMap toTrackingHashMap(IEnumerable<(K, V)> items) where EqK : Eq => LanguageExt.TrackingHashMap.createRange(items); /// /// Create an immutable tracking hash-map /// [Pure] public static TrackingHashMap toTrackingHashMap(ReadOnlySpan<(K, V)> items) where EqK : Eq => LanguageExt.TrackingHashMap.createRange(items); /// /// Create an immutable hash-map /// [Pure] public static AtomHashMap AtomHashMap() => LanguageExt.AtomHashMap.Empty; /// /// Create an immutable hash-map /// [Pure] public static AtomHashMap AtomHashMap((K, V) head, params (K, V)[] tail) => LanguageExt.HashMap.create(head, tail).ToAtom(); /// /// Create an immutable hash-map /// [Pure] public static AtomHashMap AtomHashMap(HashMap items) => new (items); /// /// Create an immutable hash-map /// [Pure] public static AtomHashMap toAtomHashMap(IEnumerable<(K, V)> items) => LanguageExt.HashMap.createRange(items).ToAtom(); /// /// Create an immutable hash-map /// [Pure] public static AtomHashMap toAtomHashMap(ReadOnlySpan<(K, V)> items) => LanguageExt.HashMap.createRange(items).ToAtom(); /// /// Create an immutable hash-map /// [Pure] public static AtomHashMap AtomHashMap() where EqK : Eq => LanguageExt.AtomHashMap.Empty; /// /// Create an immutable hash-map /// [Pure] public static AtomHashMap AtomHashMap((K, V) head, params (K, V)[] tail) where EqK : Eq => LanguageExt.HashMap.create(head, tail).ToAtom(); /// /// Create an immutable hash-map /// [Pure] public static AtomHashMap AtomHashMap(HashMap items) where EqK : Eq => new (items); /// /// Create an immutable hash-map /// [Pure] public static AtomHashMap toAtomHashMap(IEnumerable<(K, V)> items) where EqK : Eq => LanguageExt.HashMap.createRange(items).ToAtom(); /// /// Create an immutable hash-map /// [Pure] public static AtomHashMap toAtomHashMap(ReadOnlySpan<(K, V)> items) where EqK : Eq => LanguageExt.HashMap.createRange(items).ToAtom(); /// /// Create an immutable list /// [Pure] public static Lst List() => Lst.Empty; /// /// Create an immutable list /// [Pure] public static Lst List(T x, params T[] xs) { return new Lst(Yield()); IEnumerable Yield() { yield return x; foreach(var item in xs) { yield return item; } } } /// /// Create an immutable list /// [Pure] public static Lst toList(Arr items) => new (items.AsSpan()); /// /// Create an immutable list /// [Pure] public static Lst toList(IEnumerable items) => items is Lst lst ? lst : new Lst(items); /// /// Create an immutable list /// [Pure] public static Lst toList(ReadOnlySpan items) => new (items); /// /// Create an immutable array /// [Pure] public static Arr Array() => Arr.Empty; /// /// Create an immutable array /// [Pure] public static Arr Array(T x, params T[] xs) => new (x.Cons(xs).ToArray()); /// /// Create an immutable array /// [Pure] public static Arr toArray(IEnumerable items) => items is Arr arr ? arr : new Arr(items); /// /// Create an immutable array /// [Pure] public static Arr toArray(ReadOnlySpan items) => new (items); /// /// Create an immutable queue /// [Pure] public static Que Queue() => Que.Empty; /// /// Create an immutable queue /// [Pure] public static Que Queue(params T[] items) { var q = new QueInternal(); foreach (var item in items) { q = q.Enqueue(item); } return new Que(q); } /// /// Create an immutable queue /// [Pure] public static Que toQueue(IEnumerable items) => new (items); /// /// Create an immutable queue /// [Pure] public static Que toQueue(ReadOnlySpan items) => new (items); /// /// Create an immutable stack /// [Pure] public static Stck Stack() => new (); /// /// Create an immutable stack /// [Pure] public static Stck Stack(params T[] items) => new (items.AsSpan()); /// /// Create an immutable stack /// [Pure] public static Stck toStack(IEnumerable items) => items is Stck s ? s : new Stck(items); /// /// Create an immutable stack /// [Pure] public static Stck toStackRev(IEnumerable items) => new (items.Reverse()); /// /// Create an immutable map, updating duplicates so that the final value of any key is retained /// [Pure] public static Map toMapUpdate(IEnumerable<(K, V)> keyValues) => new(keyValues, false); /// /// Create an immutable map, updating duplicates so that the final value of any key is retained /// [Pure] public static Map toMapUpdate(ReadOnlySpan<(K, V)> keyValues) => new(keyValues, false); /// /// Create an immutable map, ignoring duplicates so the first value of any key is retained /// [Pure] public static Map toMapTry(IEnumerable<(K, V)> keyValues) => new(keyValues, false); /// /// Create an immutable map, ignoring duplicates so the first value of any key is retained /// [Pure] public static Map toMapTry(ReadOnlySpan<(K, V)> keyValues) => new(keyValues, false); /// /// Create an immutable set /// [Pure] public static Set Set() => LanguageExt.Set.create(); /// /// Create an immutable set /// [Pure] public static Set Set(T head, params T[] tail) => LanguageExt.Set.createRange(head.Cons(tail)); /// /// Create an immutable set /// [Pure] public static Set toSet(IEnumerable items) => items is Set s ? s : LanguageExt.Set.createRange(items); /// /// Create an immutable set /// [Pure] public static Set toSet(ReadOnlySpan items) => LanguageExt.Set.createRange(items); /// /// Create an immutable set /// [Pure] public static Set Set() where OrdT : Ord => LanguageExt.Set.create(); /// /// Create an immutable set /// [Pure] public static Set Set(T head, params T[] tail) where OrdT : Ord => LanguageExt.Set.createRange(head.Cons(tail)); /// /// Create an immutable set /// [Pure] public static Set toSet(IEnumerable items) where OrdT : Ord => items is Set s ? s : LanguageExt.Set.createRange(items); /// /// Create an immutable set /// [Pure] public static Set toSet(ReadOnlySpan items) where OrdT : Ord => LanguageExt.Set.createRange(items); /// /// Create an immutable hash-set /// [Pure] public static HashSet HashSet() => LanguageExt.HashSet.create(); /// /// Create an immutable hash-set /// [Pure] public static HashSet HashSet(T head, params T[] tail) => LanguageExt.HashSet.createRange(head.Cons(tail)); /// /// Create an immutable hash-set /// [Pure] public static HashSet toHashSet(IEnumerable items) => items is HashSet hs ? hs : LanguageExt.HashSet.createRange(items); /// /// Create an immutable hash-set /// [Pure] public static HashSet toHashSet(ReadOnlySpan items) => LanguageExt.HashSet.createRange(items); /// /// Create an immutable hash-set /// [Pure] public static HashSet HashSet() where EqT : Eq => LanguageExt.HashSet.create(); /// /// Create an immutable hash-set /// [Pure] public static HashSet HashSet(T head, params T[] tail) where EqT : Eq => LanguageExt.HashSet.createRange(head.Cons(tail)); /// /// Create an immutable hash-set /// [Pure] public static HashSet toHashSet(IEnumerable items) where EqT : Eq => items is HashSet hs ? hs : LanguageExt.HashSet.createRange(items); /// /// Create an immutable hash-set /// [Pure] public static HashSet toHashSet(ReadOnlySpan items) where EqT : Eq => LanguageExt.HashSet.createRange(items); /// /// Create a queryable /// [Pure] public static IQueryable Query(params T[] items) => toQuery(items); /// /// Convert to queryable /// [Pure] public static IQueryable toQuery(IEnumerable items) => items.AsQueryable(); /// /// Match empty list, or multi-item list /// /// Return value type /// Match for an empty list /// Match for a non-empty /// Result of match function invoked public static B match(IEnumerable list, Func Empty, Func, B> More) => toSeq(list).Match(Empty, More); /// /// List pattern matching /// [Pure] public static B match(IEnumerable list, Func Empty, Func, B> More) => toSeq(list).Match(Empty, More); /// /// List pattern matching /// [Pure] public static R match(IEnumerable list, Func Empty, Func One, Func, R> More) => toSeq(list).Match(Empty, One, More); [Pure] public static R match(Map map, K key, Func Some, Func None) => match(LanguageExt.Map.find(map, key), Some, None ); public static Unit match(Map map, K key, Action Some, Action None) => match(LanguageExt.Map.find(map, key), Some, None); [Pure] public static R match(HashMap map, K key, Func Some, Func None) => match(LanguageExt.HashMap.find(map, key), Some, None); public static Unit match(HashMap map, K key, Action Some, Action None) => match(LanguageExt.HashMap.find(map, key), Some, None); /// /// Construct an empty Iterable /// [Pure] public static Iterable Iterable() => LanguageExt.Iterable.Empty; /// /// Construct a sequence from an Enumerable /// [Pure] public static Iterable Iterable(ReadOnlySpan value) => LanguageExt.Iterable.FromSpan(value); /// /// Construct a sequence from an Enumerable /// [Pure] public static Iterable Iterable(A fst, A snd, params A[] rest) { return new IterableEnumerable(IO.pure(Yield())); IEnumerable Yield() { yield return fst; yield return snd; foreach (var value in rest) { yield return value; } } } /// /// Construct a sequence from an Enumerable /// Deals with `value == null` by returning `[]` and also memoizes the /// items in the enumerable as they're being consumed. /// [Pure] public static Iterable Iterable(IEnumerable? value) => value switch { null => LanguageExt.Iterable.Empty, Iterable iter => iter, Seq seq => seq.AsIterable(), Arr arr => arr.AsIterable(), A[] array => Iterable(array), _ => new IterableEnumerable(IO.pure(value)) }; [Pure] public static Iterable Iterable(IAsyncEnumerable? value) => value switch { null => LanguageExt.Iterable.Empty, _ => new IterableAsyncEnumerable(IO.pure(value)) }; /// /// Construct an empty Seq /// [Pure] public static Seq Seq() => Empty; /// /// Construct a sequence from any value /// /// var list = Seq1(124); /// /// [Pure] [Obsolete(Change.UseCollectionIntialiserSeq)] public static Seq Seq1(A value) { var arr = new A[4]; arr[2] = value; return new Seq(new SeqStrict(arr, 2, 1, 0, 0)); } /// /// Construct a singleton sequence from any value /// /// var list = Seq(1); /// /// [Pure] public static Seq Seq(A value) { var arr = new A[4]; arr[2] = value; return new Seq(new SeqStrict(arr, 2, 1, 0, 0)); } /// /// Construct a sequence from any value /// /// var list = Seq(1, 2); /// /// [Pure] public static Seq Seq(A a, A b) { var arr = new A[4]; arr[2] = a; arr[3] = b; return new Seq(new SeqStrict(arr, 2, 2, 0, 0)); } /// /// Construct a sequence from any value /// /// var list = Seq(1, 2, 3); /// /// [Pure] public static Seq Seq(A a, A b, A c) { var arr = new A[8]; arr[2] = a; arr[3] = b; arr[4] = c; return new Seq(new SeqStrict(arr, 2, 3, 0, 0)); } /// /// Construct a sequence from any value /// /// var list = Seq(1, 2, 3, 4); /// /// [Pure] public static Seq Seq(A a, A b, A c, A d) { var arr = new A[8]; arr[2] = a; arr[3] = b; arr[4] = c; arr[5] = d; return new Seq(new SeqStrict(arr, 2, 4, 0, 0)); } /// /// Construct a sequence from any value /// /// var list = Seq(1, 2, 3, 4, 5); /// /// [Pure] public static Seq Seq(A a, A b, A c, A d, A e) { var arr = new A[8]; arr[2] = a; arr[3] = b; arr[4] = c; arr[5] = d; arr[6] = e; return new Seq(new SeqStrict(arr, 2, 5, 0, 0)); } /// /// Construct a sequence from any value /// /// var list = Seq(1, 2, 3, 4, 5, 6); /// /// [Pure] public static Seq Seq(A a, A b, A c, A d, A e, A f) { var arr = new A[16]; arr[4] = a; arr[5] = b; arr[6] = c; arr[7] = d; arr[8] = e; arr[9] = f; return new Seq(new SeqStrict(arr, 4, 6, 0, 0)); } /// /// Construct a sequence from any value /// /// var list = Seq(1, 2, 3, 4, 5, 6, 7); /// /// [Pure] public static Seq Seq(A a, A b, A c, A d, A e, A f, A g) { var arr = new A[16]; arr[4] = a; arr[5] = b; arr[6] = c; arr[7] = d; arr[8] = e; arr[9] = f; arr[10] = g; return new Seq(new SeqStrict(arr, 4, 7, 0, 0)); } /// /// Construct a sequence from any value /// /// var list = Seq(1, 2, 3, 4, 5, 6, 7, 8); /// /// [Pure] public static Seq Seq(A a, A b, A c, A d, A e, A f, A g, A h) { var arr = new A[16]; arr[4] = a; arr[5] = b; arr[6] = c; arr[7] = d; arr[8] = e; arr[9] = f; arr[10] = g; arr[11] = h; return new Seq(new SeqStrict(arr, 4, 8, 0, 0)); } /// /// Construct a sequence from any value /// /// var list = Seq(1, 2, 3, 4); /// /// [Pure] public static Seq Seq(A a, A b, A c, A d, A e, A f, A g, A h, params A[] tail) { var arr = new A[16 + tail.Length]; arr[4] = a; arr[5] = b; arr[6] = c; arr[7] = d; arr[8] = e; arr[9] = f; arr[10] = g; arr[11] = h; System.Array.Copy(tail, 0, arr, 12, tail.Length); return new Seq(new SeqStrict(arr, 4, 8 + tail.Length, 0, 0)); } /// /// Construct a sequence from an Enumerable /// Deals with `value == null` by returning `[]` and also memoizes the /// items in the enumerable as they're being consumed. /// [Pure] public static Seq Seq(ReadOnlySpan value) => new (value); /// /// Construct a sequence from an Enumerable /// Deals with `value == null` by returning `[]` and also memoizes the /// items in the enumerable as they're being consumed. /// [Pure] public static Seq Seq(IEnumerable? value) => value switch { null => Empty, Seq seq => seq, Arr arr => toSeq(arr), A[] array => toSeq(array), IList list => toSeq(list), ICollection coll => toSeq(coll), _ => new Seq(value) }; /// /// Construct a sequence from an Enumerable /// Deals with `value == null` by returning `[]` and also memoizes the /// items in the enumerable as they're being consumed. /// [Pure] public static Seq toSeq(IEnumerable? value) => value switch { null => Empty, Seq seq => seq, Arr arr => toSeq(arr.ToArray()), A[] array => toSeq(array), IList list => toSeq(list), ICollection coll => toSeq(coll), _ => new Seq(value) }; /// /// Construct a sequence from a nullable /// HasValue == true : [x] /// HasValue == false : [] /// [Pure] public static Seq toSeq(A? value) where A : struct => value is null ? Empty : LSeq.FromSingleValue(value.Value); /// /// Construct a sequence from an array /// [Pure] public static Seq toSeq(A[]? value) { if (value is null || value.Length == 0) { return Empty; } else { var length = value.Length; var data = new A[length]; System.Array.Copy(value, data, length); return LSeq.FromArray(data); } } /// /// Construct a sequence from a list /// [Pure] public static Seq toSeq(IList? value) => toSeq(value?.ToArray()); /// /// Construct a sequence from a list /// [Pure] public static Seq toSeq(ICollection? value) => toSeq(value?.ToArray()); /// /// Construct a sequence from an either /// Right(x) : [x] /// Left(y) : [] /// [Pure] public static Seq toSeq(Either value) => value.IsRight ? LSeq.FromSingleValue(value.RightValue) : Empty; /// /// Construct a sequence from a tuple /// [Pure] public static Seq toSeq(ValueTuple tup) => [tup.Item1]; /// /// Construct a sequence from a tuple /// [Pure] public static Seq toSeq(ValueTuple tup) => [tup.Item1, tup.Item2]; /// /// Construct a sequence from a tuple /// [Pure] public static Seq toSeq(ValueTuple tup) => [tup.Item1, tup.Item2, tup.Item3]; /// /// Construct a sequence from a tuple /// [Pure] public static Seq toSeq(ValueTuple tup) => [tup.Item1, tup.Item2, tup.Item3, tup.Item4]; /// /// Construct a sequence from a tuple /// [Pure] public static Seq toSeq(ValueTuple tup) => [tup.Item1, tup.Item2, tup.Item3, tup.Item4, tup.Item5]; /// /// Construct a sequence from a tuple /// [Pure] public static Seq toSeq(ValueTuple tup) => [tup.Item1, tup.Item2, tup.Item3, tup.Item4, tup.Item5, tup.Item6]; /// /// Construct a sequence from a tuple /// [Pure] public static Seq toSeq(ValueTuple tup) => [tup.Item1, tup.Item2, tup.Item3, tup.Item4, tup.Item5, tup.Item6, tup.Item7]; /// /// Construct an empty AtomSeq /// [Pure] public static AtomSeq AtomSeq() => new (SeqStrict.Empty); /// /// Construct an AtomSeq /// [Pure] public static AtomSeq AtomSeq(params A[] items) => new (LSeq.FromArray(items).Value); /// /// Construct an AtomSeq /// [Pure] public static AtomSeq AtomSeq(Seq items) => new (items.Value); /// /// Construct an AtomSeq /// [Pure] public static AtomSeq AtomSeq(IEnumerable items) => new (items); /// /// Compute the difference between two documents, using the Wagner-Fischer algorithm. /// O(mn) time and space. /// /// apply(diff(d,e), d) == e /// /// diff(d, d) == Patch.empty /// /// apply(diff(d, e), d) == apply(inverse(diff(e, d)), d) /// /// apply(append(diff(a, b), diff(b, c), a) == apply(diff(a, c), a) /// /// applicable(diff(a, b) a) /// /// public static Patch Diff(this IEnumerable va, IEnumerable vb) where EqA : Eq => Patch.diff(va, vb); /// /// Apply the supplied patch to this collection /// public static IEnumerable Apply(this IEnumerable va, Patch patch) where EqA : Eq => Patch.apply(patch, va); /// /// Apply the supplied patch to this collection /// public static Lst Apply(this Lst va, Patch patch) where EqA : Eq => Patch.apply(patch, va); /// /// Apply the supplied patch to this collection /// public static Seq Apply(this Seq va, Patch patch) where EqA : Eq => Patch.apply(patch, va); /// /// Apply the supplied patch to this collection /// public static SpanArray Apply(this SpanArray va, Patch patch) where EqA : Eq => Patch.apply(patch, va); /// /// Apply the supplied patch to this collection /// public static A[] Apply(this A[] va, Patch patch) where EqA : Eq => Patch.apply(patch, va); /// /// Apply the supplied patch to this collection /// public static Arr Apply(this Arr va, Patch patch) where EqA : Eq => Patch.apply(patch, va); /// /// Apply the supplied patch to this collection /// public static List Apply(this List va, Patch patch) where EqA : Eq => Patch.apply(patch, va); /// /// Returns true if a patch can be safely applied to a document, that is, /// `applicable(p, d)` holds when `d` is a valid source document for the patch `p`. /// public static bool Applicable(this IEnumerable va, Patch patch) where EqA : Eq => Patch.applicable(patch, va); } ================================================ FILE: LanguageExt.Core/Immutable Collections/Queue/Que.Internal.cs ================================================ using System; using System.Collections; using System.Collections.Generic; using static LanguageExt.Prelude; using System.Diagnostics.Contracts; using LanguageExt.ClassInstances; namespace LanguageExt; [Serializable] internal class QueInternal : IEnumerable { public static readonly QueInternal Empty = new (); readonly StckInternal forward; readonly StckInternal backward; StckInternal? backwardRev; int hashCode; internal QueInternal() { forward = StckInternal.Empty; backward = StckInternal.Empty; } internal QueInternal(IEnumerable items) { var q = new QueInternal(); foreach(var item in items) { q = q.Enqueue(item); } forward = q.forward; backward = q.backward; backwardRev = q.backwardRev; } internal QueInternal(ReadOnlySpan items) { var q = new QueInternal(); foreach(var item in items) { q = q.Enqueue(item); } forward = q.forward; backward = q.backward; backwardRev = q.backwardRev; } private QueInternal(StckInternal f, StckInternal b) { forward = f; backward = b; } private StckInternal BackwardRev => backwardRev ??= backward.Reverse(); [Pure] public int Count => forward.Count + backward.Count; [Pure] public bool IsEmpty => forward.IsEmpty && backward.IsEmpty; [Pure] public QueInternal Clear() => Empty; [Pure] public A Peek() => forward.Peek(); [Pure] public QueInternal Dequeue() { var f = forward.Pop(); if (!f.IsEmpty) { return new QueInternal(f, backward); } if (backward.IsEmpty) { return Empty; } return new QueInternal(BackwardRev, StckInternal.Empty); } [Pure] public QueInternal Dequeue(out A outValue) { outValue = Peek(); return Dequeue(); } [Pure] public (QueInternal, Option) TryDequeue() => forward.TryPeek().Match( Some: x => (Dequeue(), Some(x)), None: () => (this, Option.None) ); [Pure] public Option TryPeek() => forward.TryPeek(); [Pure] public QueInternal Enqueue(A value) => IsEmpty ? new QueInternal(StckInternal.Empty.Push(value), StckInternal.Empty) : new QueInternal(forward, backward.Push(value)); [Pure] public Seq ToSeq() => toSeq(forward.AsIterable().ConcatFast(BackwardRev)); [Pure] public Iterable AsIterable() => forward.AsIterable().ConcatFast(BackwardRev); [Pure] public IEnumerator GetEnumerator() => forward.AsIterable().ConcatFast(BackwardRev).GetEnumerator(); [Pure] IEnumerator IEnumerable.GetEnumerator() => forward.AsIterable().ConcatFast(BackwardRev).GetEnumerator(); [Pure] public static QueInternal operator +(QueInternal lhs, QueInternal rhs) => lhs.Combine(rhs); [Pure] public QueInternal Combine(QueInternal rhs) { var self = this; foreach (var item in rhs) { self = self.Enqueue(item); } return self; } [Pure] public override int GetHashCode() => hashCode == 0 ? hashCode = FNV32.Hash, A>(this) : hashCode; } ================================================ FILE: LanguageExt.Core/Immutable Collections/Queue/Que.cs ================================================ using System; using System.Linq; using System.Collections; using System.Collections.Generic; using static LanguageExt.Prelude; using System.Diagnostics.Contracts; using System.Runtime.CompilerServices; using LanguageExt.ClassInstances; using LanguageExt.Common; namespace LanguageExt; /// /// Immutable queue collection /// /// Item value type [Serializable] [CollectionBuilder(typeof(Queue), nameof(Queue.createRange))] public readonly struct Que : IEnumerable, IEquatable> { public static readonly Que Empty = new (QueInternal.Empty); readonly QueInternal value; internal QueInternal Value => value ?? QueInternal.Empty; internal Que(QueInternal value) => this.value = value; /// /// Construct from a enumerable of items /// /// Items to construct the queue from public Que(IEnumerable items) => value = new QueInternal(items); /// /// Construct from a enumerable of items /// /// Items to construct the queue from public Que(ReadOnlySpan items) => value = new QueInternal(items); /// /// Impure iteration of the bound value in the structure /// /// /// Returns the original unmodified structure /// public Que Do(Action f) { this.Iter(f); return this; } /// /// Reference version for use in pattern-matching /// /// /// /// Empty collection = null /// Singleton collection = A /// More = (A, Seq〈A〉) -- head and tail /// /// var res = list.Case switch /// { /// /// (var x, var xs) => ..., /// A value => ..., /// _ => ... /// } /// /// [Pure] public object? Case => IsEmpty ? null : toSeq(Value).Case; /// /// Is the queue empty /// [Pure] public bool IsEmpty { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => value?.IsEmpty ?? true; } /// /// Number of items in the queue /// [Pure] public int Count { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => value?.Count ?? 0; } /// /// Alias of Count /// [Pure] public int Length { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => value?.Count ?? 0; } /// /// Returns an empty queue /// /// Empty queue [Pure] public Que Clear() => Empty; /// /// Look at the item at the front of the queue /// /// Throws if the queue is empty /// The item at the front of the queue, or throw an exception if none exists [Pure] public A Peek() => Value.Peek(); /// /// Removes the item from the front of the queue /// /// A new `Que` with the first item removed [Pure] public Que Dequeue() => new (Value.Dequeue()); /// /// Removes the item from the front of the queue /// /// Throws if the queue is empty /// A tuple containing the new `Que` with the first item removed and the first item [Pure] public (Que Queue, A Value) DequeueUnsafe() => (Dequeue(), Peek()); /// /// Removes the item from the front of the queue /// /// A tuple containing the new `Que` with the first item removed and optionally the first item [Pure] public (Que Queue, Option Value) TryDequeue() => Value.TryDequeue().MapFirst(qi => new Que(qi)); /// /// Look at the item at the front of the queue, if it exists. /// /// The item at the front of the queue, if it exists. `None` otherwise. [Pure] public Option TryPeek() => Value.TryPeek(); /// /// Add an item to the end of the queue /// /// Value to add to the queue /// A new queue with the item added [Pure] public Que Enqueue(A value) => new (Value.Enqueue(value)); /// /// Convert to a `Seq` /// /// `Seq` [Pure] public Seq ToSeq() => Value.ToSeq(); /// /// Convert to an `IEnumerable` /// /// `IEnumerable` [Pure] public Iterable AsIterable() => Value.AsIterable(); /// /// Get an enumerator of the collection /// /// `IEnumerator` [Pure] public IEnumerator GetEnumerator() => AsIterable().GetEnumerator(); /// /// Get an enumerator of the collection /// /// `IEnumerator` [Pure] IEnumerator IEnumerable.GetEnumerator() => AsIterable().GetEnumerator(); /// /// Append two queues together /// /// First part of the queue /// Second part of the queue /// Concatenated queue [Pure] public static Que operator +(Que lhs, Que rhs) => lhs.Combine(rhs); /// /// Append two queues together /// /// Second part of the queue /// Concatenated queue [Pure] public Que Combine(Que rhs) => new(IterableExtensions.AsIterable(Value).Combine(IterableExtensions.AsIterable(rhs))); /// /// Subtract one queue from another /// /// Starting queue /// Items to remove from the queue /// lhs - rhs [Pure] public static Que operator -(Que lhs, Que rhs) => lhs.Subtract(rhs); /// /// Subtract one queue from another /// /// Items to remove from the queue /// lhs - rhs [Pure] public Que Subtract(Que rhs) => new (Enumerable.Except(Value.AsIterable(), rhs.Value.AsIterable())); /// /// Queue equality /// /// First queue /// Second queue /// `true` if the queues are equal [Pure] public static bool operator ==(Que lhs, Que rhs) => lhs.Equals(rhs); /// /// Queue inequality /// /// First queue /// Second queue /// `true` if the queues are not-equal [Pure] public static bool operator !=(Que lhs, Que rhs) => !(lhs == rhs); /// /// Hash code of the items in the queue /// /// Hash code [Pure] public override int GetHashCode() => Value.GetHashCode(); /// /// Queue equality /// /// Value that may be a queue /// `true` if the queues are equal [Pure] public override bool Equals(object? obj) => obj is Que q && Equals(q); /// /// Queue equality /// /// Second queue /// `true` if the queues are equal [Pure] public bool Equals(Que other) => GetHashCode() == other.GetHashCode() && EqEnumerable.Equals(Value, other.Value); /// /// Implicit conversion from an untyped empty list /// public static implicit operator Que(SeqEmpty _) => Empty; } ================================================ FILE: LanguageExt.Core/Immutable Collections/Queue/Queue.Module.cs ================================================ using System; using System.Collections.Generic; using static LanguageExt.Prelude; using System.Diagnostics.Contracts; using LanguageExt.Traits; namespace LanguageExt; public static class Queue { [Pure] public static Que singleton(T item) => [item]; [Pure] public static Que createRange(IEnumerable items) => new (items); [Pure] public static Que createRange(ReadOnlySpan items) => items.IsEmpty ? Que.Empty : new (items); [Pure] public static Que enq(Que queue, T value) => queue.Enqueue(value); [Pure] public static (Que Queue, T Value) deqUnsafe(Que queue) => queue.DequeueUnsafe(); [Pure] public static (Que Queue, Option Value) deq(Que queue) => queue.TryDequeue(); [Pure] public static T peekUnsafe(Que queue) => queue.Peek(); [Pure] public static Option peek(Que queue) => queue.TryPeek(); [Pure] public static Que clear(Que queue) => queue.Clear(); [Pure] public static Que map(Que queue, Func map) => queue.Map(map); [Pure] public static Que filter(Que queue, Func predicate) => queue.Filter(predicate); [Pure] public static Que choose(Que queue, Func> selector) => queue.Choose(selector); [Pure] public static Que choose(Que queue, Func> selector) => queue.Choose(selector); [Pure] public static Que collect(Que queue, Func> map) => queue.Collect(map); [Pure] public static Que rev(Que queue) => queue.Rev(); [Pure] public static Que append(Que lhs, IEnumerable rhs) => lhs.Append(rhs); /// /// Folds each value of the QueT into an S. /// [wikipedia.org/wiki/Fold_(higher-order_function)](https://en.wikipedia.org/wiki/Fold_(higher-order_function)) /// /// Queue to fold /// Initial state /// Fold function /// Folded state [Pure] public static S fold(Que queue, S state, Func folder) => List.fold(queue, state, folder); /// /// Folds each value of the QueT into an S, but in reverse order. /// [wikipedia.org/wiki/Fold_(higher-order_function)](https://en.wikipedia.org/wiki/Fold_(higher-order_function)) /// /// Queue to fold /// Initial state /// Fold function /// Folded state [Pure] public static S foldBack(Que queue, S state, Func folder) => List.foldBack(queue, state, folder); [Pure] public static T reduce(Que queue, Func reducer) => List.reduce(queue, reducer); [Pure] public static T reduceBack(Que queue, Func reducer) => List.reduceBack(queue, reducer); [Pure] public static IEnumerable scan(Que queue, S state, Func folder) => List.scan(queue, state, folder); [Pure] public static IEnumerable scanBack(Que queue, S state, Func folder) => List.scanBack(queue, state, folder); [Pure] public static Option find(Que queue, Func pred) => List.find(queue, pred); [Pure] public static Que zip(Que queue, IEnumerable other, Func zipper) => toQueue(List.zip(queue, other, zipper)); [Pure] public static int length(Que queue) => List.length(queue); public static Unit iter(Que queue, Action action) => List.iter(queue, action); public static Unit iter(Que queue, Action action) => List.iter(queue, action); [Pure] public static bool forall(Que queue, Func pred) => List.forall(queue, pred); [Pure] public static Que distinct(Que queue) => queue.Distinct(); [Pure] public static Que distinct(Que queue) where EQ : Eq => queue.Distinct(); [Pure] public static IEnumerable take(Que queue, int count) => List.take(queue, count); [Pure] public static IEnumerable takeWhile(Que queue, Func pred) => List.takeWhile(queue, pred); [Pure] public static IEnumerable takeWhile(Que queue, Func pred) => List.takeWhile(queue, pred); [Pure] public static bool exists(Que queue, Func pred) => List.exists(queue, pred); } public static class QueueExtensions { [Pure] public static (Que, T) PopUnsafe(this Que queue) => Queue.deqUnsafe(queue); [Pure] public static (Que, Option) Pop(this Que queue) => Queue.deq(queue); [Pure] public static T PeekUnsafe(this Que queue) => Queue.peekUnsafe(queue); [Pure] public static Option Peek(this Que queue) => Queue.peek(queue); [Pure] public static Que Map(this Que queue, Func map) => toQueue(List.map(queue, map)); [Pure] public static Que Map(this Que queue, Func map) => toQueue(List.map(queue, map)); [Pure] public static Que Filter(this Que queue, Func predicate) => toQueue(List.filter(queue, predicate)); [Pure] public static Que Choose(this Que queue, Func> selector) => toQueue(List.choose(queue, selector)); [Pure] public static Que Choose(this Que queue, Func> selector) => toQueue(List.choose(queue, selector)); [Pure] public static Que Collect(this Que queue, Func> map) => toQueue(List.collect(queue, map)); [Pure] public static Que Rev(this Que queue) => toQueue(List.rev(queue)); [Pure] public static Que Append(this Que lhs, IEnumerable rhs) => toQueue(List.append(lhs, rhs)); [Pure] public static S Fold(this Que queue, S state, Func folder) => List.fold(queue, state, folder); [Pure] public static S FoldBack(this Que queue, S state, Func folder) => List.foldBack(queue, state, folder); [Pure] public static T ReduceBack(this Que queue, Func reducer) => List.reduceBack(queue, reducer); [Pure] public static T Reduce(this Que queue, Func reducer) => List.reduce(queue, reducer); [Pure] public static Que Scan(this Que queue, S state, Func folder) => toQueue(List.scan(queue, state, folder)); [Pure] public static IEnumerable ScanBack(this Que queue, S state, Func folder) => toQueue(List.scanBack(queue, state, folder)); [Pure] public static Option Find(this Que queue, Func pred) => List.find(queue, pred); [Pure] public static int Length(this Que queue) => List.length(queue); public static Unit Iter(this Que queue, Action action) => List.iter(queue, action); public static Unit Iter(this Que queue, Action action) => List.iter(queue, action); [Pure] public static bool ForAll(this Que queue, Func pred) => List.forall(queue, pred); [Pure] public static Que Distinct(this Que queue) => toQueue(List.distinct(queue)); [Pure] public static Que Distinct(this Que list) where EQ : Eq => toQueue(List.distinct(list)); [Pure] public static bool Exists(this Que queue, Func pred) => List.exists(queue, pred); } ================================================ FILE: LanguageExt.Core/Immutable Collections/README.md ================================================ This a suite of [very high-performance immutable-collections](https://github.com/louthy/language-ext/blob/main/Performance.md). * For lists you should always prefer to use `Seq` - it is about 10x faster than `Lst`. The only reason you'd pick `Lst` is if you needed to do inserts into the middle of the list: `Seq` doesn't allow this (it only allows prepending or appending), as it would be a performance hit. `Seq` is backed by an array, and so it has exceptional memory locality, `Lst` is an AVL tree to allow for efficient insertion, but suffers from poorer memory locality. * For 'dictionaries' or maps as we prefer to call them, then `HashMap` is the fastest implemention you'll find in .NET-land. It is unsorted. If you need a sorted dictionary, use `Map`. `HashMap` uses the CHAMP data-structure, `Map` uses an AVL tree. * The same goes for sets, prefer `HashSet` over `Set`, unless you need the set to be sorted. You can construct the collection types using the constrctor functions in the `Prelude`: HashMap hashSet = HashMap(("a", 1), ("b", 2), ("c", 3)); HashSet hashMap = HashSet(1, 2, 3); Map hashSet = Map(("a", 1), ("b", 2), ("c", 3)); Set hashMap = Set(1, 2, 3); Seq list = Seq(1, 2, 3); Lst list = List(1, 2, 3); ================================================ FILE: LanguageExt.Core/Immutable Collections/Seq/DSL/Enum.cs ================================================ using System; using System.Collections.Generic; using System.Runtime.CompilerServices; using System.Threading; using LanguageExt.ClassInstances; namespace LanguageExt; /// /// Enumerates an IEnumerable at most once and caches /// the values in a List. Seq uses this to iterate an /// enumerable by index, which allows this type to be /// shared. /// [method: MethodImpl(MethodImplOptions.AggressiveInlining)] internal class Enum(IEnumerable ma) : IDisposable { const int DefaultCapacity = 32; A[] data = new A[DefaultCapacity]; int count; int ncount = -1; IEnumerator? iter = ma.GetEnumerator(); public int Count { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => count; } public A[] Data { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => data; } public (bool Success, A? Value) Get(int index) { while (true) { // Early out if the data has already been streamed if (index < count) { return (true, data[index]); } // If there's nothing left to stream, we must be done var liter = iter; var lcount = index - 1; // lindex is a lagging counter that gets moved on by 1 here. It's the // gatekeeper to moving along the iterator. if (Interlocked.CompareExchange(ref ncount, index, lcount) == lcount) { if (liter != null && liter.MoveNext()) { // Get the next value var value = liter.Current; // If we've run out of space, double it and copy. // Note, this operation is atomic if (index >= data.Length) { var ndata = new A[data.Length << 1]; Array.Copy(data, ndata, data.Length); data = ndata; } // Store the value data[index] = value; // Now, by updating the actual `count` we have essentially done an // atomic operation to get the value from the iterator and store it // in our internal memory. count = index + 1; return (true, value); } else { // End of the iterator, so let's dispose liter?.Dispose(); iter = null; ncount = count - 1; return (false, default); } } } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public override int GetHashCode() => FNV32.Hash, A>(data, 0, count); public void Dispose() => iter?.Dispose(); } ================================================ FILE: LanguageExt.Core/Immutable Collections/Seq/DSL/ISeqInternal.cs ================================================ using System; using System.Collections.Generic; namespace LanguageExt; internal enum SeqType { Empty, Lazy, Strict, Concat } internal interface ISeqInternal : IEnumerable { SeqType Type { get; } A this[int index] { get; } Option At(int index); ISeqInternal Add(A value); ISeqInternal Cons(A value); A Head { get; } ISeqInternal Tail { get; } bool IsEmpty { get; } ISeqInternal Init { get; } A Last { get; } int Count { get; } S Fold(S state, Func f); S FoldBack(S state, Func f); ISeqInternal Skip(int amount); ISeqInternal Take(int amount); ISeqInternal Strict(); Unit Iter(Action f); bool Exists(Func f); bool ForAll(Func f); int GetHashCode(int offsetBasis); ReadOnlySpan AsSpan(); } ================================================ FILE: LanguageExt.Core/Immutable Collections/Seq/DSL/SeqConcat.cs ================================================ using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Runtime.CompilerServices; using LanguageExt.Common; using LanguageExt.UnsafeValueAccess; namespace LanguageExt; internal class SeqConcat(Seq> ms) : ISeqInternal { internal readonly Seq> ms = ms; int selfHash; [MethodImpl(MethodImplOptions.AggressiveInlining)] public ReadOnlySpan AsSpan() => Strict().AsSpan(); public A this[int index] { get { var r = At(index); if (r.IsSome) return r.Value!; throw new IndexOutOfRangeException(); } } public Option At(int index) { if (index < 0) return default; var ms1 = ms; while (!ms1.IsEmpty) { var head = ms1.Head.ValueUnsafe() ?? throw new InvalidOperationException(); var r = head.At(index); if (r.IsSome) return r; index -= head.Count; ms1 = ms1.Tail; } return default; } public SeqType Type => SeqType.Concat; public A Head { get { foreach (var s in ms) { foreach (var a in s) { return a; } } throw Exceptions.SequenceEmpty; } } public ISeqInternal Tail => new SeqLazy(Skip(1)); public bool IsEmpty => ms.ForAll(s => s.IsEmpty); public ISeqInternal Init { get { var take = Count - 1; return take <= 0 ? SeqEmptyInternal.Default : Take(take); } } public A Last { get { foreach (var s in ms.Reverse()) { foreach (var a in s.Reverse()) { return a; } } throw Exceptions.SequenceEmpty; } } public int Count => ms.Sum(s => s.Count); public SeqConcat AddSeq(ISeqInternal ma) => new (ms.Add(ma)); public SeqConcat AddSeqRange(Seq> ma) => new (ms.Concat(ma)); public SeqConcat ConsSeq(ISeqInternal ma) => new (ma.Cons(ms)); public ISeqInternal Add(A value) { var last = ms.Last.ValueUnsafe()?.Add(value) ?? throw new NotSupportedException(); return new SeqConcat(ms.Take(ms.Count - 1).Add(last)); } public ISeqInternal Cons(A value) { var head = ms.Head.ValueUnsafe()?.Cons(value) ?? throw new NotSupportedException(); return new SeqConcat(head.Cons(ms.Skip(1))); } public bool Exists(Func f) => ms.Exists(s => s.Exists(f)); public S Fold(S state, Func f) => ms.Fold(state, (s, x) => x.Fold(s, f)); public S FoldBack(S state, Func f) => ms.FoldBack(state, (s, x) => x.FoldBack(s, f)); public bool ForAll(Func f) => ms.ForAll(s => s.ForAll(f)); public IEnumerator GetEnumerator() { foreach(var s in ms) { foreach(var a in s) { yield return a; } } } public Unit Iter(Action f) { foreach (var s in ms) { foreach (var a in s) { f(a); } } return default; } public ISeqInternal Skip(int amount) => new SeqLazy(((IEnumerable)this).Skip(amount)); public ISeqInternal Strict() { foreach(var s in ms) { s.Strict(); } return this; } public ISeqInternal Take(int amount) { IEnumerable Yield() { using var iter = GetEnumerator(); for(; amount > 0 && iter.MoveNext(); amount--) { yield return iter.Current; } } return new SeqLazy(Yield()); } IEnumerator IEnumerable.GetEnumerator() { foreach (var s in ms) { foreach (var a in s) { yield return a; } } } ISeqInternal Flatten() { var total = 0; foreach (var s in ms) { s.Strict(); total = s.Count; } var cap = 8; while(cap < total) { cap <<= 1; } var data = new A[cap]; var start = (cap - total) >> 1; var current = start; foreach(var s in ms) { var strict = (SeqStrict)s; Array.Copy(strict.data, strict.start, data, current, strict.count); current += strict.count; } return new SeqStrict(data, start, total, 0, 0); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public override int GetHashCode() => selfHash == 0 ? selfHash = GetHashCode(FNV32.OffsetBasis) : selfHash; [MethodImpl(MethodImplOptions.AggressiveInlining)] public int GetHashCode(int hash) { foreach (var seq in ms) { hash = seq.GetHashCode(hash); } return hash; } } ================================================ FILE: LanguageExt.Core/Immutable Collections/Seq/DSL/SeqEmptyInternal.cs ================================================ using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; using LanguageExt.Common; namespace LanguageExt; internal class SeqEmptyInternal : ISeqInternal { public static ISeqInternal Default = new SeqEmptyInternal(); [MethodImpl(MethodImplOptions.AggressiveInlining)] public ReadOnlySpan AsSpan() => ReadOnlySpan.Empty; public A this[int index] => throw new IndexOutOfRangeException(); public Option At(int index) => default; public A Head => throw Exceptions.SequenceEmpty; public ISeqInternal Tail => this; public bool IsEmpty => true; public ISeqInternal Init => this; public A Last => throw Exceptions.SequenceEmpty; public int Count => 0; public ISeqInternal Add(A value) => SeqStrict.FromSingleValue(value); public ISeqInternal Cons(A value) => SeqStrict.FromSingleValue(value); public S Fold(S state, Func f) => state; public S FoldBack(S state, Func f) => state; public ISeqInternal Skip(int amount) => this; public ISeqInternal Strict() => this; public ISeqInternal Take(int amount) => this; public IEnumerator GetEnumerator() { yield break; } IEnumerator IEnumerable.GetEnumerator() { yield break; } public Unit Iter(Action f) => default; public bool Exists(Func f) => false; public bool ForAll(Func f) => true; public SeqType Type => SeqType.Empty; public override int GetHashCode() => FNV32.OffsetBasis; public int GetHashCode(int offsetBasis) => offsetBasis; } ================================================ FILE: LanguageExt.Core/Immutable Collections/Seq/DSL/SeqLazy.cs ================================================ using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; using System.Threading; using LanguageExt.ClassInstances; using LanguageExt.Common; namespace LanguageExt; internal class SeqLazy : ISeqInternal { const int DefaultCapacity = 8; const int NoCons = 1; /// /// Backing data /// readonly A[] data; /// /// Index into data where the Head is /// readonly int start; /// /// Known size of the sequence - 0 means unknown /// readonly int count; /// /// 1 if no more consing is allowed /// int consDisallowed; /// /// Lazy sequence /// readonly Enum seq; /// /// Start position in sequence /// readonly int seqStart; /// /// Cached hash code /// int selfHash; [MethodImpl(MethodImplOptions.AggressiveInlining)] public ReadOnlySpan AsSpan() => Strict().AsSpan(); /// /// Constructor /// [MethodImpl(MethodImplOptions.AggressiveInlining)] internal SeqLazy(IEnumerable ma) : this(new A[DefaultCapacity], DefaultCapacity, 0, 0, new Enum(ma), 0) { } /// /// Constructor /// [MethodImpl(MethodImplOptions.AggressiveInlining)] SeqLazy(A[] data, int start, int count, int noCons, Enum seq, int seqStart) { this.data = data; this.start = start; this.count = count; this.seq = seq; this.seqStart = seqStart; consDisallowed = noCons; } public A this[int index] { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { var r = At(index); if (r.IsSome) return r.Value!; throw new IndexOutOfRangeException(); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public Option At(int index) { if (index < 0) return default; if (index < count) return data[^count]; var lazyIndex = index - count + seqStart; var (succ, val) = StreamTo(lazyIndex); return succ ? val : default(Option); } [MethodImpl(MethodImplOptions.AggressiveInlining)] (bool Success, A? Value) StreamTo(int index) { if(index < seq.Count) return seq.Get(index); while (seq.Count <= index && seq.Get(seq.Count).Success) { // this is empty intentionally } return index < seq.Count ? (true, seq.Data[index]) : (false, default); } public A Head { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { if(count > 0) { return data[^count]; } else if(seq.Count > seqStart) { return seq.Data[seqStart]; } else { var (succ, val) = seq.Get(seqStart); return succ ? val! : throw Exceptions.SequenceEmpty; } } } public ISeqInternal Tail { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { if(count > 0) { return new SeqLazy(data, start + 1, count - 1, NoCons, seq, seqStart); } else if(seq.Count > seqStart) { return new SeqLazy(data, start, count, NoCons, seq, seqStart + 1); } else { var (succ, _) = StreamTo(seq.Count); if(succ) { return new SeqLazy(data, start, count, NoCons, seq, seqStart + 1); } else { return SeqEmptyInternal.Default; } } } } public bool IsEmpty { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => !(count > 0 || seq.Count - seqStart > 0 || seq.Get(seqStart).Success); } public A Last { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { InternalStrict(); return seq.Count > seqStart ? seq.Data[seq.Count - 1] : count > 0 ? data[^1] : throw Exceptions.SequenceEmpty; } } public ISeqInternal Init { get { var take = Count - 1; return take <= 0 ? SeqEmptyInternal.Default : Take(take); } } public int Count { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { InternalStrict(); return count + seq.Count - seqStart; } } public ISeqInternal Add(A value) { InternalStrict(); var seqCount = seq.Count - seqStart; var total = count + seqCount + 1; var len = DefaultCapacity; while (len < total) len <<= 1; var ndata = new A[len]; if (count > 0) { Array.Copy(data, data.Length - count, ndata, 0, count); } if (seqCount > 0) { Array.Copy(seq.Data, seqStart, ndata, count, seqCount); } ndata[count + seqCount] = value; return new SeqStrict(ndata, 0, total, 0, 0); } public ISeqInternal Cons(A value) { if (1 == Interlocked.Exchange(ref consDisallowed, 1) || start == 0) { return CloneCons(value); } else { var nstart = start - 1; data[nstart] = value; return new SeqLazy(data, start - 1, count + 1, 0, seq, seqStart); } } SeqLazy CloneCons(A value) { if (start == 0) { // Find the new size of the data array var nlength = Math.Max(data.Length << 1, 1); // Allocate it var ndata = new A[nlength]; // Copy the old data block to the second half of the new one // so we have space on the left-hand-side to put the cons'd // value Array.Copy(data, 0, ndata, data.Length, data.Length); // The new head position will be 1 cell to to left of the // middle of the newly allocated block. var nstart = data.Length - 1; // We have one more item var ncount = count + 1; // Set the value in the new data block ndata[nstart] = value; // Return everything return new SeqLazy(ndata, nstart, ncount, 0, seq, seqStart); } else { // We're cloning because there are multiple cons operations // from the same Seq. We can't keep walking along the same // array, so we clone with the exact same settings and insert var ndata = new A[data.Length]; var nstart = start - 1; Array.Copy(data, start, ndata, start, count); ndata[nstart] = value; return new SeqLazy(ndata, nstart, count + 1, 0, seq, seqStart); } } public S Fold(S state, Func f) { InternalStrict(); if (count > 0) { for (var i = data.Length - count; i < data.Length; i++) { state = f(state, data[i]); } } if (seq.Count - seqStart > 0) { var scount = seq.Count; var sdata = seq.Data; for (var i = seqStart; i < scount; i++) { state = f(state, sdata[i]); } } return state; } public S FoldBack(S state, Func f) { InternalStrict(); if (seq.Count - seqStart > 0) { var sdata = seq.Data; for (var i = seq.Count - 1; i >= seqStart; i--) { state = f(state, sdata[i]); } } if (count > 0) { var nstart = data.Length - count; for (var i = data.Length - 1; i >= nstart; i--) { state = f(state, data[i]); } } return state; } public ISeqInternal Skip(int amount) { if(amount < count) { return new SeqLazy(data, start + amount, count - amount, NoCons, seq, seqStart); } else if (amount == count) { return new SeqLazy(new A[DefaultCapacity], DefaultCapacity, 0, 0, seq, seqStart); } else { var namount = amount - count; var end = seqStart + namount; if (end > seq.Count) { for (var i = seqStart; i < end && seq.Get(i).Success; i++) { // this is empty intentionally } } if(seq.Count >= end) { return new SeqLazy(new A[DefaultCapacity], DefaultCapacity, 0, 0, seq, end); } else { return SeqEmptyInternal.Default; } } } void InternalStrict() { while (seq.Get(seq.Count).Success) { // this is empty intentionally } } public ISeqInternal Strict() { InternalStrict(); var len = DefaultCapacity; var ncount = count + seq.Count - seqStart; while (len < ncount) len <<= 1; var nstart = (len - ncount) >> 1; var ndata = new A[len]; if (count > 0) { Array.Copy(data, data.Length - count, ndata, nstart, count); } if (seq.Count > 0) { Array.Copy(seq.Data, seqStart, ndata, nstart + count, seq.Count - seqStart); } return new SeqStrict(ndata, nstart, ncount, 0, 0); } public ISeqInternal Take(int amount) { if(amount <= count) { var ndata = new A[data.Length]; var nstart = data.Length - count; Array.Copy(data, nstart, ndata, nstart, data.Length); return new SeqStrict(ndata, start, amount, 0, 0); } else { var namount = amount - count; var end = seqStart + namount; for (var i = seqStart; i < end && seq.Get(i).Success; i++) { // this is empty intentionally } var seqLen = seq.Count - seqStart; amount = Math.Min(seqLen + count, amount); if (amount == 0) { // Empty var edata = new A[DefaultCapacity]; return new SeqStrict(edata, DefaultCapacity >> 1, 0, 0, 0); } else { var len = DefaultCapacity; while (len < amount) len <<= 1; var ndata = new A[len]; var nstart = (len - amount) >> 1; if (count > 0) { Array.Copy(data, data.Length - count, ndata, nstart, count); } if (seq.Count - seqStart > 0) { Array.Copy(seq.Data, seqStart, ndata, nstart + count, amount - count); } return new SeqStrict(ndata, nstart, amount, 0, 0); } } } public IEnumerator GetEnumerator() { var nstart = data.Length - count; var nend = data.Length; for (var i = nstart; i < nend; i++) { yield return data[i]; } for(var i = seqStart; ; i++) { var (succ, val) = seq.Get(i); if(succ) { yield return val!; } else { yield break; } } } IEnumerator IEnumerable.GetEnumerator() { var nstart = data.Length - count; var nend = data.Length; for (var i = nstart; i < nend; i++) { yield return data[i]; } for (var i = seqStart; ; i++) { var (succ, val) = seq.Get(i); if (succ) { yield return val!; } else { yield break; } } } public Unit Iter(Action f) { foreach(var item in this) { f(item); } return default; } public bool Exists(Func f) { foreach(var item in this) { if (f(item)) { return true; } } return false; } public bool ForAll(Func f) { foreach (var item in this) { if (!f(item)) { return false; } } return true; } public SeqType Type => SeqType.Lazy; [MethodImpl(MethodImplOptions.AggressiveInlining)] public override int GetHashCode() => selfHash == 0 ? selfHash = GetHashCode(FNV32.OffsetBasis) : selfHash; public int GetHashCode(int hash) { InternalStrict(); if (count > 0) { hash = FNV32.Hash, A>(data, start, count, hash); } if (seq.Count - seqStart > 0) { hash = FNV32.Hash, A>(seq.Data, seqStart, seq.Count - seqStart, hash); } return hash; } } ================================================ FILE: LanguageExt.Core/Immutable Collections/Seq/DSL/SeqStrict.cs ================================================ using System; using System.Linq; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; using System.Threading; using LanguageExt.ClassInstances; using LanguageExt.Common; namespace LanguageExt; internal class SeqStrict : ISeqInternal { public const int DefaultCapacity = 8; /* const int HalfDefaultCapacity = DefaultCapacity >> 1; */ const int NoCons = 1; const int NoAdd = 1; /// /// Backing data /// internal readonly A[] data; /// /// Index into data where the Head is /// internal readonly int start; /// /// Known size of the sequence /// internal readonly int count; /// /// 1 if no more consing is allowed /// int consDisallowed; /// /// 1 if no more adding is allowed /// int addDisallowed; /// /// Cached hash code /// int selfHash; [MethodImpl(MethodImplOptions.AggressiveInlining)] public ReadOnlySpan AsSpan() => new(data, start, count); /// /// Constructor /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public SeqStrict(A[] data, int start, int count, int consDisallowed, int addDisallowed) { this.data = data; this.start = start; this.count = count; this.consDisallowed = consDisallowed; this.addDisallowed = addDisallowed; } public static SeqStrict Empty { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new (new A[DefaultCapacity], 4, 0, 0, 0); } /// /// Add constructor (called in the Add function only) /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public SeqStrict(A[] data, int start, int count) { this.data = data; this.start = start; this.count = count; consDisallowed = NoCons; } /// /// Indexer /// public A this[int index] { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => index < 0 || index >= count ? throw new IndexOutOfRangeException() : data[start + index]; } /// /// Indexer /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public Option At(int index) => index < 0 || index >= count ? default(Option) : data[start + index]; /// /// Add an item to the end of the sequence /// /// /// Forces evaluation of the entire lazy sequence so the item /// can be appended /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public ISeqInternal Add(A value) { var end = start + count; if (1 == Interlocked.Exchange(ref addDisallowed, 1) || end == data.Length) { return CloneAdd(value); } else { data[end] = value; return new SeqStrict(data, start, count + 1); } } /// /// Add a range of items to the end of the sequence /// [MethodImpl(MethodImplOptions.AggressiveInlining)] SeqStrict Concat(A[] items, int itemsStart, int itemsCount) { var end = start + count; if (1 == Interlocked.Exchange(ref addDisallowed, 1) || end + itemsCount >= data.Length) { return CloneAddRange(items, itemsStart, itemsCount); } else { Array.Copy(items, itemsStart, data, end, itemsCount); return new SeqStrict(data, start, count + itemsCount, NoCons, 0); } } /// /// Prepend an item to the sequence /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public ISeqInternal Cons(A value) { if (1 == Interlocked.Exchange(ref consDisallowed, 1) || start == 0) { return CloneCons(value); } else { var nstart = start - 1; data[nstart] = value; return new SeqStrict(data, start - 1, count + 1, 0, NoAdd); } } SeqStrict CloneCons(A value) { if (start == 0) { // Find the new size of the data array var nlength = Math.Max(data.Length << 1, 1); // Allocate it var ndata = new A[nlength]; // Copy the old data block to the second half of the new one // so we have space on the left-hand-side to put the cons'd // value Array.Copy(data, 0, ndata, data.Length, data.Length); // The new head position will be 1 cell to to left of the // middle of the newly allocated block. var nstart = data.Length == 0 ? 0 : data.Length - 1; // We have one more item var ncount = count + 1; // Set the value in the new data block ndata[nstart] = value; // Return everything return new SeqStrict(ndata, nstart, ncount, 0, 0); } else { // We're cloning because there are multiple cons operations // from the same Seq. We can't keep walking along the same // array, so we clone with the exact same settings and insert var ndata = new A[data.Length]; var nstart = start - 1; Array.Copy(data, start, ndata, start, count); ndata[nstart] = value; return new SeqStrict(ndata, nstart, count + 1, 0, 0); } } SeqStrict CloneAdd(A value) { var end = start + count; // Find the new size of the data array var nlength = data.Length == end ? Math.Max(data.Length << 1, 1) : data.Length; // Allocate it var ndata = new A[nlength]; // Copy the old data block to the first half of the new one // so we have space on the right-hand-side to put the added // value Array.Copy(data, 0, ndata, 0, data.Length); // Set the value in the new data block ndata[end] = value; // Return everything return new SeqStrict(ndata, start, count + 1, 0, 0); } SeqStrict CloneAddRange(A[] values, int valuesStart, int valuesCount) { var end = start + count; // Find the new size of the data array var nlength = Math.Max(Math.Max(data.Length << 1, 1), end + valuesCount); // Allocate it var ndata = new A[nlength]; // Copy the old data block to the first half of the new one // so we have space on the right-hand-side to put the added // value Array.Copy(data, 0, ndata, 0, end); // Set the value in the new data block Array.Copy(values, valuesStart, ndata, end, valuesCount); // Return everything return new SeqStrict(ndata, start, count + valuesCount, 0, 0); } /// /// Head item in the sequence. NOTE: If `IsEmpty` is true then Head /// is undefined. Call HeadOrNone() if for maximum safety. /// public A Head { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => count == 0 ? throw Exceptions.SequenceEmpty : data[start]; } /// /// Tail of the sequence /// public ISeqInternal Tail { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => count < 1 ? SeqEmptyInternal.Default : new SeqStrict(data, start + 1, count - 1, NoCons, NoAdd); } public ISeqInternal Init { get { var take = count - 1; return take <= 0 ? SeqEmptyInternal.Default : new SeqStrict(data, start, take, NoCons, NoAdd); } } /// /// Returns true if the sequence is empty /// /// /// For lazy streams this will have to peek at the first /// item. So, the first item will be consumed. /// public bool IsEmpty { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => count == 0; } /// /// Last item in sequence. Throws if no items in sequence /// public A Last { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => IsEmpty ? throw Exceptions.SequenceEmpty : data[start + count - 1]; } /// /// Returns the number of items in the sequence /// /// Number of items in the sequence public int Count { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => count; } /// /// Fold the sequence from the first item to the last /// /// State type /// Initial state /// Fold function /// Aggregated state [MethodImpl(MethodImplOptions.AggressiveInlining)] public S Fold(S state, Func f) { var end = start + count; for(var i = start; i < end; i++) { state = f(state, data[i]); } return state; } /// /// Fold the sequence from the last item to the first. For /// sequences that are not lazy and are less than 5000 items /// long, FoldBackRec is called instead, because it is faster. /// /// State type /// Initial state /// Fold function /// Aggregated state [MethodImpl(MethodImplOptions.AggressiveInlining)] public S FoldBack(S state, Func f) { for (var i = start + count - 1; i >= start; i--) { state = f(state, data[i]); } return state; } /// /// Skip count items /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public ISeqInternal Skip(int amount) { if (amount < 1) { return this; } var end = start + count; var newStart = start + amount; return newStart < end ? new SeqStrict(data, newStart, count - amount, NoCons, NoAdd) : SeqEmptyInternal.Default; } /// /// Take count items /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public ISeqInternal Take(int amount) => amount < count ? new SeqStrict(data, start, amount, NoCons, NoAdd) : this; [MethodImpl(MethodImplOptions.AggressiveInlining)] public ISeqInternal Strict() => this; [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static SeqStrict FromSingleValue(A value) => new ([default!, default!, default!, default!, value, default!, default!, default!], 4, 1, 0, 0); public IEnumerator GetEnumerator() { var end = start + count; for (var i = start; i < end; i++) { yield return data[i]; } } IEnumerator IEnumerable.GetEnumerator() { var end = start + count; for (var i = start; i < end; i++) { yield return data[i]; } } public SeqStrict SetItem(int index, A value) { var ndata = new A[data.Length]; Array.Copy(data, start, ndata, start, count); ndata[index] = value; return new SeqStrict(data, start, count); } public Unit Iter(Action f) { var end = start + count; for (var i = start; i < end; i++) { f(data[i]); } return default; } public bool Exists(Func f) { var end = start + count; for (var i = start; i < end; i++) { if(f(data[i])) { return true; } } return false; } public bool ForAll(Func f) { var end = start + count; for (var i = start; i < end; i++) { if (!f(data[i])) { return false; } } return true; } public SeqType Type => SeqType.Strict; public SeqStrict Append(SeqStrict right) { var end = start + count + right.count; if (end > data.Length || 1 == Interlocked.Exchange(ref addDisallowed, 1)) { // Clone var nsize = 8; while(nsize < end) { nsize <<= 1; } var ndata = new A[nsize]; Array.Copy(data, start, ndata, start, count); Array.Copy(right.data, right.start, ndata, start + count, right.count); return new SeqStrict(ndata, start, count + right.count, 0, 0); } else { Array.Copy(right.data, right.start, data, start + count, right.count); return new SeqStrict(data, start, count + right.count, NoCons, 0); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public override int GetHashCode() => selfHash == 0 ? selfHash = GetHashCode(FNV32.OffsetBasis) : selfHash; [MethodImpl(MethodImplOptions.AggressiveInlining)] public int GetHashCode(int offsetBasis) => FNV32.Hash, A>(data, start, count, offsetBasis); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static SeqStrict FromEnumerable(IEnumerable ma) { var arr = ma.ToArray(); return new SeqStrict(arr, 0, arr.Length, 0, 0); } public SeqStrict Rev() { var ndata = new A[data.Length]; var i = start; var j = data.Length - start; var end = start + count; for (; i < end; i++, j--) { ndata[j] = data[i]; } return new SeqStrict(ndata, data.Length - start, count, 0, 0); } public SeqStrict Map(Func f) { var ndata = new B[data.Length]; var end = start + count; for (var i = start; i < end; i++) { ndata[i] = f(data[i]); } return new SeqStrict(ndata, start, count, 0, 0); } public SeqStrict Map(Func f) { var ndata = new B[data.Length]; var end = start + count; for (var i = start; i < end; i++) { ndata[i] = f(data[i], i - start); } return new SeqStrict(ndata, start, count, 0, 0); } public SeqStrict Filter(Func f) { var ndata = new A[data.Length]; var end = start + count; var ncount = 0; for (var i = start; i < end; i++) { var d = data[i]; if (f(d)) { ndata[start + ncount] = d; ncount++; } } return new SeqStrict(ndata, start, ncount, 0, 0); } } ================================================ FILE: LanguageExt.Core/Immutable Collections/Seq/Extensions/Seq.Extensions.MapApply.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public static partial class SeqExtensions { /// /// Functor map operation /// /// /// Unwraps the value within the functor, passes it to the map function `f` provided, and /// then takes the mapped value and wraps it back up into a new functor. /// /// Functor to map /// Mapping function /// Mapped functor public static Seq Map(this Func f, K ma) => Functor.map(f, ma).As(); /// /// Functor map operation /// /// /// Unwraps the value within the functor, passes it to the map function `f` provided, and /// then takes the mapped value and wraps it back up into a new functor. /// /// Functor to map /// Mapping function /// Mapped functor public static Seq Map(this Func f, Seq ma) => Functor.map(f, ma).As(); /// /// Applicative action: runs the first applicative, ignores the result, and returns the second applicative /// public static Seq Action(this Seq ma, K mb) => Applicative.action(ma, mb).As(); /// /// Applicative action: runs the first applicative, ignores the result, and returns the second applicative /// public static Seq Action(this K ma, K mb) => Applicative.action(ma, mb).As(); /// /// Applicative functor apply operation /// /// /// Unwraps the value within the `ma` applicative-functor, passes it to the unwrapped function(s) within `mf`, and /// then takes the resulting value and wraps it back up into a new applicative-functor. /// /// Value(s) applicative functor /// Mapping function(s) /// Mapped applicative functor public static Seq Apply(this Seq> mf, K ma) => Applicative.apply(mf, ma).As(); /// /// Applicative functor apply operation /// /// /// Unwraps the value within the `ma` applicative-functor, passes it to the unwrapped function(s) within `mf`, and /// then takes the resulting value and wraps it back up into a new applicative-functor. /// /// Value(s) applicative functor /// Mapping function(s) /// Mapped applicative functor public static Seq Apply(this K> mf, K ma) => Applicative.apply(mf, ma).As(); } ================================================ FILE: LanguageExt.Core/Immutable Collections/Seq/Extensions/Seq.Extensions.cs ================================================ #pragma warning disable CS0693 // Type parameter has the same name as the type parameter from outer type using LanguageExt.ClassInstances; using LanguageExt.Traits; using System; using System.Collections.Generic; using System.Diagnostics.Contracts; using System.Linq; using static LanguageExt.Prelude; namespace LanguageExt; public static partial class SeqExtensions { public static Seq As(this K xs) => (Seq)xs; /// /// Monadic join /// [Pure] public static Seq Flatten(this Seq> ma) => ma.Bind(identity); /// /// Applies the given function 'selector' to each element of the sequence. Returns the sequence /// comprised of the results for each element where the function returns Some(f(x)). /// /// sequence item type /// sequence /// Selector function /// Mapped and filtered sequence [Pure] public static Seq Choose(this Seq list, Func> selector) => Seq.choose(list, selector); /// /// Applies the given function 'selector' to each element of the sequence. Returns the /// sequence comprised of the results for each element where the function returns Some(f(x)). /// An index value is passed through to the selector function also. /// /// sequence item type /// sequence /// Selector function /// Mapped and filtered sequence [Pure] public static Seq Choose(this Seq list, Func> selector) => Seq.choose(list, selector); /// /// Reverses the sequence (Reverse in LINQ) /// /// sequence item type /// sequence to reverse /// Reversed sequence [Pure] public static Seq Rev(this Seq list) => Seq.rev(list); /// /// Concatenate two sequences (Concat in LINQ) /// /// sequence item type /// First sequence /// Second sequence /// Concatenated sequence [Pure] public static Seq Append(this Seq lhs, Seq rhs) => Seq.append(lhs, rhs); /// /// Concatenate a sequence and a sequence of sequences /// /// List item type /// First list /// Second list /// Concatenated list [Pure] public static Seq Append(this Seq x, Seq> xs) => Seq.append(x, xs); /// /// Applies a function to each element of the sequence, threading an accumulator argument /// through the computation. This function takes the state argument, and applies the function /// to it and the first element of the sequence. Then, it passes this result into the function /// along with the second element, and so on. Finally, it returns the list of intermediate /// results and the final result. /// /// State type /// sequence item type /// sequence to fold /// Initial state /// Folding function /// Aggregate state [Pure] public static Seq Scan(this Seq list, S state, Func folder) => Seq.scan(list, state, folder); /// /// Applies a function to each element of the sequence (from last element to first), /// threading an accumulator argument through the computation. This function takes the state /// argument, and applies the function to it and the first element of the sequence. Then, it /// passes this result into the function along with the second element, and so on. Finally, /// it returns the list of intermediate results and the final result. /// /// State type /// Enumerable item type /// Enumerable to fold /// Initial state /// Folding function /// Aggregate state [Pure] public static Seq ScanBack(this Seq list, S state, Func folder) => Seq.scanBack(list, state, folder); /// /// Joins two sequences together either into a single sequence using the join /// function provided /// /// First sequence to join /// Second sequence to join /// Join function /// Joined sequence [Pure] public static Seq Zip(this Seq list, Seq other, Func zipper) => toSeq(Enumerable.Zip(list, other, zipper)); /// /// Joins two sequences together either into an sequence of tuples /// /// First sequence to join /// Second sequence to join /// Join function /// Joined sequence of tuples [Pure] public static Seq<(T First, U Second)> Zip(this Seq list, Seq other) => toSeq(Enumerable.Zip(list, other, (t, u) => (t, u))); /// /// Return a new sequence with all duplicate values removed /// /// sequence item type /// sequence /// A new sequence with all duplicate values removed [Pure] public static Seq Distinct(this Seq list) => toSeq(Enumerable.Distinct(list)); /// /// Return a new sequence with all duplicate values removed /// /// sequence item type /// sequence /// A new sequence with all duplicate values removed [Pure] public static Seq Distinct(this Seq list) where EQ : Eq => toSeq(Enumerable.Distinct(list, new EqCompare(static (x, y) => EQ.Equals(x, y), static x => EQ.GetHashCode(x)))); /// /// Return a new sequence with all duplicate values removed /// /// sequence item type /// sequence /// A new sequence with all duplicate values removed [Pure] public static Seq Distinct(this Seq list, Func keySelector, Option> compare = default) => toSeq(Enumerable.Distinct(list, new EqCompare( (a, b) => compare.IfNone(EqDefault.Equals)(keySelector(a), keySelector(b)), a => compare.Match(Some: _ => 0, None: () => EqDefault.GetHashCode(keySelector(a)))))); /// /// The tails function returns all final segments of the argument, longest first. For example: /// /// tails(['a','b','c']) == [['a','b','c'], ['b','c'], ['c'],[]] /// /// Seq item type /// Seq /// Seq of Seq of A [Pure] public static Seq> Tails(this Seq self) => Seq.tails(self); /// /// Span, applied to a predicate 'pred' and a list, returns a tuple where first element is /// longest prefix (possibly empty) of elements that satisfy 'pred' and second element is the /// remainder of the list: /// /// /// Seq.span(List(1,2,3,4,1,2,3,4), x => x 〈 3) == (List(1,2),List(3,4,1,2,3,4)) /// /// /// Seq.span(List(1,2,3), x => x 〈 9) == (List(1,2,3),List()) /// /// /// Seq.span(List(1,2,3), x => x 〈 0) == (List(),List(1,2,3)) /// /// List element type /// List /// Predicate /// Split list [Pure] public static (Seq, Seq) Span(this Seq self, Func pred) => Seq.span(self, pred); /// /// Convert to a queryable /// /// [Pure] public static IQueryable AsQueryable(this Seq source) => // NOTE TO FUTURE ME: Don't delete this thinking it's not needed! source.Value.AsQueryable(); } ================================================ FILE: LanguageExt.Core/Immutable Collections/Seq/Operators/Seq.Operators.cs ================================================ using LanguageExt.Traits; namespace LanguageExt; public static partial class SeqExtensions { extension(K _) { /// /// Downcast operator /// public static Seq operator +(K ma) => (Seq)ma; public static Seq operator >> (K ma, Lower lower) => (Seq)ma; } } ================================================ FILE: LanguageExt.Core/Immutable Collections/Seq/Prelude/Seq.Prelude.mapapply.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public static partial class Prelude { /// /// Functor map operation /// /// /// Unwraps the value within the functor, passes it to the map function `f` provided, and /// then takes the mapped value and wraps it back up into a new functor. /// /// Functor to map /// Mapping function /// Mapped functor public static Seq map(Func f, K ma) => Functor.map(f, ma).As(); /// /// Applicative action: runs the first applicative, ignores the result, and returns the second applicative /// public static Seq action(K ma, K mb) => Applicative.action(ma, mb).As(); /// /// Applicative functor apply operation /// /// /// Unwraps the value within the `ma` applicative-functor, passes it to the unwrapped function(s) within `mf`, and /// then takes the resulting value and wraps it back up into a new applicative-functor. /// /// Value(s) applicative functor /// Mapping function(s) /// Mapped applicative functor public static Seq apply(K> mf, K ma) => Applicative.apply(mf, ma).As(); } ================================================ FILE: LanguageExt.Core/Immutable Collections/Seq/Seq.Module.cs ================================================ #pragma warning disable CS0693 // Type parameter has the same name as the type parameter from outer type using System; using System.Collections.Generic; using static LanguageExt.Prelude; using System.Diagnostics.Contracts; using System.Linq; using LanguageExt.Traits; using LanguageExt.ClassInstances; using System.Runtime.CompilerServices; namespace LanguageExt; /// /// `Seq` module /// public partial class Seq { /// /// Monadic join /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Seq flatten(Seq> ma) => ma.Bind(identity); /// /// Create an empty sequence /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Seq empty() => Seq.Empty; /// /// Construct a sequence from a single item and a tail sequence /// /// Head item /// Tail sequence /// Value type /// Constructed sequence [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Seq cons(A head, Seq tail) => head.Cons(tail); /// /// Create an empty sequence /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Seq singleton(A value) => [value]; /// /// Create a new empty sequence /// /// sequence [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Seq create() => Seq.Empty; /// /// Create a sequence from a initial set of items /// /// Items /// sequence [Pure] public static Seq create(params A[] items) { if (items.Length == 0) return Seq.Empty; var nitems = new A[items.Length]; System.Array.Copy(items, nitems, items.Length); return FromArray(items); } /// /// Create a sequence from an initial set of items /// /// Items /// sequence [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Seq createRange(ReadOnlySpan items) => items.Length == 0 ? Seq.Empty : new (items); /// /// Create a sequence from an initial set of items /// /// Items /// sequence [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Seq createRange(IEnumerable items) => new (items); /// /// Generates a sequence of A using the provided delegate to initialise /// each item. /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Seq generate(int count, Func generator) => IterableExtensions.AsIterable(Range(0, count)).Map(generator).ToSeq(); /// /// Generates a sequence that contains one repeated value. /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Seq repeat(A item, int count) => IterableExtensions.AsIterable(Range(0, count)).Map(_ => item).ToSeq(); /// /// Get the item at the head (first) of the sequence /// /// sequence /// Head item [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Option head(Seq list) => list.Head; /// /// Get the last item of the sequence /// /// sequence /// Last item [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Option last(Seq list) => list.Last; /// /// Get all items in the list except the last one /// /// /// Must evaluate the last item to know it's the last, but won't return it /// /// List /// The initial items (all but the last) [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Seq init(Seq list) => list.Init; /// /// Get the tail of the sequence (skips the head item) /// /// sequence /// Tail sequence [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Seq tail(Seq list) => list.Tail; /// /// Projects the values in the sequence using a map function into a new sequence (Select in LINQ). /// /// sequence item type /// Return sequence item type /// sequence to map /// Map function /// Mapped sequence [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Seq map(Seq list, Func map) => list.Select(map); /// /// Projects the values in the sequence into a new sequence using a map function, which is also given an index value /// (Select in LINQ - note that the order of the arguments of the map function are the other way around, here the index /// is the first argument). /// /// sequence item type /// Return sequence item type /// sequence to map /// Map function /// Mapped sequence [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Seq map(Seq list, Func map) => toSeq(zip(list, toSeq(Range(0, int.MaxValue)), (t, i) => map(i, t))); /// /// Removes items from the sequence that do not match the given predicate (Where in LINQ) /// /// sequence item type /// sequence to filter /// Predicate function /// Filtered sequence [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Seq filter(Seq list, Func predicate) => list.Where(predicate); /// /// Applies the given function 'selector' to each element of the sequence. Returns the sequence /// comprised of the results for each element where the function returns Some(f(x)). /// /// sequence item type /// sequence /// Selector function /// Mapped and filtered sequence [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Seq choose(Seq list, Func> selector) => map(filter(map(list, selector), t => t.IsSome), t => t.Value!); /// /// Applies the given function 'selector' to each element of the sequence. Returns the /// sequence comprised of the results for each element where the function returns Some(f(x)). /// An index value is passed through to the selector function also. /// /// sequence item type /// sequence /// Selector function /// Mapped and filtered sequence [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Seq choose(Seq list, Func> selector) => map(filter(map(list, selector), t => t.IsSome), t => t.Value!); /// /// Reverses the sequence (Reverse in LINQ) /// /// sequence item type /// sequence to reverse /// Reversed sequence [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Seq rev(Seq list) => toSeq(list.Reverse()); /// /// Concatenate two sequences (Concat in LINQ) /// /// sequence item type /// First sequence /// Second sequence /// Concatenated sequence [Pure] public static Seq append(Seq lhs, Seq rhs) => lhs.Concat(rhs); /// /// Concatenate a sequence and a sequence of sequences /// /// List item type /// First list /// Second list /// Concatenated list [Pure] public static Seq append(Seq x, Seq> xs) => head(xs).IsNone ? x : append(x, append((Seq)xs.Head, xs.Skip(1))); /// /// Concatenate N sequences /// /// sequence type /// sequences to concatenate /// A single sequence with all of the items concatenated [Pure] public static Seq append(params Seq[] lists) => lists.Length switch { 0 => Seq.Empty, 1 => lists[0], _ => append(lists[0], toSeq(lists).Skip(1)) }; /// /// Applies a function to each element of the sequence, threading an accumulator argument /// through the computation. This function takes the state argument, and applies the function /// to it and the first element of the sequence. Then, it passes this result into the function /// along with the second element, and so on. Finally, it returns the list of intermediate /// results and the final result. /// /// State type /// sequence item type /// sequence to fold /// Initial state /// Folding function /// Aggregate state [Pure] public static Seq scan(Seq list, S state, Func folder) { IEnumerable Yield() { yield return state; foreach (var item in list) { state = folder(state, item); yield return state; } } return toSeq(Yield()); } /// /// Applies a function to each element of the sequence (from last element to first), /// threading an accumulator argument through the computation. This function takes the state /// argument, and applies the function to it and the first element of the sequence. Then, it /// passes this result into the function along with the second element, and so on. Finally, /// it returns the list of intermediate results and the final result. /// /// State type /// Enumerable item type /// Enumerable to fold /// Initial state /// Folding function /// Aggregate state [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Seq scanBack(Seq list, S state, Func folder) => scan(rev(list), state, folder); /// /// Joins two sequences together either into a single sequence using the join /// function provided /// /// First sequence to join /// Second sequence to join /// Join function /// Joined sequence [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Seq zip(Seq list, Seq other, Func zipper) => toSeq(list.Zip(other, zipper)); /// /// Joins two sequences together either into an sequence of tuples /// /// First sequence to join /// Second sequence to join /// Join function /// Joined sequence of tuples [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Seq<(T First, U Second)> zip(Seq list, Seq other) => toSeq(list.Zip(other, (t, u) => (t, u))); /// /// Returns true if all items in the sequence match a predicate (Any in LINQ) /// /// sequence item type /// sequence to test /// Predicate /// True if all items in the sequence match the predicate [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool forall(Seq list, Func pred) => list.ForAll(pred); /// /// Return a new sequence with all duplicate values removed /// /// sequence item type /// sequence /// A new sequence with all duplicate values removed [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Seq distinct(Seq list) => toSeq(list.Distinct()); /// /// Return a new sequence with all duplicate values removed /// /// sequence item type /// sequence /// A new sequence with all duplicate values removed [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Seq distinct(Seq list) where EQ : Eq => toSeq(list.Distinct(new EqCompare(static (x, y) => EQ.Equals(x, y), static x => EQ.GetHashCode(x)))); /// /// Return a new sequence with all duplicate values removed /// /// sequence item type /// sequence /// A new sequence with all duplicate values removed [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Seq distinct(Seq list, Func keySelector, Option> compare = default) => toSeq(list.Distinct(new EqCompare( (a, b) => compare.IfNone(EqDefault.Equals)(keySelector(a), keySelector(b)), a => compare.Match(Some: _ => 0, None: () => EqDefault.GetHashCode(keySelector(a)))))); /// /// Returns a new sequence with the first 'count' items from the sequence provided /// /// sequence item type /// sequence /// Number of items to take /// A new sequence with the first 'count' items from the sequence provided [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Seq take(Seq list, int count) => list.Take(count); /// /// Iterate the sequence, yielding items if they match the predicate provided, and stopping /// as soon as one doesn't /// /// sequence item type /// sequence /// Number of items to take /// A new sequence with the first items that match the predicate [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Seq takeWhile(Seq list, Func pred) => list.TakeWhile(pred); /// /// Iterate the sequence, yielding items if they match the predicate provided, and stopping /// as soon as one doesn't. An index value is also provided to the predicate function. /// /// sequence item type /// sequence /// Number of items to take /// A new sequence with the first items that match the predicate [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Seq takeWhile(Seq list, Func pred) => list.TakeWhile(pred); /// /// The tails function returns all final segments of the argument, longest first. For example: /// /// tails(['a','b','c']) == [['a','b','c'], ['b','c'], ['c'],[]] /// /// Seq item type /// Seq /// Seq of Seq of A [Pure] public static Seq> tails(Seq self) { var res = Seq>.Empty; while (!self.IsEmpty) { res = self.Cons(res); self = self.Tail; } return rev(res); } /// /// The tailsr function returns all final segments of the argument, longest first. For example: /// /// tails(['a','b','c']) == [['a','b','c'], ['b','c'], ['c'],[]] /// /// Differs from `tails` in implementation only. The `tailsr` uses recursive processing /// whereas `tails` uses a while loop aggregation followed by a reverse. For small sequences /// `tailsr` is probably more efficient. /// Seq item type /// Seq /// Seq of Seq of A [Pure] public static Seq> tailsr(Seq self) => self.Match( () => Seq>.Empty, xs => xs.Cons(tailsr(xs.Tail))); /// /// Span, applied to a predicate 'pred' and a list, returns a tuple where first element is /// longest prefix (possibly empty) of elements that satisfy 'pred' and second element is the /// remainder of the list: /// /// /// Seq.span(Seq(1,2,3,4,1,2,3,4), x => x 〈 3) == (Seq(1,2), Seq(3,4,1,2,3,4)) /// /// /// Seq.span(Seq(1,2,3), x => x 〈 9) == (Seq(1,2,3), Seq()) /// /// /// Seq.span(Seq(1,2,3), x => x 〈 0) == (Seq(), Seq(1,2,3)) /// /// List element type /// List /// Predicate /// Split list [Pure] public static (Seq, Seq) span(Seq self, Func pred) { int index = 0; foreach (var item in self) { if (!pred(item)) { break; } index++; } return (self.Take(index), self.Skip(index)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static Seq FromSingleValue(A value) => new (SeqStrict.FromSingleValue(value)); [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static Seq FromArray(A[] value) => new (new SeqStrict(value, 0, value.Length, 0, 0)); [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static Seq FromArray(A[] value, int length) => new (new SeqStrict(value, 0, length, 0, 0)); } ================================================ FILE: LanguageExt.Core/Immutable Collections/Seq/Seq.cs ================================================ #pragma warning disable CS0693 // Type parameter has the same name as the type parameter from outer type using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics.Contracts; using System.Linq; using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using LanguageExt.ClassInstances; using LanguageExt.Common; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; /// /// Cons sequence /// Represents a sequence of values in a similar way to IEnumerable, but without the /// issues of multiple evaluation for key LINQ operators like Skip, Count, etc. /// /// Type of the values in the sequence [CollectionBuilder(typeof(Seq), nameof(Seq.createRange))] public readonly struct Seq : IEnumerable, IComparable>, IEquatable>, IComparable, IComparisonOperators, Seq, bool>, IAdditionOperators, Seq, Seq>, IAdditiveIdentity, Seq>, Monoid>, K { /// /// Empty sequence /// public static Seq Empty { get; } = new(SeqEmptyInternal.Default); /// /// Internal representation of the sequence (SeqStrict|SeqLazy|SeqEmptyInternal) /// readonly ISeqInternal value; /// /// Internal value accessor - protects against `default` /// internal ISeqInternal Value { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => value ?? SeqEmptyInternal.Default; } /// /// Constructor from lazy sequence /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public Seq(IEnumerable ma) : this(new SeqLazy(ma)) { } /// /// Constructor from lazy sequence /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public Seq(ReadOnlySpan ma) : this(Seq.FromArray(ma.ToArray())) { } /// /// Constructor /// [MethodImpl(MethodImplOptions.AggressiveInlining)] internal Seq(ISeqInternal value) => this.value = value; /// /// Reference version for use in pattern-matching /// [Pure] public object? Case => IsEmpty ? null : Tail.IsEmpty ? Head.Value : (Head.Value, Tail); public void Deconstruct(out A head, out Seq tail) { head = Head.IfNone(() => throw Exceptions.SequenceEmpty); tail = Tail; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public ReadOnlySpan AsSpan() => Value.AsSpan(); /// /// Head lens /// public static Lens, A> head => Lens, A>.New( Get: la => la.IsEmpty ? throw new IndexOutOfRangeException() : la[0], Set: a => la => la.IsEmpty ? throw new IndexOutOfRangeException() : a.Cons(la.Tail) ); /// /// Head or none lens /// public static Lens, Option> headOrNone => Lens, Option>.New( Get: la => la.Head, Set: a => la => la.IsEmpty || a.IsNone ? la : a.Value.Cons(la.Tail!)! ); /// /// Tail lens /// public static Lens, Seq> tail => Lens, Seq>.New( Get: la => la.IsEmpty ? Empty : la.Tail, Set: a => la => la.IsEmpty ? a : ((A)la.Head).Cons(a) ); /// /// Last lens /// public static Lens, A> last => Lens, A>.New( Get: la => la.IsEmpty ? throw new IndexOutOfRangeException() : (A)la.Last, Set: a => la => la.IsEmpty ? throw new IndexOutOfRangeException() : la.Take(la.Count - 1).Add(a) ); /// /// Last or none lens /// public static Lens, Option> lastOrNone => Lens, Option>.New( Get: la => la.Last, Set: a => la => la.IsEmpty || a.IsNone ? la : la.Take(la.Count - 1).Add(a.Value!)); /// /// Lens map /// [Pure] public static Lens, Seq> map(Lens lens) => Lens, Seq>.New( Get: la => la.Map(lens.Get), Set: lb => la => la.Zip(lb).Map(ab => lens.Set(ab.Item2, ab.Item1)) ); /// /// Indexer /// /// public A this[Index index] { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => Value[index.IsFromEnd ? index.GetOffset(Count) : index.Value]; } /// /// Add an item to the end of the sequence /// /// /// Forces evaluation of the entire lazy sequence so the item /// can be appended /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Seq Add(A value) => new (Value.Add(value)); /// /// Add a range of items to the end of the sequence /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Seq Concat(IEnumerable items) => items switch { Lst lst => Concat(lst), Set set => Concat(set), HashSet hset => Concat(hset), Arr arr => Concat(arr), Stck stck => Concat(stck), IReadOnlyList rolist => Concat(rolist), _ => new Seq(EnumerableOptimal.ConcatFast(this, items)) }; /// /// Add a range of items to the end of the sequence /// /// /// Forces evaluation of the entire lazy sequence so the items /// can be appended. /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Seq Concat(in Lst items) { if (items.Count == 0) { return this; } var arr = items.Value.ToArray(); return Concat(Seq.FromArray(arr)); } /// /// Add a range of items to the end of the sequence /// /// /// Forces evaluation of the entire lazy sequence so the items /// can be appended. /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Seq Concat(in ReadOnlySpan items) { if (items.Length == 0) { return this; } var arr = items.ToArray(); return Concat(Seq.FromArray(arr)); } /// /// Add a range of items to the end of the sequence /// /// /// Forces evaluation of the entire lazy sequence so the items /// can be appended. /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Seq Concat(in Set items) { if (items.Count == 0) { return this; } var arr = items.Value.ToArray(); return Concat(Seq.FromArray(arr)); } /// /// Add a range of items to the end of the sequence /// /// /// Forces evaluation of the entire lazy sequence so the items /// can be appended. /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Seq Concat(in HashSet items) { if (items.Count == 0) { return this; } var arr = items.ToArray(); return Concat(Seq.FromArray(arr)); } /// /// Add a range of items to the end of the sequence /// /// /// Forces evaluation of the entire lazy sequence so the items /// can be appended. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public Seq Concat(in Stck items) { if (items.Count == 0) { return this; } var arr = items.ToArray(); return Concat(Seq.FromArray(arr)); } /// /// Add a range of items to the end of the sequence /// /// /// Forces evaluation of the entire lazy sequence so the items /// can be appended. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public Seq Concat(IReadOnlyCollection items) { if (items.Count == 0) { return this; } var arr = items.ToArray(); return Concat(Seq.FromArray(arr)); } /// /// Add a range of items to the end of the sequence /// /// /// Forces evaluation of the entire lazy sequence so the items /// can be appended. /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Seq Concat(in Seq rhs) { switch(Value.Type) { case SeqType.Empty: // lhs is empty, so just return rhs return rhs; case SeqType.Lazy: switch (rhs.Value.Type) { // lhs lazy, rhs empty // return lhs case SeqType.Empty: return this; // lhs lazy, rhs lazy // return SeqConcat case SeqType.Lazy: return new Seq(new SeqConcat(Seq(value, rhs.value))); // lhs lazy, rhs strict // force lhs to be strict and concat the two case SeqType.Strict: return new Seq(((SeqStrict)value.Strict()).Append((SeqStrict)rhs.value)); // lhs lazy, rhs concat // prepend rhs with lhs case SeqType.Concat: return new Seq(((SeqConcat)rhs.value).ConsSeq(value)); } break; case SeqType.Strict: switch (rhs.Value.Type) { // lhs strict, rhs empty // return lhs case SeqType.Empty: return this; // lhs strict, rhs lazy // return SeqConcat case SeqType.Lazy: return new Seq(new SeqConcat(Seq(value, rhs.value))); // lhs strict, rhs strict // append the two case SeqType.Strict: return new Seq(((SeqStrict)value).Append((SeqStrict)rhs.value)); // lhs strict, rhs concat // prepend rhs with lhs case SeqType.Concat: return new Seq(((SeqConcat)rhs.value).ConsSeq(value)); } break; case SeqType.Concat: switch (rhs.Value.Type) { // lhs concat, rhs empty // return lhs case SeqType.Empty: return this; // lhs concat, rhs lazy || lhs concat, rhs strict // add rhs to concat case SeqType.Lazy: case SeqType.Strict: return new Seq(((SeqConcat)value).AddSeq(rhs.value)); // lhs concat, rhs concat // add rhs to concat case SeqType.Concat: return new Seq(((SeqConcat)value).AddSeqRange(((SeqConcat)rhs.value).ms)); } break; } throw new NotSupportedException(); } /// /// Prepend an item to the sequence /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] internal Seq Cons(A value) => new (Value.Cons(value)); /// /// Head item in the sequence. /// /// /// If `IsEmpty` is `true` then Head is undefined and therefore returns `None` /// public Option Head { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => Value.IsEmpty ? None : Value.Head; } /// /// Tail of the sequence /// public Seq Tail { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new (Value.Tail); } /// /// Get all items except the last one /// public Seq Init { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new (Value.Init); } /// /// Last item in sequence /// public Option Last { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => Value.IsEmpty ? None : Some(Value.Last); } /// /// Returns true if the sequence is empty /// /// /// For lazy streams this will have to peek at the first /// item. So, the first item will be consumed. /// public bool IsEmpty { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => value?.IsEmpty ?? true; } /// /// Returns the number of items in the sequence /// /// Number of items in the sequence public int Count { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => value?.Count ?? 0; } /// /// Alias of `Count` /// /// Number of items in the sequence public int Length { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => value?.Count ?? 0; } /// /// Stream as an enumerable /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public IEnumerable AsEnumerable() => this; /* /// /// Stream as an enumerable /// [Pure] public StreamT AsStream() where M : Monad => StreamT.Lift(AsEnumerable()); */ /// /// Stream as an enumerable /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Iterable AsIterable() => Iterable.createRange(AsEnumerable()); /// /// Match empty sequence, or multi-item sequence /// /// Return value type /// Match for an empty list /// Match for a non-empty /// Result of match function invoked [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public B Match( Func Empty, Func, B> Tail) => IsEmpty ? Empty() : Tail((A)Head, this.Tail); /// /// Match empty sequence, or one item sequence, or multi-item sequence /// /// Return value type /// Match for an empty list /// Match for a non-empty /// Result of match function invoked [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public B Match( Func Empty, Func Head, Func, B> Tail) => IsEmpty ? Empty() : this.Tail.IsEmpty ? Head((A)this.Head) : Tail((A)this.Head, this.Tail); /// /// Match empty sequence, or multi-item sequence /// /// Return value type /// Match for an empty list /// Match for a non-empty /// Result of match function invoked [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public B Match( Func Empty, Func, B> Seq) => IsEmpty ? Empty() : Seq(this); /// /// Match empty sequence, or one item sequence, or multi-item sequence /// /// Return value type /// Match for an empty list /// Match for a non-empty /// Result of match function invoked [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public B Match( Func Empty, Func Head, Func, B> Tail) => IsEmpty ? Empty() : this.Tail.IsEmpty ? Head((A)this.Head) : Tail(this.Tail); /// /// Impure iteration of the bound values in the structure /// /// /// Returns the original unmodified structure /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Seq Do(Action f) { this.Iter(f); return this; } /// /// Map each element of a structure to an action, evaluate these actions from /// left to right, and collect the results. /// /// /// Traversable structure /// Applicative functor trait /// Bound value (output) [Pure] public K> Traverse(Func> f) where F : Applicative => F.Map(x => x.As(), Traversable.traverse(f, this)); /// /// Map each element of a structure to an action, evaluate these actions from /// left to right, and collect the results. /// /// /// Traversable structure /// Monad trait /// Bound value (output) [Pure] public K> TraverseM(Func> f) where M : Monad => M.Map(x => x.As(), Traversable.traverseM(f, this)); /// /// Map the sequence using the function provided /// /// /// Mapping function /// Mapped sequence [Pure] public Seq Map(Func f) { return new Seq(new SeqLazy(Yield(this))); IEnumerable Yield(Seq items) { foreach (var item in items) { yield return f(item); } } } /// /// Map the sequence using the function provided /// /// /// Exposes an index for each map /// /// Mapped sequence [Pure] public Seq Map(Func f) { return new Seq(new SeqLazy(Yield(this))); IEnumerable Yield(Seq items) { var ix = 0; foreach (var item in items) { yield return f(item, ix); ix++; } } } /// /// Map the sequence using the function provided /// /// /// Mapping function /// Mapped sequence [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Seq Select(Func f) => Map(f); /// /// Monadic bind (flatmap) of the sequence /// /// Bound return value type /// Bind function /// Flat-mapped sequence [Pure] public Seq Bind(Func> f) { static IEnumerable Yield(Seq ma, Func> bnd) { foreach (var a in ma) { foreach (var b in bnd(a)) { yield return b; } } } return new Seq(Yield(this, f)); } /// /// Monadic bind (flatmap) of the sequence /// /// Bound return value type /// Bind function /// Flat-mapped sequence [Pure] public Seq Bind(Func> f) { static IEnumerable Yield(K ma, Func> bnd) { foreach (var a in ma.As()) { foreach (var b in bnd(a).As()) { yield return b; } } } return new Seq(Yield(this, f)); } /// /// Monadic bind (flatmap) of the sequence /// /// Bound return value type /// Bind function /// Flat-mapped sequence [Pure] public Seq SelectMany(Func> bind, Func project) { static IEnumerable Yield(Seq ma, Func> bnd, Func prj) { foreach (var a in ma) { foreach (var b in bnd(a)) { yield return prj(a, b); } } } return new Seq(Yield(this, bind, project)); } /// /// Filter the items in the sequence /// /// Predicate to apply to the items /// Filtered sequence [Pure] public Seq Filter(Func f) { return new Seq(new SeqLazy(Yield(this, f))); static IEnumerable Yield(Seq items, Func f) { foreach (var item in items) { if (f(item)) { yield return item; } } } } /// /// Filter the items in the sequence /// /// Predicate to apply to the items /// Filtered sequence [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Seq Where(Func f) => Filter(f); /// /// Returns true if the supplied predicate returns true for any /// item in the sequence. False otherwise. /// /// Predicate to apply /// True if the supplied predicate returns true for any /// item in the sequence. False otherwise. [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Exists(Func f) => Value.Exists(f); /// /// Returns true if the supplied predicate returns true for all /// items in the sequence. False otherwise. If there is an /// empty sequence then true is returned. /// /// Predicate to apply /// True if the supplied predicate returns true for all /// items in the sequence. False otherwise. If there is an /// empty sequence then true is returned. [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool ForAll(Func f) => Value.ForAll(f); /// /// Returns true if the sequence has items in it /// /// True if the sequence has items in it [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Any() => !IsEmpty; /// /// Inject a value in between each item in the sequence /// /// Sequence to inject values into /// Item to inject /// Bound type /// A sequence with the values injected [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Seq Intersperse(A value) => toSeq(Value.Intersperse(value)); /// /// Get the hash code for all of the items in the sequence, or 0 if empty /// /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public override int GetHashCode() => Value.GetHashCode(); [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public int CompareTo(object? obj) => obj switch { Seq s => CompareTo(s), IEnumerable e => CompareTo(toSeq(e)), _ => 1 }; /// /// Format the collection as `[a, b, c, ...]` /// The ellipsis is used for collections over 50 items /// To get a formatted string with all the items, use `ToFullString` /// or `ToFullArrayString`. /// [Pure] public override string ToString() => Value is SeqLazy ? CollectionFormat.ToShortArrayString(this) : CollectionFormat.ToShortArrayString(this, Count); /// /// Format the collection as `a, b, c, ...` /// [Pure] public string ToFullString(string separator = ", ") => CollectionFormat.ToFullString(this, separator); /// /// Format the collection as `[a, b, c, ...]` /// [Pure] public string ToFullArrayString(string separator = ", ") => CollectionFormat.ToFullArrayString(this, separator); [Pure] public Seq Combine(Seq y) => this + y; /// /// Append operator /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Seq operator +(Seq x, Seq y) => x.Concat(y); /// /// Append operator /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Seq operator +(A x, Seq y) => x.Cons(y); /// /// Append operator /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Seq operator +(Seq x, A y) => x.Add(y); /// /// Append operator /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Seq operator +(Seq x, K y) => x.Concat(y.As()); /// /// Append operator /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Seq operator +(K x, Seq y) => x.As().Concat(y); /// /// Choice operator /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Seq operator |(Seq x, K y) => x.Choose(y).As(); /// /// Choice operator /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Seq operator |(K x, Seq y) => x.Choose(y).As(); /// /// Ordering operator /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator >(Seq x, Seq y) => x.CompareTo(y) > 0; /// /// Ordering operator /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator >=(Seq x, Seq y) => x.CompareTo(y) >= 0; /// /// Ordering operator /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator <(Seq x, Seq y) => x.CompareTo(y) < 0; /// /// Ordering operator /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator <=(Seq x, Seq y) => x.CompareTo(y) <= 0; /// /// Equality operator /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(Seq x, Seq y) => x.Equals(y); /// /// Non-equality operator /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(Seq x, Seq y) => !(x == y); /// /// Equality test /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public override bool Equals(object? obj) => obj switch { Seq s => Equals(s), IEnumerable e => Equals(toSeq(e)), _ => false }; /// /// Equality test /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(Seq rhs) => Equals>(rhs); /// /// Equality test /// [Pure] public bool Equals(Seq rhs) where EqA : Eq { // Differing lengths? if(Count != rhs.Count) return false; // If the hash code has been calculated on both sides then // check for differences if (GetHashCode() != rhs.GetHashCode()) { return false; } // Iterate through both sides using var iterA = GetEnumerator(); using var iterB = rhs.GetEnumerator(); while (iterA.MoveNext() && iterB.MoveNext()) { if (!EqA.Equals(iterA.Current, iterB.Current)) { return false; } } return true; } /// /// Skip count items /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Seq Skip(int amount) => amount < 1 ? this : new Seq(Value.Skip(amount)); /// /// Take count items /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Seq Take(int amount) => amount < 1 ? Empty : new Seq(Value.Take(amount)); /// /// Iterate the sequence, yielding items if they match the predicate /// provided, and stopping as soon as one doesn't /// /// A new sequence with the first items that match the /// predicate [Pure] public Seq TakeWhile(Func pred) { return new Seq(new SeqLazy(Yield(Value, pred))); IEnumerable Yield(IEnumerable xs, Func f) { foreach (var x in xs) { if (!f(x)) break; yield return x; } } } /// /// Iterate the sequence, yielding items if they match the predicate /// provided, and stopping as soon as one doesn't. An index value is /// also provided to the predicate function. /// /// A new sequence with the first items that match the /// predicate [Pure] public Seq TakeWhile(Func pred) { return new Seq(new SeqLazy(Yield(Value, pred))); IEnumerable Yield(IEnumerable xs, Func f) { var i = 0; foreach (var x in xs) { if (!f(x, i)) break; yield return x; i++; } } } /// /// Returns all initial segments of the sequence, shortest first /// /// /// Including the empty sequence /// /// /// /// Seq("a", "b", "c").Inits /// /// > Seq(Seq(), Seq("a"), Seq("a", "b"), Seq("a", "b", "c")) /// /// /// Initial segments of the sequence public Seq> Inits => [Seq()] + NonEmptyInits; /// /// Returns all initial segments of the sequence, shortest first. /// /// /// Not including the empty sequence /// /// /// /// Seq("a", "b", "c").Inits /// /// > Seq(Seq("a"), Seq("a", "b"), Seq("a", "b", "c")) /// /// /// Initial segments of the sequence public Seq> NonEmptyInits { get { var mma = Seq>(); for (var i = 1; i <= Count; i++) { mma = mma.Add(Take(i)); } return mma; } } /// /// Returns all final segments of the argument, longest first. /// /// /// Including the empty sequence /// /// /// /// Seq("a", "b", "c").Tails /// /// > Seq(Seq("a", "b", "c"), Seq("a", "b"), Seq("a"), Seq()) /// /// /// Initial segments of the sequence public Seq> Tails { get { return new Seq>(go(this)); static IEnumerable> go(Seq ma) { while (!ma.IsEmpty) { yield return ma; ma = ma.Tail; } yield return Empty; } } } /// /// Returns all final segments of the argument, longest first. /// /// /// Not including the empty sequence /// /// /// /// Seq("a", "b", "c").Tails /// /// > Seq(Seq("a", "b", "c"), Seq("a", "b"), Seq("a")) /// /// /// Initial segments of the sequence public Seq> NonEmptyTails { get { return new Seq>(go(this)); static IEnumerable> go(Seq ma) { while (!ma.IsEmpty) { yield return ma; ma = ma.Tail; } } } } /// /// Partition a list into two based on a predicate /// /// True if the item goes in the first list, false for the second list /// Pair of lists public (Seq First, Seq Second) Partition(Func predicate) { var f = Seq(); var s = Seq(); foreach (var item in this) { if (predicate(item)) { f = f.Add(item); } else { s = s.Add(item); } } return (f, s); } /// /// Compare to another sequence /// [Pure] public int CompareTo(Seq rhs) => CompareTo>(rhs); /// /// Compare to another sequence /// [Pure] public int CompareTo(Seq rhs) where OrdA : Ord { // Differing lengths? var cmp = Count.CompareTo(rhs.Count); if (cmp != 0) return cmp; // Iterate through both sides using var iterA = GetEnumerator(); using var iterB = rhs.GetEnumerator(); while (iterA.MoveNext() && iterB.MoveNext()) { cmp = OrdA.Compare(iterA.Current, iterB.Current); if (cmp != 0) return cmp; } return 0; } /// /// Force all items lazy to stream /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public Seq Strict() => new (Value.Strict()); [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public IEnumerator GetEnumerator() => Value.GetEnumerator(); [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] IEnumerator IEnumerable.GetEnumerator() => Value.GetEnumerator(); /// /// Implicit conversion from an untyped empty list /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator Seq(SeqEmpty _) => Empty; [Pure] public Seq Cast() { IEnumerable Yield(Seq ma) { foreach (object? item in ma) { if( item is B b) yield return b; } } return Value is IEnumerable mb ? new Seq(mb) : new Seq(Yield(this)); } public static Seq AdditiveIdentity => Empty; } ================================================ FILE: LanguageExt.Core/Immutable Collections/Seq/SeqEmpty.cs ================================================ namespace LanguageExt; /// /// A unit type that represents `Seq.Empty`. This type can be implicitly /// converted to `Seq〈A〉`. /// public readonly struct SeqEmpty { public static SeqEmpty Default = new(); } ================================================ FILE: LanguageExt.Core/Immutable Collections/Seq/SeqLoan.cs ================================================ using System; using System.Buffers; using System.Diagnostics.Contracts; using System.Runtime.CompilerServices; using LanguageExt.ClassInstances; using System.Threading; using LanguageExt.Common; namespace LanguageExt; /// /// Represents a sequence on loan from an `ArrayPool` /// /// /// This supports rapid reading of data for use in streaming situations. As soon as any transformations are made /// the backing data is baked into a Seq of A. This involves cloning the original array (because obviously the rented /// array will be returned to the pool eventually). /// /// You can manually call Dispose() to release the Array, however it also supports a finaliser to return the backing /// array back to its pool of origin. /// /// NOTE: If you call Dispose() whilst using this structure on another thread, behaviour is undefined. /// /// Bound value public class SeqLoan : IDisposable { /// /// Backing data /// internal readonly A[] data; /// /// The pool the date came from /// internal readonly ArrayPool pool; /// /// Start of the sequence /// internal readonly int start; /// /// Known size of the sequence /// internal readonly int count; /// /// Flags whether the rented array has been freed /// internal int freed; /// /// Cached hash code /// int selfHash; /// /// Constructor /// [MethodImpl(MethodImplOptions.AggressiveInlining)] internal SeqLoan(A[] data, ArrayPool pool, int start, int count) { this.data = data; this.pool = pool; this.start = start; this.count = count; } /// /// Create a newly rented array /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static SeqLoan Rent(ArrayPool pool, int size) => new (pool.Rent(size), pool, 0, size); /// /// Create a newly rented array /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static SeqLoan Rent(int size) => Rent(ArrayPool.Shared, size); /// /// Indexer /// public A this[int index] { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => freed == 1 || index < 0 || index >= count ? throw new IndexOutOfRangeException() : data[start + index]; } /// /// Head item in the sequence. NOTE: If `IsEmpty` is true then Head /// is undefined. Call HeadOrNone() if for maximum safety. /// [Pure] public A Head { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => freed == 1 || count == 0 ? throw Exceptions.SequenceEmpty : data[start]; } /// /// Clone to a Seq /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Seq ToSeq() { if(freed == 1) return Seq.Empty; var ndata = new A[data.Length]; Array.Copy(data, start, ndata, start, count); return new Seq(new SeqStrict(ndata, start, count, 0, 0)); } [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public ReadOnlySpan ToReadOnlySpan() => freed == 1 ? new ReadOnlySpan(Array.Empty(), 0, 0) : new ReadOnlySpan(data, start, count); [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Span ToSpan() => freed == 1 ? new Span(Array.Empty(), 0, 0) : new Span(data, start, count); /// /// Tail of the sequence /// [Pure] public Seq Tail { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => freed == 1 ? Seq.Empty : ToSeq().Tail; } [Pure] public Seq Init { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => freed == 1 ? Seq.Empty : ToSeq().Tail; } /// /// Returns true if the sequence is empty /// /// /// For lazy streams this will have to peek at the first /// item. So, the first item will be consumed. /// public bool IsEmpty { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => freed == 1 || count == 0; } /// /// Last item in sequence. Throws if no items in sequence /// public A Last { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => freed == 1 || IsEmpty ? throw Exceptions.SequenceEmpty : data[start + count - 1]; } /// /// Returns the number of items in the sequence /// /// Number of items in the sequence public int Count { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => freed == 1 ? 0 : count; } /// /// Fold the sequence from the first item to the last /// /// State type /// Initial state /// Fold function /// Aggregated state [MethodImpl(MethodImplOptions.AggressiveInlining)] public S Fold(S state, Func f) { if (freed == 1) return state; var end = start + count; for(var i = start; i < end; i++) { state = f(state, data[i]); } return state; } /// /// Fold the sequence from the last item to the first. For /// sequences that are not lazy and are less than 5000 items /// long, FoldBackRec is called instead, because it is faster. /// /// State type /// Initial state /// Fold function /// Aggregated state [MethodImpl(MethodImplOptions.AggressiveInlining)] public S FoldBack(S state, Func f) { if (freed == 1) return state; for (var i = start + count - 1; i >= start; i--) { state = f(state, data[i]); } return state; } /// /// Skip count items /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public Seq Skip(int amount) => freed == 1 ? Seq.Empty : ToSeq().Skip(amount); /// /// Take count items /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public Seq Take(int amount) => freed == 1 ? Seq.Empty : ToSeq().Take(amount); [MethodImpl(MethodImplOptions.AggressiveInlining)] public Enumerator GetEnumerator() => freed == 1 ? new Enumerator(Array.Empty(), 0, 0) : new Enumerator(data, start, count); public Unit Iter(Action f) { var end = start + count; for (var i = start; i < end; i++) { f(data[i]); } return default; } public bool Exists(Func f) { if (freed == 1) return false; var end = start + count; for (var i = start; i < end; i++) { if(f(data[i])) { return true; } } return false; } public bool ForAll(Func f) { if (freed == 1) return false; var end = start + count; for (var i = start; i < end; i++) { if (!f(data[i])) { return false; } } return true; } public Seq Append(Seq right) => freed == 1 ? right : ToSeq() + right; public Seq Append(SeqLoan right) => freed == 1 ? right.ToSeq() : ToSeq() + right.ToSeq(); [MethodImpl(MethodImplOptions.AggressiveInlining)] public override int GetHashCode() => freed == 1 ? 0 : selfHash == 0 ? selfHash = GetHashCode(FNV32.OffsetBasis) : selfHash; [MethodImpl(MethodImplOptions.AggressiveInlining)] int GetHashCode(int offsetBasis) => FNV32.Hash, A>(data, start, count, offsetBasis); [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Dispose() => Free(); [MethodImpl(MethodImplOptions.AggressiveInlining)] ~SeqLoan() => Free(); [MethodImpl(MethodImplOptions.AggressiveInlining)] void Free() { if (Interlocked.CompareExchange(ref freed, 1, 0) == 0) { pool.Return(data, ClearArray); } } static readonly bool ClearArray = !typeof(A).IsValueType; public ref struct Enumerator { readonly A[] data; readonly int count; int index; [MethodImpl(MethodImplOptions.AggressiveInlining)] internal Enumerator(A[] data, int index, int count) { this.data = data; this.count = count; this.index = index; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool MoveNext() { var nindex = index + 1; if (nindex < count) { index = nindex; return true; } else { return false; } } public ref A Current { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => ref data[index]; } } } ================================================ FILE: LanguageExt.Core/Immutable Collections/Seq/Trait/Seq.TraitImpl.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; public partial class Seq : Monad, MonoidK, Alternative, Traversable { static K Monad.Recur(A value, Func>> f) { return toSeq(go()); IEnumerable go() { List values = [value]; List next = []; while (true) { foreach (var x in values) { foreach (var mb in +f(x)) { if (mb.IsDone) { yield return mb.Done; } else { next.Add(mb.Loop); } } } if (next.Count == 0) { break; } else { (values, next) = (next, values); next.Clear(); } } } } static K Monad.Bind(K ma, Func> f) { return new Seq(go()); IEnumerable go() { foreach (var x in ma.As()) { foreach (var y in f(x).As()) { yield return y; } } } } static K Functor.Map(Func f, K ma) { return new Seq(go()); IEnumerable go() { foreach (var x in ma.As()) { yield return f(x); } } } static K Applicative.Pure(A value) => singleton(value); static K Applicative.Apply(K> mf, K ma) { return new Seq(go()); IEnumerable go() { foreach (var f in mf.As()) { foreach (var a in ma.As()) { yield return f(a); } } } } static K Applicative.Apply(K> mf, Memo ma) { return new Seq(go()); IEnumerable go() { foreach (var f in mf.As()) { foreach (var a in ma.Value.As()) { yield return f(a); } } } } static K MonoidK.Empty() => Seq.Empty; static K Alternative.Empty() => Seq.Empty; static K Choice.Choose(K ma, K mb) => ma.As().IsEmpty ? mb : ma; static K Choice.Choose(K ma, Memo mb) => ma.As().IsEmpty ? ~mb : ma; static K SemigroupK.Combine(K ma, K mb) => ma.As() + mb.As(); static int Foldable.Count(K ta) => ta.As().Count; static bool Foldable.IsEmpty(K ta) => ta.As().IsEmpty; static Option Foldable.At(K ta, Index index) { var list = ta.As(); return index.Value >= 0 && index.Value < list.Count ? Some(list[index]) : Option.None; } static Option Foldable.Head(K ta) => ta.As().Head; static Option Foldable.Last(K ta) => ta.As().Last; static S Foldable.FoldWhile(Func> f, Func<(S State, A Value), bool> predicate, S state, K ta) { foreach (var x in ta.As()) { if (!predicate((state, x))) return state; state = f(x)(state); } return state; } static S Foldable.FoldBackWhile(Func> f, Func<(S State, A Value), bool> predicate, S state, K ta) { foreach (var x in ta.As().Rev()) { if (!predicate((state, x))) return state; state = f(state)(x); } return state; } static K> Traversable.Traverse(Func> f, K ta) { return Foldable.fold(add, F.Pure(Seq.Empty), ta) .Map(bs => bs.Kind()); Func>, K>> add(A value) => state => Applicative.lift((bs, b) => bs.Add(b), state, f(value)); } static K> Traversable.TraverseM(Func> f, K ta) { return Foldable.fold(add, F.Pure(Seq.Empty), ta) .Map(bs => bs.Kind()); Func>, K>> add(A value) => state => state.Bind( bs => f(value).Bind( b => F.Pure(bs.Add(b)))); } static Fold Foldable.FoldStep(K ta, S initialState) { // ReSharper disable once GenericEnumeratorNotDisposed var iter = ta.As().GetEnumerator(); return go(initialState); Fold go(S state) => iter.MoveNext() ? Fold.Loop(state, iter.Current, go) : Fold.Done(state); } static Fold Foldable.FoldStepBack(K ta, S initialState) { // ReSharper disable once GenericEnumeratorNotDisposed var iter = ta.As().Reverse().GetEnumerator(); return go(initialState); Fold go(S state) => iter.MoveNext() ? Fold.Loop(state, iter.Current, go) : Fold.Done(state); } } ================================================ FILE: LanguageExt.Core/Immutable Collections/Set/Extensions/Set.Extensions.MapApply.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public static partial class SetExtensions { /// /// Functor map operation /// /// /// Unwraps the value within the functor, passes it to the map function `f` provided, and /// then takes the mapped value and wraps it back up into a new functor. /// /// Functor to map /// Mapping function /// Mapped functor public static Set Map(this Func f, K ma) => Functor.map(f, ma).As(); /// /// Functor map operation /// /// /// Unwraps the value within the functor, passes it to the map function `f` provided, and /// then takes the mapped value and wraps it back up into a new functor. /// /// Functor to map /// Mapping function /// Mapped functor public static Set Map(this Func f, Set ma) => Functor.map(f, ma).As(); /// /// Applicative action: runs the first applicative, ignores the result, and returns the second applicative /// public static Set Action(this Set ma, Set mb) => Applicative.action(ma, mb).As(); /// /// Applicative action: runs the first applicative, ignores the result, and returns the second applicative /// public static Set Action(this K ma, K mb) => Applicative.action(ma, mb).As(); /// /// Applicative functor apply operation /// /// /// Unwraps the value within the `ma` applicative-functor, passes it to the unwrapped function(s) within `mf`, and /// then takes the resulting value and wraps it back up into a new applicative-functor. /// /// Value(s) applicative functor /// Mapping function(s) /// Mapped applicative functor public static Set Apply(this Set> mf, K ma) => Applicative.apply(mf, ma).As(); /// /// Applicative functor apply operation /// /// /// Unwraps the value within the `ma` applicative-functor, passes it to the unwrapped function(s) within `mf`, and /// then takes the resulting value and wraps it back up into a new applicative-functor. /// /// Value(s) applicative functor /// Mapping function(s) /// Mapped applicative functor public static Set Apply(this K> mf, K ma) => Applicative.apply(mf, ma).As(); } ================================================ FILE: LanguageExt.Core/Immutable Collections/Set/Extensions/Set.Extensions.cs ================================================ #pragma warning disable CS0693 // Type parameter has the same name as the type parameter from outer type using System.Diagnostics.Contracts; using System.Linq; using LanguageExt.Traits; namespace LanguageExt; public static partial class SetExtensions { public static Set As(this K ma) => (Set)ma; /// /// Convert to a queryable /// [Pure] public static IQueryable AsQueryable(this Set source) => // NOTE TO FUTURE ME: Don't delete this thinking it's not needed! source.Value.AsQueryable(); } ================================================ FILE: LanguageExt.Core/Immutable Collections/Set/Internal/Set.Internal.cs ================================================ #pragma warning disable CS0693 // Type parameter has the same name as the type parameter from outer type using System; using System.Linq; using System.Collections.Generic; using System.Collections; using static LanguageExt.Prelude; using System.Diagnostics.Contracts; using LanguageExt.Traits; namespace LanguageExt; /// /// Immutable set /// AVL tree implementation /// AVL tree is a self-balancing binary search tree. /// [wikipedia.org/wiki/AVL_tree](http://en.wikipedia.org/wiki/AVL_tree) /// /// List item type [Serializable] internal class SetInternal : IEnumerable, IEquatable> where OrdA : Ord { public static readonly SetInternal Empty = new (); readonly SetItem set; int hashCode; /// /// Default ctor /// internal SetInternal() => set = SetItem.Empty; /// /// Ctor that takes a root element /// /// internal SetInternal(SetItem root) { set = root; } /// /// Ctor from an enumerable /// public SetInternal(IEnumerable items) : this(items, SetModuleM.AddOpt.TryAdd) { } public override int GetHashCode() => hashCode == 0 ? hashCode = FNV32.Hash(AsIterable()) : hashCode; public Iterable AsIterable() { IEnumerable Yield() { using var iter = GetEnumerator(); while (iter.MoveNext()) { yield return iter.Current; } } return Iterable.createRange(Yield()); } public Iterable Skip(int amount) { return Iterable.createRange(Go()); IEnumerable Go() { using var iter = new SetModule.SetEnumerator(set, false, amount); while (iter.MoveNext()) { yield return iter.Current; } } } /// /// Ctor that takes an initial (distinct) set of items /// /// internal SetInternal(IEnumerable items, SetModuleM.AddOpt option) { set = SetItem.Empty; foreach (var item in items) { set = SetModuleM.Add(set, item, option); } } /// /// Ctor that takes an initial (distinct) set of items /// /// internal SetInternal(ReadOnlySpan items, SetModuleM.AddOpt option) { set = SetItem.Empty; foreach (var item in items) { set = SetModuleM.Add(set, item, option); } } /// /// Number of items in the set /// [Pure] public int Count => set.Count; [Pure] public Option Min => set.IsEmpty ? None : SetModule.Min(set); [Pure] public Option Max => set.IsEmpty ? None : SetModule.Max(set); /// /// Add an item to the set /// /// Value to add to the set /// New set with the item added [Pure] public SetInternal Add(A value) => new (SetModule.Add(set,value)); /// /// Attempt to add an item to the set. If an item already /// exists then return the Set as-is. /// /// Value to add to the set /// New set with the item maybe added [Pure] public SetInternal TryAdd(A value) => Contains(value) ? this : Add(value); /// /// Add an item to the set. If an item already /// exists then replace it. /// /// Value to add to the set /// New set with the item maybe added [Pure] public SetInternal AddOrUpdate(A value) => new (SetModule.AddOrUpdate(set, value)); [Pure] public SetInternal AddRange(IEnumerable xs) { if(Count == 0) { return new SetInternal(xs, SetModuleM.AddOpt.ThrowOnDuplicate); } var set = this; foreach(var x in xs) { set = set.Add(x); } return set; } [Pure] public SetInternal TryAddRange(IEnumerable xs) { if (Count == 0) { return new SetInternal(xs, SetModuleM.AddOpt.TryAdd); } var set = this; foreach (var x in xs) { set = set.TryAdd(x); } return set; } [Pure] public SetInternal AddOrUpdateRange(IEnumerable xs) { if (Count == 0) { return new SetInternal(xs, SetModuleM.AddOpt.TryUpdate); } var set = this; foreach (var x in xs) { set = set.AddOrUpdate(x); } return set; } /// /// Get the number of elements in the set /// /// Number of elements [Pure] public int Length() => Count; /// /// Attempts to find an item in the set. /// /// Value to find /// Some(T) if found, None otherwise [Pure] public Option Find(A value) => SetModule.TryFind(set, value); /// /// Retrieve the value from predecessor item to specified key /// /// Key to find /// Found key [Pure] public Option FindPredecessor(A key) => SetModule.TryFindPredecessor(set, key); /// /// Retrieve the value from exact key, or if not found, the predecessor item /// /// Key to find /// Found key [Pure] public Option FindOrPredecessor(A key) => SetModule.TryFindOrPredecessor(set, key); /// /// Retrieve the value from next item to specified key /// /// Key to find /// Found key [Pure] public Option FindSuccessor(A key) => SetModule.TryFindSuccessor(set, key); /// /// Retrieve the value from exact key, or if not found, the next item /// /// Key to find /// Found key [Pure] public Option FindOrSuccessor(A key) => SetModule.TryFindOrSuccessor(set, key); /// /// Retrieve a range of values /// /// Range start (inclusive) /// Range to (inclusive) /// Throws ArgumentNullException the keyFrom or keyTo are null /// Range of values [Pure] public Iterable FindRange(A keyFrom, A keyTo) { if (isnull(keyFrom)) throw new ArgumentNullException(nameof(keyFrom)); if (isnull(keyTo)) throw new ArgumentNullException(nameof(keyTo)); return OrdA.Compare(keyFrom, keyTo) > 0 ? SetModule.FindRange(set, keyTo, keyFrom).AsIterable() : SetModule.FindRange(set, keyFrom, keyTo).AsIterable(); } /// /// Returns the elements that are in both this and other /// [Pure] public SetInternal Intersect(IEnumerable other) { var root = SetItem.Empty; foreach (var item in other) { if (Contains(item)) { root = SetModuleM.Add(root, item, SetModuleM.AddOpt.TryAdd); } } return new SetInternal(root); } /// /// Returns this - other. Only the items in this that are not in /// other will be returned. /// [Pure] public SetInternal Except(SetInternal rhs) { var root = SetItem.Empty; foreach (var item in this) { if (!rhs.Contains(item)) { root = SetModuleM.Add(root, item, SetModuleM.AddOpt.TryAdd); } } return new SetInternal(root); } /// /// Returns this - other. Only the items in this that are not in /// other will be returned. /// [Pure] public SetInternal Except(IEnumerable other) => Except(new SetInternal(other)); /// /// Only items that are in one set or the other will be returned. /// If an item is in both, it is dropped. /// [Pure] public SetInternal SymmetricExcept(SetInternal rhs) { var root = SetItem.Empty; foreach (var item in this) { if (!rhs.Contains(item)) { root = SetModuleM.Add(root, item, SetModuleM.AddOpt.TryAdd); } } foreach (var item in rhs) { if (!Contains(item)) { root = SetModuleM.Add(root, item, SetModuleM.AddOpt.TryAdd); } } return new SetInternal(root); } /// /// Only items that are in one set or the other will be returned. /// If an item is in both, it is dropped. /// [Pure] public SetInternal SymmetricExcept(IEnumerable other) => SymmetricExcept(new SetInternal(other)); /// /// Finds the union of two sets and produces a new set with /// the results /// /// Other set to union with /// A set which contains all items from both sets [Pure] public SetInternal Union(IEnumerable other) { var root = SetItem.Empty; foreach(var item in this) { root = SetModuleM.Add(root, item, SetModuleM.AddOpt.TryAdd); } foreach (var item in other) { root = SetModuleM.Add(root, item, SetModuleM.AddOpt.TryAdd); } return new SetInternal(root); } /// /// Clears the set /// /// An empty set [Pure] public SetInternal Clear() => Empty; /// /// Get enumerator /// /// IEnumerator T [Pure] public IEnumerator GetEnumerator() => new SetModule.SetEnumerator(set, false, 0); /// /// Removes an item from the set (if it exists) /// /// Value to check /// New set with item removed [Pure] public SetInternal Remove(A value) => new (SetModule.Remove(set, value)); /// /// Applies a function 'folder' to each element of the collection, threading an accumulator /// argument through the computation. The fold function takes the state argument, and /// applies the function 'folder' to it and the first element of the set. Then, it feeds this /// result into the function 'folder' along with the second element, and so on. It returns the /// final result. (Aggregate in LINQ) /// /// State type /// Initial state /// Fold function /// Aggregate value [Pure] public S Fold(S state, Func folder) => SetModule.Fold(set,state,folder); /// /// Applies a function 'folder' to each element of the collection (from last element to first), /// threading an aggregate state through the computation. The fold function takes the state /// argument, and applies the function 'folder' to it and the first element of the set. Then, /// it feeds this result into the function 'folder' along with the second element, and so on. It /// returns the final result. /// /// State type /// Initial state /// Fold function /// Aggregate value [Pure] public S FoldBack(S state, Func folder) => SetModule.FoldBack(set, state, folder); /// /// Maps the values of this set into a new set of values using the /// mapper function to tranform the source values. /// /// Mapped element type /// Mapping function /// Mapped Set [Pure] public SetInternal Map(Func f) where OrdB : Ord => new (AsIterable().Map(f)); /// /// Maps the values of this set into a new set of values using the /// mapper function to tranform the source values. /// /// Mapped element type /// Mapping function /// Mapped Set [Pure] public SetInternal Map(Func f) => new (AsIterable().Map(f)); /// /// Filters items from the set using the predicate. If the predicate /// returns True for any item then it remains in the set, otherwise /// it's dropped. /// /// Predicate /// Filtered enumerable [Pure] public SetInternal Filter(Func pred) => new (AsIterable().Filter(pred), SetModuleM.AddOpt.TryAdd); /// /// Check the existence of an item in the set using a /// predicate. /// /// Note this scans the entire set. /// Predicate /// True if predicate returns true for any item [Pure] public bool Exists(Func pred) => SetModule.Exists(set, pred); /// /// Returns True if the value is in the set /// /// Value to check /// True if the item 'value' is in the Set 'set' [Pure] public bool Contains(A value) => SetModule.Contains(set, value); /// /// Returns true if both sets contain the same elements /// /// Other distinct set to compare /// True if the sets are equal [Pure] public bool SetEquals(IEnumerable other) { var rhs = new SetInternal(other); if (rhs.Count != Count) return false; foreach (var item in rhs) { if (!Contains(item)) return false; } return true; } /// /// True if the set has no elements /// [Pure] public bool IsEmpty => Count == 0; /// /// IsReadOnly - Always true /// [Pure] public bool IsReadOnly { get { return true; } } /// /// Returns True if 'other' is a proper subset of this set /// /// True if 'other' is a proper subset of this set [Pure] public bool IsProperSubsetOf(IEnumerable other) { if (IsEmpty) { return other.Any(); } var otherSet = new Set(other); if (Count >= otherSet.Count) { return false; } int matches = 0; bool extraFound = false; foreach (A item in otherSet) { if (Contains(item)) { matches++; } else { extraFound = true; } if (matches == Count && extraFound) { return true; } } return false; } /// /// Returns True if 'other' is a proper superset of this set /// /// True if 'other' is a proper superset of this set [Pure] public bool IsProperSupersetOf(IEnumerable other) { if (IsEmpty) { return false; } int matchCount = 0; foreach (A item in other) { matchCount++; if (!Contains(item)) { return false; } } return Count > matchCount; } /// /// Returns True if 'other' is a superset of this set /// /// True if 'other' is a superset of this set [Pure] public bool IsSubsetOf(IEnumerable other) { if (IsEmpty) { return true; } var otherSet = new SetInternal(other); int matches = 0; foreach (A item in otherSet) { if (Contains(item)) { matches++; } } return matches == Count; } /// /// Returns True if 'other' is a superset of this set /// /// True if 'other' is a superset of this set [Pure] public bool IsSupersetOf(IEnumerable other) { foreach (A item in other) { if (!Contains(item)) { return false; } } return true; } /// /// Returns True if other overlaps this set /// /// Element type /// Set A /// Set B /// True if other overlaps this set [Pure] public bool Overlaps(IEnumerable other) { if (IsEmpty) { return false; } foreach (A item in other) { if (Contains(item)) { return true; } } return false; } /// /// Copy the items from the set into the specified array /// /// Array to copy to /// Index into the array to start public void CopyTo(A[] array, int index) { if (array == null) throw new ArgumentNullException(nameof(array)); if (index < 0 || index > array.Length) throw new IndexOutOfRangeException(); if (index + Count > array.Length) throw new IndexOutOfRangeException(); foreach (var element in this) { array[index++] = element; } } /// /// Copy the items from the set into the specified array /// /// Array to copy to /// Index into the array to start public void CopyTo(Array array, int index) { if (array == null) throw new ArgumentNullException(nameof(array)); if (index < 0 || index > array.Length) throw new IndexOutOfRangeException(); if (index + Count > array.Length) throw new IndexOutOfRangeException(); foreach (var element in this) { array.SetValue(element, index++); } } /// /// Add operator + performs a union of the two sets /// /// Left hand side set /// Right hand side set /// Unioned set [Pure] public static SetInternal operator +(SetInternal lhs, SetInternal rhs) => lhs.Append(rhs); /// /// Append performs a union of the two sets /// /// Right hand side set /// Unioned set [Pure] public SetInternal Append(SetInternal rhs) => Union(rhs.AsIterable()); /// /// Subtract operator - performs a subtract of the two sets /// /// Left hand side set /// Right hand side set /// Subtractd set [Pure] public static SetInternal operator -(SetInternal lhs, SetInternal rhs) => lhs.Subtract(rhs); /// /// Subtract operator - performs a subtract of the two sets /// /// Right hand side set /// Subtractd set [Pure] public SetInternal Subtract(SetInternal rhs) { if (Count == 0) return Empty; if (rhs.Count == 0) return this; if (rhs.Count < Count) { var self = this; foreach (var item in rhs) { self = self.Remove(item); } return self; } else { var root = SetItem.Empty; foreach (var item in this) { if (!rhs.Contains(item)) { root = SetModuleM.Add(root, item, SetModuleM.AddOpt.TryAdd); } } return new SetInternal(root); } } /// /// Equality test /// /// Other set to test /// True if sets are equal [Pure] public bool Equals(SetInternal? other) => other is not null && SetEquals(other.AsIterable()); [Pure] public int CompareTo(SetInternal other) { var cmp = Count.CompareTo(other.Count); if (cmp != 0) return cmp; using var iterA = GetEnumerator(); using var iterB = other.GetEnumerator(); while (iterA.MoveNext() && iterB.MoveNext()) { cmp = OrdA.Compare(iterA.Current, iterB.Current); if (cmp != 0) return cmp; } return 0; } [Pure] public int CompareTo(SetInternal other) where OrdAlt : Ord { var cmp = Count.CompareTo(other.Count); if (cmp != 0) return cmp; using var iterA = GetEnumerator(); using var iterB = other.GetEnumerator(); while (iterA.MoveNext() && iterB.MoveNext()) { cmp = OrdAlt.Compare(iterA.Current, iterB.Current); if (cmp != 0) return cmp; } return 0; } IEnumerator IEnumerable.GetEnumerator() => new SetModule.SetEnumerator(set, false, 0); } internal class SetItem { public static readonly SetItem Empty = new (0, 0, default!, default!, default!); public bool IsEmpty => Count == 0; public int Count; public byte Height; public SetItem Left; public SetItem Right; /// /// Ctor /// internal SetItem(byte height, int count, K key, SetItem left, SetItem right) { Count = count; Height = height; Key = key; Left = left; Right = right; } [Pure] internal int BalanceFactor => Count == 0 ? 0 : Right.Height - Left.Height; [Pure] public K Key { get; internal set; } } internal static class SetModuleM { public enum AddOpt { ThrowOnDuplicate, TryAdd, TryUpdate } public static SetItem Add(SetItem node, K key, AddOpt option) where OrdK : Ord { if (node.IsEmpty) { return new SetItem(1, 1, key, SetItem.Empty, SetItem.Empty); } var cmp = OrdK.Compare(key, node.Key); if (cmp < 0) { node.Left = Add(node.Left, key, option); return Balance(node); } else if (cmp > 0) { node.Right = Add(node.Right, key, option); return Balance(node); } else if (option == AddOpt.TryAdd) { // Already exists, but we don't care return node; } else if (option == AddOpt.TryUpdate) { // Already exists, and we want to update the content node.Key = key; return node; } else { throw new ArgumentException("An element with the same key already exists in the Map"); } } public static SetItem Balance(SetItem node) { node.Height = (byte)(1 + Math.Max(node.Left.Height, node.Right.Height)); node.Count = 1 + node.Left.Count + node.Right.Count; return node.BalanceFactor >= 2 ? node.Right.BalanceFactor < 0 ? DblRotLeft(node) : RotLeft(node) : node.BalanceFactor <= -2 ? node.Left.BalanceFactor > 0 ? DblRotRight(node) : RotRight(node) : node; } public static SetItem DblRotRight(SetItem node) { node.Left = RotLeft(node.Left); return RotRight(node); } public static SetItem DblRotLeft(SetItem node) { node.Right = RotRight(node.Right); return RotLeft(node); } public static SetItem RotRight(SetItem node) { if (node.IsEmpty || node.Left.IsEmpty) return node; var y = node; var x = y.Left; var t2 = x.Right; x.Right = y; y.Left = t2; y.Height = (byte)(1 + Math.Max(y.Left.Height, y.Right.Height)); x.Height = (byte)(1 + Math.Max(x.Left.Height, x.Right.Height)); y.Count = 1 + y.Left.Count + y.Right.Count; x.Count = 1 + x.Left.Count + x.Right.Count; return x; } public static SetItem RotLeft(SetItem node) { if (node.IsEmpty || node.Right.IsEmpty) return node; var x = node; var y = x.Right; var t2 = y.Left; y.Left = x; x.Right = t2; x.Height = (byte)(1 + Math.Max(x.Left.Height, x.Right.Height)); y.Height = (byte)(1 + Math.Max(y.Left.Height, y.Right.Height)); x.Count = 1 + x.Left.Count + x.Right.Count; y.Count = 1 + y.Left.Count + y.Right.Count; return y; } } internal static class SetModule { [Pure] public static S Fold(SetItem node, S state, Func folder) { if (node.IsEmpty) { return state; } state = Fold(node.Left, state, folder); state = folder(state, node.Key); state = Fold(node.Right, state, folder); return state; } [Pure] public static S FoldBack(SetItem node, S state, Func folder) { if (node.IsEmpty) { return state; } state = FoldBack(node.Right, state, folder); state = folder(state, node.Key); state = FoldBack(node.Left, state, folder); return state; } [Pure] public static bool ForAll(SetItem node, Func pred) => node.IsEmpty || pred(node.Key) && ForAll(node.Left, pred) && ForAll(node.Right, pred); [Pure] public static bool Exists(SetItem node, Func pred) => !node.IsEmpty && (pred(node.Key) || Exists(node.Left, pred) || Exists(node.Right, pred)); [Pure] public static SetItem Add(SetItem node, K key) where OrdK : Ord { if (node.IsEmpty) { return new SetItem(1, 1, key, SetItem.Empty, SetItem.Empty); } var cmp = OrdK.Compare(key, node.Key); if (cmp < 0) { return Balance(Make(node.Key, Add(node.Left, key), node.Right)); } else if (cmp > 0) { return Balance(Make(node.Key, node.Left, Add(node.Right, key))); } else { throw new ArgumentException("An element with the same key already exists in the set"); } } [Pure] public static SetItem TryAdd(SetItem node, K key) where OrdK : Ord { if (node.IsEmpty) { return new SetItem(1, 1, key, SetItem.Empty, SetItem.Empty); } var cmp = OrdK.Compare(key, node.Key); if (cmp < 0) { return Balance(Make(node.Key, TryAdd(node.Left, key), node.Right)); } else if (cmp > 0) { return Balance(Make(node.Key, node.Left, TryAdd(node.Right, key))); } else { return node; } } [Pure] public static SetItem AddOrUpdate(SetItem node, K key) where OrdK : Ord { if (node.IsEmpty) { return new SetItem(1, 1, key, SetItem.Empty, SetItem.Empty); } var cmp = OrdK.Compare(key, node.Key); if (cmp < 0) { return Balance(Make(node.Key, TryAdd(node.Left, key), node.Right)); } else if (cmp > 0) { return Balance(Make(node.Key, node.Left, TryAdd(node.Right, key))); } else { return new SetItem(node.Height, node.Count, key, node.Left, node.Right); } } [Pure] public static SetItem AddTreeToRight(SetItem node, SetItem toAdd) => node.IsEmpty ? toAdd : Balance(Make(node.Key, node.Left, AddTreeToRight(node.Right, toAdd))); [Pure] public static SetItem Remove(SetItem node, K key) where OrdK : Ord { if (node.IsEmpty) { return node; } var cmp = OrdK.Compare(key, node.Key); if (cmp < 0) { return Balance(Make(node.Key, Remove(node.Left, key), node.Right)); } else if (cmp > 0) { return Balance(Make(node.Key, node.Left, Remove(node.Right, key))); } else { return Balance(AddTreeToRight(node.Left, node.Right)); } } [Pure] public static bool Contains(SetItem node, K key) where OrdK : Ord { if (node.IsEmpty) { return false; } var cmp = OrdK.Compare(key, node.Key); if (cmp < 0) { return Contains(node.Left, key); } else if (cmp > 0) { return Contains(node.Right, key); } else { return true; } } [Pure] public static K Find(SetItem node, K key) where OrdK : Ord { if (node.IsEmpty) { throw new ArgumentException("Key not found in set"); } var cmp = OrdK.Compare(key, node.Key); if (cmp < 0) { return Find(node.Left, key); } else if (cmp > 0) { return Find(node.Right, key); } else { return node.Key; } } /// /// TODO: I suspect this is suboptimal, it would be better with a custom Enumerator /// that maintains a stack of nodes to retrace. /// [Pure] public static IEnumerable FindRange(SetItem node, K a, K b) where OrdK : Ord { if (node.IsEmpty) { yield break; } if (OrdK.Compare(node.Key, a) < 0) { foreach (var item in FindRange(node.Right, a, b)) { yield return item; } } else if (OrdK.Compare(node.Key, b) > 0) { foreach (var item in FindRange(node.Left, a, b)) { yield return item; } } else { foreach (var item in FindRange(node.Left, a, b)) { yield return item; } yield return node.Key; foreach (var item in FindRange(node.Right, a, b)) { yield return item; } } } [Pure] public static Option TryFind(SetItem node, K key) where OrdK : Ord { if (node.IsEmpty) { return None; } var cmp = OrdK.Compare(key, node.Key); if (cmp < 0) { return TryFind(node.Left, key); } else if (cmp > 0) { return TryFind(node.Right, key); } else { return Some(node.Key); } } [Pure] public static SetItem Skip(SetItem node, int amount) { if (amount == 0 || node.IsEmpty) { return node; } if (amount >= node.Count) { return SetItem.Empty; } if (!node.Left.IsEmpty && node.Left.Count == amount) { return Balance(Make(node.Key, SetItem.Empty, node.Right)); } if (!node.Left.IsEmpty && node.Left.Count == amount - 1) { return node.Right; } if (node.Left.IsEmpty) { return Skip(node.Right, amount - 1); } var newleft = Skip(node.Left, amount); var remaining = amount - node.Left.Count - newleft.Count; if (remaining > 0) { return Skip(Balance(Make(node.Key, newleft, node.Right)), remaining); } else { return Balance(Make(node.Key, newleft, node.Right)); } } [Pure] public static SetItem Make(K k, SetItem l, SetItem r) => new ((byte)(1 + Math.Max(l.Height, r.Height)), l.Count + r.Count + 1, k, l, r); [Pure] public static SetItem Balance(SetItem node) => node.BalanceFactor >= 2 ? node.Right.BalanceFactor < 0 ? DblRotLeft(node) : RotLeft(node) : node.BalanceFactor <= -2 ? node.Left.BalanceFactor > 0 ? DblRotRight(node) : RotRight(node) : node; [Pure] public static SetItem RotRight(SetItem node) => node.IsEmpty || node.Left.IsEmpty ? node : Make(node.Left.Key, node.Left.Left, Make(node.Key, node.Left.Right, node.Right)); [Pure] public static SetItem RotLeft(SetItem node) => node.IsEmpty || node.Right.IsEmpty ? node : Make(node.Right.Key, Make(node.Key, node.Left, node.Right.Left), node.Right.Right); [Pure] public static SetItem DblRotRight(SetItem node) => node.IsEmpty ? node : RotRight(Make(node.Key, RotLeft(node.Left), node.Right)); [Pure] public static SetItem DblRotLeft(SetItem node) => node.IsEmpty ? node : RotLeft(Make(node.Key, node.Left, RotRight(node.Right))); internal static Option Max(SetItem node) => node.Right.IsEmpty ? node.Key : Max(node.Right); internal static Option Min(SetItem node) => node.Left.IsEmpty ? node.Key : Min(node.Left); internal static Option TryFindPredecessor(SetItem root, A key) where OrdA : Ord { Option predecessor = None; var current = root; if (root.IsEmpty) { return None; } do { var cmp = OrdA.Compare(key, current.Key); if (cmp < 0) { current = current.Left; } else if (cmp > 0) { predecessor = current.Key; current = current.Right; } else { break; } } while (!current.IsEmpty); if(!current.IsEmpty && !current.Left.IsEmpty) { predecessor = Max(current.Left); } return predecessor; } internal static Option TryFindOrPredecessor(SetItem root, A key) where OrdA : Ord { Option predecessor = None; var current = root; if (root.IsEmpty) { return None; } do { var cmp = OrdA.Compare(key, current.Key); if (cmp < 0) { current = current.Left; } else if (cmp > 0) { predecessor = current.Key; current = current.Right; } else { return current.Key; } } while (!current.IsEmpty); if (!current.IsEmpty && !current.Left.IsEmpty) { predecessor = Max(current.Left); } return predecessor; } internal static Option TryFindSuccessor(SetItem root, A key) where OrdA : Ord { Option successor = None; var current = root; if (root.IsEmpty) { return None; } do { var cmp = OrdA.Compare(key, current.Key); if (cmp < 0) { successor = current.Key; current = current.Left; } else if (cmp > 0) { current = current.Right; } else { break; } } while (!current.IsEmpty); if (!current.IsEmpty && !current.Right.IsEmpty) { successor = Min(current.Right); } return successor; } internal static Option TryFindOrSuccessor(SetItem root, A key) where OrdA : Ord { Option successor = None; var current = root; if (root.IsEmpty) { return None; } do { var cmp = OrdA.Compare(key, current.Key); if (cmp < 0) { successor = current.Key; current = current.Left; } else if (cmp > 0) { current = current.Right; } else { return current.Key; } } while (!current.IsEmpty); if (!current.IsEmpty && !current.Right.IsEmpty) { successor = Min(current.Right); } return successor; } public class SetEnumerator : IEnumerator { internal struct NewStack : New[]> { public SetItem[] New() => new SetItem[32]; } int stackDepth; SetItem[]? stack; readonly SetItem map; int left; readonly bool rev; readonly int start; public SetEnumerator(SetItem root, bool rev, int start) { this.rev = rev; this.start = start; map = root; stack = Pool[]>.Pop(); NodeCurrent = default!; Reset(); } private SetItem NodeCurrent { get; set; } public K Current => NodeCurrent.Key; object IEnumerator.Current => NodeCurrent.Key!; public void Dispose() { if (stack is not null) { Pool[]>.Push(stack); stack = default!; } } private SetItem Next(SetItem node) => rev ? node.Left : node.Right; private SetItem Prev(SetItem node) => rev ? node.Right : node.Left; private void Push(SetItem node) { while (!node.IsEmpty) { stack![stackDepth] = node; stackDepth++; node = Prev(node); } } public bool MoveNext() { if (left > 0 && stackDepth > 0) { stackDepth--; NodeCurrent = stack![stackDepth]; Push(Next(NodeCurrent)); left--; return true; } NodeCurrent = default!; return false; } public void Reset() { var skip = rev ? map.Count - start - 1 : start; stackDepth = 0; NodeCurrent = map; left = map.Count; while (!NodeCurrent.IsEmpty && skip != Prev(NodeCurrent).Count) { if (skip < Prev(NodeCurrent).Count) { stack![stackDepth] = NodeCurrent; stackDepth++; NodeCurrent = Prev(NodeCurrent); } else { skip -= Prev(NodeCurrent).Count + 1; NodeCurrent = Next(NodeCurrent); } } if (!NodeCurrent.IsEmpty) { stack![stackDepth] = NodeCurrent; stackDepth++; } } } } ================================================ FILE: LanguageExt.Core/Immutable Collections/Set/Prelude/Set.Prelude.mapapply.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public static partial class Prelude { /// /// Functor map operation /// /// /// Unwraps the value within the functor, passes it to the map function `f` provided, and /// then takes the mapped value and wraps it back up into a new functor. /// /// Functor to map /// Mapping function /// Mapped functor public static Set map(Func f, K ma) => Functor.map(f, ma).As(); /// /// Applicative action: runs the first applicative, ignores the result, and returns the second applicative /// public static Set action(K ma, K mb) => Applicative.action(ma, mb).As(); /// /// Applicative functor apply operation /// /// /// Unwraps the value within the `ma` applicative-functor, passes it to the unwrapped function(s) within `mf`, and /// then takes the resulting value and wraps it back up into a new applicative-functor. /// /// Value(s) applicative functor /// Mapping function(s) /// Mapped applicative functor public static Set apply(K> mf, K ma) => Applicative.apply(mf, ma).As(); } ================================================ FILE: LanguageExt.Core/Immutable Collections/Set/Set.Module.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics.Contracts; namespace LanguageExt; public partial class Set { /// /// True if the set has no elements /// /// Element type /// True if the set has no elements [Pure] public static bool isEmpty(Set set) => set.IsEmpty; /// /// Singleton set /// [Pure] public static Set singleton(A value) => [value]; /// /// Create a new empty set /// /// Element type /// Empty set [Pure] public static Set create() => Set.Empty; /// /// Create a new set pre-populated with the items in range /// /// Element type /// Range of items /// Set [Pure] public static Set createRange(IEnumerable range) => new (range); /// /// Create a new set pre-populated with the items in range /// /// Element type /// Range of items /// Set [Pure] public static Set createRange(ReadOnlySpan range) => range.IsEmpty ? Set.Empty : new (range); /// /// Create a new empty set /// /// Element type /// Empty set [Pure] public static Set empty() => Set.Empty; /// /// Add an item to the set /// /// Element type /// Set to add item to /// Value to add to the set /// New set with the item added [Pure] public static Set add(Set set, T value) => set.Add(value); /// /// Attempt to add an item to the set. If an item already /// exists then return the Set as-is. /// /// Element type /// Set to add item to /// Value to add to the set /// New set with the item maybe added [Pure] public static Set tryAdd(Set set, T value) => set.TryAdd(value); /// /// Add an item to the set. If an item already /// exists then replace it. /// /// Element type /// Set to add item to /// Value to add to the set /// New set with the item maybe added [Pure] public static Set addOrUpdate(Set set, T value) => set.AddOrUpdate(value); /// /// Atomically adds a range of items to the set. /// /// Null is not allowed for a Key /// Range of keys to add /// Throws ArgumentException if any of the keys already exist /// Throws ArgumentNullException if any of the keys are null /// New Set with the items added [Pure] public static Set addRange(Set set, IEnumerable range) => set.AddRange(range); /// /// Atomically adds a range of items to the set. If an item already exists, it's ignored. /// /// Null is not allowed for a Key /// Range of keys to add /// Throws ArgumentNullException if any of the keys are null /// New Set with the items added [Pure] public static Set tryAddRange(Set set, IEnumerable range) => set.TryAddRange(range); /// /// Atomically adds a range of items to the set. If an item already exists then replace it. /// /// Null is not allowed for a Key /// Range of keys to add /// Throws ArgumentNullException if any of the keys are null /// New Set with the items added [Pure] public static Set addOrUpdateRange(Set set, IEnumerable range) => set.AddOrUpdateRange(range); /// /// Attempts to find an item in the set. /// /// Element type /// Set /// Value to find /// Some(T) if found, None otherwise [Pure] public static Option find(Set set, T value) => set.Find(value); /// /// Check the existence of an item in the set using a /// predicate. /// /// Note this scans the entire set. /// Element type /// Set /// Predicate /// True if predicate returns true for any item [Pure] public static bool exists(Set set, Func pred) => set.Exists(pred); /// /// Returns true if both sets contain the same elements /// [Pure] public static bool equals(Set setA, Set setB) => setA.SetEquals(setB); /// /// Get the number of elements in the set /// /// Element type /// Set /// Number of elements [Pure] public static int length(Set set) => set.Count; /// /// Returns setA - setB. Only the items in setA that are not in /// setB will be returned. /// [Pure] public static Set subtract(Set setA, Set setB) => setA.Except(setB); /// /// Finds the union of two sets and produces a new set with /// the results /// /// Element type /// Set A /// Set A /// A set which contains all items from both sets [Pure] public static Set union(Set setA, Set setB) => setA.Union(setB); /// /// Filters items from the set using the predicate. If the predicate /// returns True for any item then it remains in the set, otherwise /// it's dropped. /// /// Element type /// Set /// Predicate /// Filtered enumerable [Pure] public static Set filter(Set set, Func pred) => set.Filter(pred); /// /// Applies a function 'folder' to each element of the collection, threading an accumulator /// argument through the computation. The fold function takes the state argument, and /// applies the function 'folder' to it and the first element of the set. Then, it feeds this /// result into the function 'folder' along with the second element, and so on. It returns the /// final result. (Aggregate in LINQ) /// /// State type /// Set element type /// Set to fold /// Initial state /// Fold function /// Aggregate value [Pure] public static S fold(Set set, S state, Func folder) => set.Fold(state, folder); /// /// Applies a function 'folder' to each element of the collection (from last element to first), /// threading an aggregate state through the computation. The fold function takes the state /// argument, and applies the function 'folder' to it and the first element of the set. Then, /// it feeds this result into the function 'folder' along with the second element, and so on. It /// returns the final result. /// /// State type /// Set element type /// Set to fold /// Initial state /// Fold function /// Aggregate value [Pure] public static S foldBack(Set set, S state, Func folder) => set.FoldBack(state, folder); /// /// Returns the elements that are in both setA and setB /// [Pure] public static Set intersect(Set setA, IEnumerable setB) => setA.Intersect(setB); /// /// Returns this - other. Only the items in this that are not in /// other will be returned. /// [Pure] public static Set except(Set setA, IEnumerable setB) => setA.Except(setB); /// /// Only items that are in one set or the other will be returned. /// If an item is in both, it is dropped. /// [Pure] public static Set symmetricExcept(Set setA, IEnumerable setB) => setA.SymmetricExcept(setB); /// /// Maps the values of this set into a new set of values using the /// mapper function to tranform the source values. /// /// Element type /// Mapped element type /// Set /// Mapping function /// Mapped enumerable [Pure] public static Set map(Set set, Func mapper) => set.Map(mapper); /// /// Returns True if the value is in the set /// /// Element type /// Set /// Value to check /// True if the item 'value' is in the Set 'set' [Pure] public static bool contains(Set set, T value) => set.Contains(value); /// /// Removes an item from the set (if it exists) /// /// Element type /// Set /// Value to check /// New set with item removed [Pure] public static Set remove(Set set, T value) => set.Remove(value); /// /// Returns True if setB is a subset of setA /// /// Element type /// Set A /// Set B /// True is setB is a subset of setA [Pure] public static bool isSubset(Set setA, IEnumerable setB) => setA.IsSubsetOf(setB); /// /// Returns True if setB is a superset of setA /// /// Element type /// Set A /// Set B /// True is setB is a superset of setA [Pure] public static bool isSuperset(Set setA, IEnumerable setB) => setA.IsSupersetOf(setB); /// /// Returns True if setB is a proper subset of setA /// /// Element type /// Set A /// Set B /// True is setB is a proper subset of setA [Pure] public static bool isProperSubset(Set setA, IEnumerable setB) => setA.IsProperSubsetOf(setB); /// /// Returns True if setB is a proper superset of setA /// /// Element type /// Set A /// Set B /// True is setB is a proper subset of setA [Pure] public static bool isProperSuperset(Set setA, IEnumerable setB) => setA.IsProperSupersetOf(setB); /// /// Returns True if setA overlaps setB /// /// Element type /// Set A /// Set B /// True if setA overlaps setB [Pure] public static bool overlaps(Set setA, IEnumerable setB) => setA.Overlaps(setB); } ================================================ FILE: LanguageExt.Core/Immutable Collections/Set/Set.Ord.Module.cs ================================================ using System; using System.Linq; using System.Collections.Generic; using System.Diagnostics.Contracts; using LanguageExt.Traits; namespace LanguageExt; /// /// Immutable set module /// AVL tree implementation /// AVL tree is a self-balancing binary search tree. /// http://en.wikipedia.org/wiki/AVL_tree /// public partial class Set { /// /// True if the set has no elements /// /// Element type /// True if the set has no elements [Pure] public static bool isEmpty(Set set) where OrdT : Ord => set.IsEmpty; /// /// Create a new single item set /// /// Element type /// Singleton set [Pure] public static Set singleton(A value) where OrdA : Ord => [value]; /// /// Create a new empty set /// /// Element type /// Empty set [Pure] public static Set create() where OrdT : Ord => Set.Empty; /// /// Create a new set pre-populated with the items in range /// /// Element type /// Range of items /// Set [Pure] public static Set createRange(IEnumerable range) where OrdT : Ord => new (range); /// /// Create a new set pre-populated with the items in range /// /// Element type /// Range of items /// Set [Pure] public static Set createRange(ReadOnlySpan range) where OrdA : Ord => range.IsEmpty ? Set.Empty : new (range); /// /// Create a new empty set /// /// Element type /// Empty set [Pure] public static Set empty() where OrdT : Ord => Set.Empty; /// /// Add an item to the set /// /// Element type /// Set to add item to /// Value to add to the set /// New set with the item added [Pure] public static Set add(Set set, T value) where OrdT : Ord => set.Add(value); /// /// Attempt to add an item to the set. If an item already /// exists then return the Set as-is. /// /// Element type /// Set to add item to /// Value to add to the set /// New set with the item maybe added [Pure] public static Set tryAdd(Set set, T value) where OrdT : Ord => set.TryAdd(value); /// /// Add an item to the set. If an item already /// exists then replace it. /// /// Element type /// Set to add item to /// Value to add to the set /// New set with the item maybe added [Pure] public static Set addOrUpdate(Set set, T value) where OrdT : Ord => set.AddOrUpdate(value); /// /// Atomically adds a range of items to the set. /// /// Null is not allowed for a Key /// Range of keys to add /// Throws ArgumentException if any of the keys already exist /// Throws ArgumentNullException if any of the keys are null /// New Set with the items added [Pure] public static Set addRange(Set set, IEnumerable range) where OrdT : Ord => set.AddRange(range); /// /// Atomically adds a range of items to the set. If an item already exists, it's ignored. /// /// Null is not allowed for a Key /// Range of keys to add /// Throws ArgumentNullException if any of the keys are null /// New Set with the items added [Pure] public static Set tryAddRange(Set set, IEnumerable range) where OrdT : Ord => set.TryAddRange(range); /// /// Atomically adds a range of items to the set. If an item already exists then replace it. /// /// Null is not allowed for a Key /// Range of keys to add /// Throws ArgumentNullException if any of the keys are null /// New Set with the items added [Pure] public static Set addOrUpdateRange(Set set, IEnumerable range) where OrdT : Ord => set.AddOrUpdateRange(range); /// /// Attempts to find an item in the set. /// /// Element type /// Set /// Value to find /// Some(T) if found, None otherwise [Pure] public static Option find(Set set, T value) where OrdT : Ord => set.Find(value); /// /// Check the existence of an item in the set using a /// predicate. /// /// Note this scans the entire set. /// Element type /// Set /// Predicate /// True if predicate returns true for any item [Pure] public static bool exists(Set set, Func pred) where OrdT : Ord => set.Exists(pred); /// /// Returns true if both sets contain the same elements /// [Pure] public static bool equals(Set setA, Set setB) where OrdT : Ord => setA.SetEquals(setB); /// /// Get the number of elements in the set /// /// Element type /// Set /// Number of elements [Pure] public static int length(Set set) where OrdT : Ord => set.Count(); /// /// Returns setA - setB. Only the items in setA that are not in /// setB will be returned. /// [Pure] public static Set subtract(Set setA, Set setB) where OrdT : Ord => setA.Except(setB); /// /// Finds the union of two sets and produces a new set with /// the results /// /// Element type /// Set A /// Set A /// A set which contains all items from both sets [Pure] public static Set union(Set setA, Set setB) where OrdT : Ord => setA.Union(setB); /// /// Filters items from the set using the predicate. If the predicate /// returns True for any item then it remains in the set, otherwise /// it's dropped. /// /// Element type /// Set /// Predicate /// Filtered enumerable [Pure] public static Set filter(Set set, Func pred) where OrdT : Ord => set.Filter(pred); /// /// Applies a function 'folder' to each element of the collection, threading an accumulator /// argument through the computation. The fold function takes the state argument, and /// applies the function 'folder' to it and the first element of the set. Then, it feeds this /// result into the function 'folder' along with the second element, and so on. It returns the /// final result. (Aggregate in LINQ) /// /// State type /// Set element type /// Set to fold /// Initial state /// Fold function /// Aggregate value [Pure] public static S fold(Set set, S state, Func folder) where OrdT : Ord => set.Fold(state, folder); /// /// Applies a function 'folder' to each element of the collection (from last element to first), /// threading an aggregate state through the computation. The fold function takes the state /// argument, and applies the function 'folder' to it and the first element of the set. Then, /// it feeds this result into the function 'folder' along with the second element, and so on. It /// returns the final result. /// /// State type /// Set element type /// Set to fold /// Initial state /// Fold function /// Aggregate value [Pure] public static S foldBack(Set set, S state, Func folder) where OrdT : Ord => set.FoldBack(state, folder); /// /// Returns the elements that are in both setA and setB /// [Pure] public static Set intersect(Set setA, Set setB) where OrdT : Ord => setA.Intersect(setB); /// /// Returns this - other. Only the items in this that are not in /// other will be returned. /// [Pure] public static Set except(Set setA, Set setB) where OrdT : Ord => setA.Except(setB); /// /// Only items that are in one set or the other will be returned. /// If an item is in both, it is dropped. /// [Pure] public static Set symmetricExcept(Set setA, Set setB) where OrdT : Ord => setA.SymmetricExcept(setB); /// /// Maps the values of this set into a new set of values using the /// mapper function to tranform the source values. /// /// Element type /// Mapped element type /// Set /// Mapping function /// Mapped enumerable [Pure] public static Set map(Set set, Func mapper) where OrdT : Ord where OrdR : Ord => set.Map(mapper); /// /// Maps the values of this set into a new set of values using the /// mapper function to tranform the source values. /// /// Element type /// Mapped element type /// Set /// Mapping function /// Mapped enumerable [Pure] public static Set map(Set set, Func mapper) where OrdT : Ord => set.Map(mapper); /// /// Returns True if the value is in the set /// /// Element type /// Set /// Value to check /// True if the item 'value' is in the Set 'set' [Pure] public static bool contains(Set set, T value) where OrdT : Ord => set.Contains(value); /// /// Removes an item from the set (if it exists) /// /// Element type /// Set /// Value to check /// New set with item removed [Pure] public static Set remove(Set set, T value) where OrdT : Ord => set.Remove(value); /// /// Returns True if setB is a subset of setA /// /// Element type /// Set A /// Set B /// True is setB is a subset of setA [Pure] public static bool isSubset(Set setA, Set setB) where OrdT : Ord => setA.IsSubsetOf(setB); /// /// Returns True if setB is a superset of setA /// /// Element type /// Set A /// Set B /// True is setB is a superset of setA [Pure] public static bool isSuperset(Set setA, Set setB) where OrdT : Ord => setA.IsSupersetOf(setB); /// /// Returns True if setB is a proper subset of setA /// /// Element type /// Set A /// Set B /// True is setB is a proper subset of setA [Pure] public static bool isProperSubset(Set setA, Set setB) where OrdT : Ord => setA.IsProperSubsetOf(setB); /// /// Returns True if setB is a proper superset of setA /// /// Element type /// Set A /// Set B /// True is setB is a proper subset of setA [Pure] public static bool isProperSuperset(Set setA, Set setB) where OrdT : Ord => setA.IsProperSupersetOf(setB); /// /// Returns True if setA overlaps setB /// /// Element type /// Set A /// Set B /// True if setA overlaps setB [Pure] public static bool overlaps(Set setA, Set setB) where OrdT : Ord => setA.Overlaps(setB); } ================================================ FILE: LanguageExt.Core/Immutable Collections/Set/Set.Ord.cs ================================================ using System; using System.Collections.Generic; using System.Collections; using System.Diagnostics.Contracts; using System.Runtime.CompilerServices; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; /// /// Immutable set /// AVL tree implementation /// AVL tree is a self-balancing binary search tree. /// [wikipedia.org/wiki/AVL_tree](http://en.wikipedia.org/wiki/AVL_tree) /// /// Set item type [Serializable] [CollectionBuilder(typeof(Set), nameof(Set.createRange))] public readonly struct Set : IEnumerable, IEquatable>, IComparable> where OrdA : Ord { public static readonly Set Empty = new (SetInternal.Empty); readonly SetInternal value; SetInternal Value => value ?? Empty.Value; /// /// Ctor from an enumerable /// public Set(IEnumerable items) : this(items, true) { } /// /// Ctor from an enumerable /// public Set(ReadOnlySpan items) : this(items, true) { } /// /// Default ctor /// internal Set(SetInternal set) => value = set; /// /// Ctor that takes a root element /// /// internal Set(SetItem root) => value = new SetInternal(root); /// /// Ctor that takes an initial (distinct) set of items /// /// public Set(IEnumerable items, bool tryAdd) => value = new SetInternal( items, tryAdd ? SetModuleM.AddOpt.TryAdd : SetModuleM.AddOpt.ThrowOnDuplicate); /// /// Ctor that takes an initial (distinct) set of items /// /// public Set(ReadOnlySpan items, bool tryAdd) => value = new SetInternal( items, tryAdd ? SetModuleM.AddOpt.TryAdd : SetModuleM.AddOpt.ThrowOnDuplicate); static Set Wrap(SetInternal set) => new (set); static Set Wrap(SetInternal set) where OrdB : Ord => new (set); /// /// Reference version for use in pattern-matching /// /// /// /// Empty collection = null /// Singleton collection = A /// More = (A, Seq〈A〉) -- head and tail /// /// var res = set.Case switch /// { /// /// (var x, var xs) => ..., /// A value => ..., /// _ => ... /// } /// /// [Pure] public object? Case => IsEmpty ? null : toSeq(Value).Case; /// /// Add an item to the set /// /// Value to add to the set /// New set with the item added [Pure] public Set Add(A value) => Wrap(Value.Add(value)); /// /// Attempt to add an item to the set. If an item already /// exists then return the Set as-is. /// /// Value to add to the set /// New set with the item maybe added [Pure] public Set TryAdd(A value) => Wrap(Value.TryAdd(value)); /// /// Add an item to the set. If an item already /// exists then replace it. /// /// Value to add to the set /// New set with the item maybe added [Pure] public Set AddOrUpdate(A value) => Wrap(Value.AddOrUpdate(value)); /// /// Atomically adds a range of items to the set. /// /// Null is not allowed for a Key /// Range of keys to add /// Throws ArgumentException if any of the keys already exist /// Throws ArgumentNullException if any of the keys are null /// New Set with the items added [Pure] public Set AddRange(IEnumerable range) => Wrap(Value.AddRange(range)); /// /// Atomically adds a range of items to the set. If an item already exists, it's ignored. /// /// Null is not allowed for a Key /// Range of keys to add /// Throws ArgumentNullException if any of the keys are null /// New Set with the items added [Pure] public Set TryAddRange(IEnumerable range) => Wrap(Value.TryAddRange(range)); /// /// Atomically adds a range of items to the set. If an item already exists then replace it. /// /// Null is not allowed for a Key /// Range of keys to add /// Throws ArgumentNullException if any of the keys are null /// New Set with the items added [Pure] public Set AddOrUpdateRange(IEnumerable range) => Wrap(Value.AddOrUpdateRange(range)); /// /// Attempts to find an item in the set. /// /// Value to find /// Some(T) if found, None otherwise [Pure] public Option Find(A value) => Value.Find(value); /// /// Retrieve the value from previous item to specified key /// /// Key to find /// Found key [Pure] public Option FindPredecessor(A key) => Value.FindPredecessor(key); /// /// Retrieve the value from exact key, or if not found, the previous item /// /// Key to find /// Found key [Pure] public Option FindExactOrPredecessor(A key) => Value.FindOrPredecessor(key); /// /// Retrieve the value from next item to specified key /// /// Key to find /// Found key [Pure] public Option FindSuccessor(A key) => Value.FindSuccessor(key); /// /// Retrieve the value from exact key, or if not found, the next item /// /// Key to find /// Found key [Pure] public Option FindExactOrSuccessor(A key) => Value.FindOrSuccessor(key); /// /// Retrieve a range of values /// /// Range start (inclusive) /// Range to (inclusive) /// Throws ArgumentNullException the keyFrom or keyTo are null /// Range of values [Pure] public Iterable FindRange(A keyFrom, A keyTo) => Value.FindRange(keyFrom, keyTo); /// /// Returns the elements that are in both this and other /// [Pure] public Set Intersect(Set other) => Wrap(Value.Intersect(other)); /// /// Returns this - other. Only the items in this that are not in /// other will be returned. /// [Pure] public Set Except(Set other) => Wrap(Value.Except(other.Value)); /// /// Returns this - other. Only the items in this that are not in /// other will be returned. /// [Pure] public Set Except(IEnumerable other) => other is Set rhs ? Except(rhs) : Wrap(Value.Except(other)); /// /// Only items that are in one set or the other will be returned. /// If an item is in both, it is dropped. /// [Pure] public Set SymmetricExcept(Set other) => Wrap(Value.SymmetricExcept(other)); /// /// Only items that are in one set or the other will be returned. /// If an item is in both, it is dropped. /// [Pure] public Set SymmetricExcept(IEnumerable other) => other is Set rhs ? SymmetricExcept(rhs) : Wrap(Value.SymmetricExcept(other)); /// /// Finds the union of two sets and produces a new set with /// the results /// /// Other set to union with /// A set which contains all items from both sets [Pure] public Set Union(Set other) => Wrap(Value.Union(other)); /// /// Clears the set /// /// An empty set [Pure] public Set Clear() => Empty; /// /// Get enumerator /// /// IEnumerator T [Pure] public IEnumerator GetEnumerator() => // ReSharper disable once NotDisposedResourceIsReturned Value.GetEnumerator(); /// /// Get enumerator /// /// IEnumerator [Pure] IEnumerator IEnumerable.GetEnumerator() => // ReSharper disable once NotDisposedResourceIsReturned Value.GetEnumerator(); /// /// Removes an item from the set (if it exists) /// /// Value to check /// New set with item removed [Pure] public Set Remove(A value) => Wrap(Value.Remove(value)); /// /// Applies a function 'folder' to each element of the collection, threading an accumulator /// argument through the computation. The fold function takes the state argument, and /// applies the function 'folder' to it and the first element of the set. Then, it feeds this /// result into the function 'folder' along with the second element, and so on. It returns the /// final result. (Aggregate in LINQ) /// /// State type /// Initial state /// Fold function /// Aggregate value [Pure] public S Fold(S state, Func folder) => Value.Fold(state,folder); /// /// Applies a function 'folder' to each element of the collection (from last element to first), /// threading an aggregate state through the computation. The fold function takes the state /// argument, and applies the function 'folder' to it and the first element of the set. Then, /// it feeds this result into the function 'folder' along with the second element, and so on. It /// returns the final result. /// /// State type /// Initial state /// Fold function /// Aggregate value [Pure] public S FoldBack(S state, Func folder) => Value.FoldBack(state, folder); /// /// Impure iteration of the bound values in the structure /// /// /// Returns the original unmodified structure /// public Set Do(Action f) { this.AsIterable().Iter(f); return this; } /// /// Maps the values of this set into a new set of values using the /// mapper function to tranform the source values. /// /// Mapped element type /// Mapping function /// Mapped Set [Pure] public Set Map(Func map) where OrdB : Ord => Wrap(Value.Map(map)); /// /// Maps the values of this set into a new set of values using the /// mapper function to tranform the source values. /// /// Mapped element type /// Mapping function /// Mapped Set [Pure] public Set Map(Func map) => Wrap(Value.Map(map)); /// /// Filters items from the set using the predicate. If the predicate /// returns True for any item then it remains in the set, otherwise /// it's dropped. /// /// Predicate /// Filtered enumerable [Pure] public Set Filter(Func pred) => Wrap(Value.Filter(pred)); /// /// Check the existence of an item in the set using a /// predicate. /// /// Note this scans the entire set. /// Predicate /// True if predicate returns true for any item [Pure] public bool Exists(Func pred) => Value.Exists(pred); /// /// Returns True if the value is in the set /// /// Value to check /// True if the item 'value' is in the Set 'set' [Pure] public bool Contains(A value) => Value.Contains(value); /// /// Returns true if both sets contain the same elements /// /// Other distinct set to compare /// True if the sets are equal [Pure] public bool SetEquals(Set other) => Value.SetEquals(other); /// /// Is the set empty /// [Pure] public bool IsEmpty { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => value?.IsEmpty ?? true; } /// /// Number of items in the set /// [Pure] public int Count { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => value?.Count ?? 0; } /// /// Alias of Count /// [Pure] public int Length { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => value?.Count ?? 0; } /// /// Returns True if 'other' is a proper subset of this set /// /// True if 'other' is a proper subset of this set [Pure] public bool IsProperSubsetOf(Set other) => Value.IsProperSubsetOf(other); /// /// Returns True if 'other' is a proper superset of this set /// /// True if 'other' is a proper superset of this set [Pure] public bool IsProperSupersetOf(Set other) => Value.IsProperSupersetOf(other); /// /// Returns True if 'other' is a superset of this set /// /// True if 'other' is a superset of this set [Pure] public bool IsSubsetOf(Set other) => Value.IsSubsetOf(other); /// /// Returns True if 'other' is a superset of this set /// /// True if 'other' is a superset of this set [Pure] public bool IsSupersetOf(Set other) => Value.IsSupersetOf(other); /// /// Returns True if other overlaps this set /// /// Element type /// Set A /// Set B /// True if other overlaps this set [Pure] public bool Overlaps(Set other) => Value.Overlaps(other); /// /// Copy the items from the set into the specified array /// /// Array to copy to /// Index into the array to start public void CopyTo(A[] array, int index) => Value.CopyTo(array, index); /// /// Copy the items from the set into the specified array /// /// Array to copy to /// Index into the array to start public void CopyTo(Array array, int index) => Value.CopyTo(array, index); /// /// Add operator - performs a union of the two sets /// /// Left hand side set /// Right hand side set /// Unioned set [Pure] public static Set operator +(Set lhs, Set rhs) => Wrap(lhs.Value + rhs.Value); /// /// Add operator - performs a union of the two sets /// /// Right hand side set /// Unioned set [Pure] public Set Append(Set rhs) => Wrap(Value.Append(rhs.Value)); /// /// Subtract operator - performs a subtract of the two sets /// /// Left hand side set /// Right hand side set /// Subtracted set [Pure] public static Set operator -(Set lhs, Set rhs) => Wrap(lhs.Value - rhs.Value); /// /// Subtract operator - performs a subtract of the two sets /// /// Right hand side set /// Subtracted set [Pure] public Set Subtract(Set rhs) => Wrap(Value.Subtract(rhs.Value)); /// /// Equality test /// /// Other set to test /// True if sets are equal [Pure] public bool Equals(Set other) => Value.SetEquals(other.Value.AsIterable()); /// /// Equality operator /// /// Left hand side set /// Right hand side set /// True if the two sets are equal [Pure] public static bool operator ==(Set lhs, Set rhs) => lhs.Equals(rhs); /// /// Non-equality operator /// /// Left hand side set /// Right hand side set /// True if the two sets are equal [Pure] public static bool operator !=(Set lhs, Set rhs) => !lhs.Equals(rhs); [Pure] public static bool operator <(Set lhs, Set rhs) => lhs.CompareTo(rhs) < 0; [Pure] public static bool operator <=(Set lhs, Set rhs) => lhs.CompareTo(rhs) <= 0; [Pure] public static bool operator >(Set lhs, Set rhs) => lhs.CompareTo(rhs) > 0; [Pure] public static bool operator >=(Set lhs, Set rhs) => lhs.CompareTo(rhs) >= 0; /// /// Equality override /// [Pure] public override bool Equals(object? obj) => obj is Set @as && Equals(@as); /// /// Get the hash code. Calculated from all items in the set. /// /// /// The hash-code is cached after the first read. /// [Pure] public override int GetHashCode() => Value.GetHashCode(); [Pure] public override string ToString() => $"Set[{Count}]"; [Pure] public Seq ToSeq() => toSeq(this); [Pure] public Iterable AsEnumerable() => Iterable.createRange(this); [Pure] public Set Where(Func pred) => Filter(pred); [Pure] public Set Bind(Func> f) where OrdB : Ord { var self = this; IEnumerable Yield() { foreach (var x in self.AsEnumerable()) { foreach (var y in f(x)) { yield return y; } } } return new Set(Yield(), true); } [Pure] public Set Bind(Func> f) { var self = this; IEnumerable Yield() { foreach (var x in self.AsEnumerable()) { foreach (var y in f(x)) { yield return y; } } } return new Set(Yield(), true); } [Pure] public Iterable Skip(int amount) => Value.Skip(amount); [Pure] public int CompareTo(Set other) => Value.CompareTo(other.Value); /// /// Implicit conversion from an untyped empty list /// public static implicit operator Set(SeqEmpty _) => Empty; /// /// Creates a new map from a range/slice of this map /// /// Range start (inclusive) /// Range to (inclusive) /// [Pure] public Set Slice(A keyFrom, A keyTo) => new (FindRange(keyFrom, keyTo)); /// /// Find the lowest ordered item in the set /// [Pure] public Option Min => Value.Min; /// /// Find the highest ordered item in the set /// [Pure] public Option Max => Value.Max; } ================================================ FILE: LanguageExt.Core/Immutable Collections/Set/Set.cs ================================================ using System; using System.Collections.Generic; using System.Collections; using static LanguageExt.Prelude; using System.Diagnostics.Contracts; using System.Numerics; using System.Runtime.CompilerServices; using LanguageExt.ClassInstances; using LanguageExt.Traits; namespace LanguageExt; /// /// Immutable set /// AVL tree implementation /// AVL tree is a self-balancing binary search tree. /// [wikipedia.org/wiki/AVL_tree](http://en.wikipedia.org/wiki/AVL_tree) /// /// Set item type [Serializable] [CollectionBuilder(typeof(Set), nameof(Set.createRange))] public readonly struct Set : IEquatable>, IComparable>, IComparable, IComparisonOperators, Set, bool>, IAdditionOperators, Set, Set>, ISubtractionOperators, Set, Set>, IAdditiveIdentity, Set>, IReadOnlyCollection, Monoid>, K { public static Set Empty { get; } = new(SetInternal, A>.Empty); readonly SetInternal, A> value; internal SetInternal, A> Value => value ?? Empty.Value; /// /// Ctor from an enumerable /// public Set(IEnumerable items) : this(items, true) { } /// /// Ctor from an enumerable /// public Set(ReadOnlySpan items) : this(items, true) { } /// /// Default ctor /// internal Set(SetInternal, A> set) => value = set; /// /// Ctor that takes a root element /// /// internal Set(SetItem root) => value = new SetInternal, A>(root); /// /// Ctor that takes an initial (distinct) set of items /// /// public Set(IEnumerable items, bool tryAdd) => value = new SetInternal, A>( items, tryAdd ? SetModuleM.AddOpt.TryAdd : SetModuleM.AddOpt.ThrowOnDuplicate); /// /// Ctor that takes an initial (distinct) set of items /// /// public Set(ReadOnlySpan items, bool tryAdd) => value = new SetInternal, A>( items, tryAdd ? SetModuleM.AddOpt.TryAdd : SetModuleM.AddOpt.ThrowOnDuplicate); /// /// Item at index lens /// [Pure] public static Lens, bool> item(A key) => Lens, bool>.New( Get: la => la.Contains(key), Set: a => la => a ? la.AddOrUpdate(key) : la.Remove(key)); /// /// Lens map /// [Pure] public static Lens, Set> map(Lens lens) => Lens, Set>.New( Get: la => la.Map(lens.Get), Set: lb => la => { foreach (var item in lb) { la = la.Find(item).Match(Some: x => la.AddOrUpdate(lens.Set(x, item)), None: () => la); } return la; }); static Set Wrap(SetInternal, A> set) => new (set); static Set Wrap(SetInternal, B> set) => new (set); /// /// Reference version for use in pattern-matching /// /// /// /// Empty collection = null /// Singleton collection = A /// More = (A, Seq〈A〉) -- head and tail /// /// var res = set.Case switch /// { /// /// (var x, var xs) => ..., /// A value => ..., /// _ => ... /// } /// /// [Pure] public object? Case => IsEmpty ? null : toSeq(Value).Case; /* /// /// Stream as an enumerable /// [Pure] public StreamT AsStream() where M : Monad => StreamT.Lift(AsEnumerable()); */ /// /// Add an item to the set /// /// Value to add to the set /// New set with the item added [Pure] public Set Add(A value) => Wrap(Value.Add(value)); /// /// Attempt to add an item to the set. If an item already /// exists then return the Set as-is. /// /// Value to add to the set /// New set with the item maybe added [Pure] public Set TryAdd(A value) => Wrap(Value.TryAdd(value)); /// /// Add an item to the set. If an item already /// exists then replace it. /// /// Value to add to the set /// New set with the item maybe added [Pure] public Set AddOrUpdate(A value) => Wrap(Value.AddOrUpdate(value)); /// /// Atomically adds a range of items to the set. /// /// Null is not allowed for a Key /// Range of keys to add /// Throws ArgumentException if any of the keys already exist /// Throws ArgumentNullException if any of the keys are null /// New Set with the items added [Pure] public Set AddRange(IEnumerable range) => Wrap(Value.AddRange(range)); /// /// Atomically adds a range of items to the set. If an item already exists, it's ignored. /// /// Null is not allowed for a Key /// Range of keys to add /// Throws ArgumentNullException if any of the keys are null /// New Set with the items added [Pure] public Set TryAddRange(IEnumerable range) => Wrap(Value.TryAddRange(range)); /// /// Atomically adds a range of items to the set. If an item already exists then replace it. /// /// Null is not allowed for a Key /// Range of keys to add /// Throws ArgumentNullException if any of the keys are null /// New Set with the items added [Pure] public Set AddOrUpdateRange(IEnumerable range) => Wrap(Value.AddOrUpdateRange(range)); /// /// Attempts to find an item in the set. /// /// Value to find /// Some(T) if found, None otherwise [Pure] public Option Find(A value) => Value.Find(value); /// /// Retrieve a range of values /// /// Range start (inclusive) /// Range to (inclusive) /// Throws ArgumentNullException the keyFrom or keyTo are null /// Range of values [Pure] public Iterable FindRange(A keyFrom, A keyTo) => Value.FindRange(keyFrom, keyTo); /// /// Retrieve the value from previous item to specified key /// /// Key to find /// Found key [Pure] public Option FindPredecessor(A key) => Value.FindPredecessor(key); /// /// Retrieve the value from exact key, or if not found, the previous item /// /// Key to find /// Found key [Pure] public Option FindExactOrPredecessor(A key) => Value.FindOrPredecessor(key); /// /// Retrieve the value from next item to specified key /// /// Key to find /// Found key [Pure] public Option FindSuccessor(A key) => Value.FindSuccessor(key); /// /// Retrieve the value from exact key, or if not found, the next item /// /// Key to find /// Found key [Pure] public Option FindExactOrSuccessor(A key) => Value.FindOrSuccessor(key); /// /// Returns the elements that are in both this and other /// [Pure] public Set Intersect(IEnumerable other) => Wrap(Value.Intersect(other)); /// /// Returns this - other. Only the items in this that are not in /// other will be returned. /// [Pure] public Set Except(IEnumerable other) => other is Set set ? Except(set) : Wrap(Value.Except(other)); /// /// Returns this - other. Only the items in this that are not in /// other will be returned. /// [Pure] public Set Except(Set other) => Wrap(Value.Except(other.Value)); /// /// Only items that are in one set or the other will be returned. /// If an item is in both, it is dropped. /// [Pure] public Set SymmetricExcept(IEnumerable other) => other is Set set ? SymmetricExcept(set) : Wrap(Value.SymmetricExcept(other)); /// /// Only items that are in one set or the other will be returned. /// If an item is in both, it is dropped. /// [Pure] public Set SymmetricExcept(Set other) => Wrap(Value.SymmetricExcept(other.Value)); /// /// Finds the union of two sets and produces a new set with /// the results /// /// Other set to union with /// A set which contains all items from both sets [Pure] public Set Union(IEnumerable other) => Wrap(Value.Union(other)); /// /// Clears the set /// /// An empty set [Pure] public Set Clear() => Empty; /// /// Get enumerator /// /// IEnumerator T [Pure] public IEnumerator GetEnumerator() => Value.GetEnumerator(); /// /// Get enumerator /// /// IEnumerator [Pure] IEnumerator IEnumerable.GetEnumerator() => // ReSharper disable once NotDisposedResourceIsReturned Value.GetEnumerator(); /// /// Removes an item from the set (if it exists) /// /// Value to remove /// New set with item removed [Pure] public Set Remove(A value) => Wrap(Value.Remove(value)); /// /// Removes a range of items from the set (if they exist) /// /// Value to remove /// New set with items removed [Pure] public Set RemoveRange(IEnumerable values) { var set = Value; foreach(var x in values) { set = set.Remove(x); } return Wrap(set); } /// /// Impure iteration of the bound value in the structure /// /// /// Returns the original unmodified structure /// public Set Do(Action f) { this.Iter(f); return this; } /// /// Maps the values of this set into a new set of values using the /// mapper function to tranform the source values. /// /// Mapped element type /// Mapping function /// Mapped Set [Pure] public Set Map(Func map) => Wrap(Value.Map, B>(map)); /// /// Map each element of a structure to an action, evaluate these actions from /// left to right, and collect the results. /// /// /// Traversable structure /// Applicative functor trait /// Bound value (output) [Pure] public K> Traverse(Func> f) where F : Applicative => F.Map(x => x.As(), Traversable.traverse(f, this)); /// /// Map each element of a structure to an action, evaluate these actions from /// left to right, and collect the results. /// /// /// Traversable structure /// Monad trait /// Bound value (output) [Pure] public K> TraverseM(Func> f) where M : Monad => M.Map(x => x.As(), Traversable.traverseM(f, this)); /// /// Filters items from the set using the predicate. If the predicate /// returns True for any item then it remains in the set, otherwise /// it's dropped. /// /// Predicate /// Filtered enumerable [Pure] public Set Filter(Func pred) => Wrap(Value.Filter(pred)); /// /// Check the existence of an item in the set using a /// predicate. /// /// Note this scans the entire set. /// Predicate /// True if predicate returns true for any item [Pure] public bool Exists(Func pred) => Value.Exists(pred); /// /// Returns True if the value is in the set /// /// Value to check /// True if the item 'value' is in the Set 'set' [Pure] public bool Contains(A value) => Value.Contains(value); /// /// Returns true if both sets contain the same elements /// /// Other distinct set to compare /// True if the sets are equal [Pure] public bool SetEquals(IEnumerable other) => Value.SetEquals(other); /// /// Is the set empty /// [Pure] public bool IsEmpty { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => value?.IsEmpty ?? true; } /// /// Number of items in the set /// [Pure] public int Count { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => value?.Count ?? 0; } /// /// Alias of Count /// [Pure] public int Length { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => value?.Count ?? 0; } /// /// Returns True if 'other' is a proper subset of this set /// /// True if 'other' is a proper subset of this set [Pure] public bool IsProperSubsetOf(IEnumerable other) => Value.IsProperSubsetOf(other); /// /// Returns True if 'other' is a proper superset of this set /// /// True if 'other' is a proper superset of this set [Pure] public bool IsProperSupersetOf(IEnumerable other) => Value.IsProperSupersetOf(other); /// /// Returns True if 'other' is a superset of this set /// /// True if 'other' is a superset of this set [Pure] public bool IsSubsetOf(IEnumerable other) => Value.IsSubsetOf(other); /// /// Returns True if 'other' is a superset of this set /// /// True if 'other' is a superset of this set [Pure] public bool IsSupersetOf(IEnumerable other) => Value.IsSupersetOf(other); /// /// Returns True if other overlaps this set /// /// Element type /// Set A /// Set B /// True if other overlaps this set [Pure] public bool Overlaps(IEnumerable other) => Value.Overlaps(other); /// /// Copy the items from the set into the specified array /// /// Array to copy to /// Index into the array to start public void CopyTo(A[] array, int index) => Value.CopyTo(array, index); /// /// Copy the items from the set into the specified array /// /// Array to copy to /// Index into the array to start public void CopyTo(Array array, int index) => Value.CopyTo(array, index); /// /// Add operator + performs a union of the two sets /// /// Left hand side set /// Right hand side set /// Unioned set [Pure] public static Set operator +(Set lhs, Set rhs) => Wrap(lhs.Value + rhs.Value); /// /// Choice operator /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Set operator |(Set x, K y) => x.Choose(y).As(); /// /// Choice operator /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Set operator |(K x, Set y) => x.Choose(y).As(); /// /// Append performs a union of the two sets /// /// Right hand side set /// Unioned set [Pure] public Set Combine(Set rhs) => Wrap(Value.Append(rhs.Value)); /// /// Subtract operator - performs a subtract of the two sets /// /// Left hand side set /// Right hand side set /// Subtractd set [Pure] public static Set operator -(Set lhs, Set rhs) => Wrap(lhs.Value - rhs.Value); /// /// Subtract operator - performs a subtract of the two sets /// /// Right hand side set /// Subtracted set [Pure] public Set Subtract(Set rhs) => Wrap(Value.Subtract(rhs.Value)); /// /// Equality test /// /// Other set to test /// True if sets are equal [Pure] public bool Equals(Set other) => Value.SetEquals(other.Value.AsIterable()); /// /// Equality operator /// /// Left hand side set /// Right hand side set /// True if the two sets are equal [Pure] public static bool operator ==(Set lhs, Set rhs) => lhs.Equals(rhs); /// /// Non-equality operator /// /// Left hand side set /// Right hand side set /// True if the two sets are equal [Pure] public static bool operator !=(Set lhs, Set rhs) => !lhs.Equals(rhs); [Pure] public static bool operator <(Set lhs, Set rhs) => lhs.CompareTo(rhs) < 0; [Pure] public static bool operator <=(Set lhs, Set rhs) => lhs.CompareTo(rhs) <= 0; [Pure] public static bool operator >(Set lhs, Set rhs) => lhs.CompareTo(rhs) > 0; [Pure] public static bool operator >=(Set lhs, Set rhs) => lhs.CompareTo(rhs) >= 0; /// /// Equality override /// [Pure] public override bool Equals(object? obj) => obj is Set set && Equals(set); /// /// Get the hash code. Calculated from all items in the set. /// /// /// The hash-code is cached after the first read. /// [Pure] public override int GetHashCode() => Value.GetHashCode(); [Pure] public int CompareTo(object? obj) => obj is Set t ? CompareTo(t) : 1; /// /// Format the collection as `[a, b, c, ...]` /// The ellipsis is used for collections over 50 items /// To get a formatted string with all the items, use `ToFullString` /// or `ToFullArrayString`. /// [Pure] public override string ToString() => CollectionFormat.ToShortArrayString(this, Count); /// /// Format the collection as `a, b, c, ...` /// [Pure] public string ToFullString(string separator = ", ") => CollectionFormat.ToFullString(this, separator); /// /// Format the collection as `[a, b, c, ...]` /// [Pure] public string ToFullArrayString(string separator = ", ") => CollectionFormat.ToFullArrayString(this, separator); [Pure] public Seq ToSeq() => toSeq(this); [Pure] public Iterable AsEnumerable() => Iterable.createRange(this); [Pure] public Set Select(Func f) => Map(f); [Pure] public Set Where(Func pred) => Filter(pred); [Pure] public Set Bind(Func> f) { var self = this; IEnumerable Yield() { foreach (var x in self.AsEnumerable()) { foreach (var y in f(x)) { yield return y; } } } return new Set(Yield(), true); } [Pure] public Set SelectMany(Func> bind, Func project) { var self = this; IEnumerable Yield() { foreach(var x in self.AsEnumerable()) { foreach(var y in bind(x)) { yield return project(x, y); } } } return new Set(Yield(), true); } [Pure] public Iterable Skip(int amount) => Value.Skip(amount); [Pure] public int CompareTo(Set other) => Value.CompareTo(other.Value); [Pure] public int CompareTo(Set other) where OrdA : Ord => Value.CompareTo(other.Value); /// /// Implicit conversion from an untyped empty list /// [Pure] public static implicit operator Set(SeqEmpty _) => Empty; /// /// Creates a new map from a range/slice of this map /// /// Range start (inclusive) /// Range to (inclusive) /// [Pure] public Set Slice(A keyFrom, A keyTo) => new (FindRange(keyFrom, keyTo)); /// /// Find the lowest ordered item in the set /// [Pure] public Option Min => Value.Min; /// /// Find the highest ordered item in the set /// [Pure] public Option Max => Value.Max; public static Set AdditiveIdentity => Empty; } ================================================ FILE: LanguageExt.Core/Immutable Collections/Set/Trait/Set.TraitImpl.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using LanguageExt.Traits; namespace LanguageExt; public partial class Set : Monad, MonoidK, Alternative, Traversable { static K Monad.Recur(A value, Func>> f) => createRange(Monad.enumerableRecur(value, x =>f(x).As().AsEnumerable())); static K Monad.Bind(K ma, Func> f) { return new Set(Go()); IEnumerable Go() { foreach (var x in ma.As()) { foreach (var y in f(x).As()) { yield return y; } } } } static K Functor.Map(Func f, K ma) { return new Set(Go()); IEnumerable Go() { foreach (var x in ma.As()) { yield return f(x); } } } static K Applicative.Pure(A value) => singleton(value); static K Applicative.Apply(K> mf, K ma) { return new Set(Go()); IEnumerable Go() { foreach (var f in mf.As()) { foreach (var a in ma.As()) { yield return f(a); } } } } static K Applicative.Apply(K> mf, Memo ma) { return new Set(Go()); IEnumerable Go() { foreach (var f in mf.As()) { foreach (var a in ma.Value.As()) { yield return f(a); } } } } static K MonoidK.Empty() => Set.Empty; static K Alternative.Empty() => Set.Empty; static K SemigroupK.Combine(K ma, K mb) => ma.As() + mb.As(); static K Choice.Choose(K ma, K mb) => ma.IsEmpty ? mb : ma; static K Choice.Choose(K ma, Memo mb) => ma.IsEmpty ? mb.Value : ma; static bool Foldable.Contains(A value, K ta) => ta.As().Contains(value); static int Foldable.Count(K ta) => ta.As().Count; static bool Foldable.IsEmpty(K ta) => ta.As().IsEmpty; static S Foldable.FoldWhile( Func> f, Func<(S State, A Value), bool> predicate, S state, K ta) { foreach (var x in ta.As()) { if (!predicate((state, x))) return state; state = f(x)(state); } return state; } static S Foldable.FoldBackWhile( Func> f, Func<(S State, A Value), bool> predicate, S state, K ta) { foreach (var x in ta.As().Reverse()) { if (!predicate((state, x))) return state; state = f(state)(x); } return state; } static K> Traversable.Traverse(Func> f, K ta) { return F.Map, K>( ks => ks, Foldable.fold(acc, F.Pure(empty()), ta)); K> acc(K> ys, A x) => Applicative.lift((bs, b) => bs.Add(b), ys, f(x)); } static K> Traversable.TraverseM(Func> f, K ta) { return F.Map, K>( ks => ks, Foldable.fold(acc, F.Pure(empty()), ta)); K> acc(K> fys, A x) => fys.Bind(ys => f(x).Map(ys.Add)); } static Option Foldable.Head(K ta) => ta.As().Min; static Option Foldable.Last(K ta) => ta.As().Max; static Option Foldable.Min(K ta) => ta.As().Min; static Option Foldable.Max(K ta) => ta.As().Max; static Fold Foldable.FoldStep(K ta, S initialState) { // ReSharper disable once GenericEnumeratorNotDisposed var iter = ta.As().GetEnumerator(); return go(initialState); Fold go(S state) => iter.MoveNext() ? Fold.Loop(state, iter.Current, go) : Fold.Done(state); } static Fold Foldable.FoldStepBack(K ta, S initialState) { // ReSharper disable once GenericEnumeratorNotDisposed var iter = ta.As().Reverse().GetEnumerator(); return go(initialState); Fold go(S state) => iter.MoveNext() ? Fold.Loop(state, iter.Current, go) : Fold.Done(state); } } ================================================ FILE: LanguageExt.Core/Immutable Collections/Stack/Stack.Extensions.cs ================================================ using System; using System.Collections.Generic; using static LanguageExt.Prelude; using System.Diagnostics.Contracts; using LanguageExt.Traits; namespace LanguageExt; public static class StackExtensions { /// /// Projects the values in the stack using a map function into a new enumerable (Select in LINQ). /// /// Stack item type /// Return enumerable item type /// Stack to map /// Map function /// Mapped enumerable [Pure] public static Stck Map(this Stck stack, Func map) => toStackRev(List.map(stack, map)); /// /// Projects the values in the stack into a new stack using a map function, which is also given an index value /// (Select in LINQ - note that the order of the arguments of the map function are the other way around, here the index /// is the first argument). /// /// Stack item type /// Return enumerable item type /// Stack to map /// Map function /// Mapped enumerable [Pure] public static Stck Map(this Stck stack, Func map) => toStackRev(List.map(stack, map)); /// /// Removes items from the stack that do not match the given predicate (Where in LINQ) /// /// Stack item type /// Stack to filter /// Predicate function /// Filtered stack [Pure] public static Stck Filter(this Stck stack, Func predicate) => toStackRev(List.filter(stack, predicate)); /// /// Applies the given function 'selector' to each element of the stack. Returns an enumerable comprised of /// the results for each element where the function returns Some(f(x)). /// /// Stack item type /// Stack /// Selector function /// Mapped and filtered enumerable [Pure] public static Stck Choose(this Stck stack, Func> selector) => toStackRev(List.choose(stack, selector)); /// /// Applies the given function 'selector' to each element of the stack. Returns an enumerable comprised of /// the results for each element where the function returns Some(f(x)). /// An index value is passed through to the selector function also. /// /// Stack item type /// Stack /// Selector function /// Mapped and filtered enumerable [Pure] public static Stck Choose(this Stck stack, Func> selector) => toStackRev(List.choose(stack, selector)); /// /// For each element of the stack, applies the given function. Concatenates all the results and /// returns the combined list. /// /// Stack item type /// Return enumerable item type /// Stack to map /// Map function /// Mapped enumerable [Pure] public static Stck Collect(this Stck stack, Func> map) => toStackRev(List.collect(stack, map)); /// /// Reverses the order of the items in the stack /// /// [Pure] public static Stck Rev(this Stck stack) => toStackRev(List.rev(stack)); /// /// Applies a function 'folder' to each element of the collection, threading an accumulator /// argument through the computation. The fold function takes the state argument, and /// applies the function 'folder' to it and the first element of the stack. Then, it feeds this /// result into the function 'folder' along with the second element, and so on. It returns the /// final result. (Aggregate in LINQ) /// /// State type /// Stack item type /// Stack to fold /// Initial state /// Fold function /// Aggregate value [Pure] public static S Fold(this Stck stack, S state, Func folder) => List.fold(stack, state, folder); /// /// Applies a function 'folder' to each element of the collection (from last element to first), /// threading an aggregate state through the computation. The fold function takes the state /// argument, and applies the function 'folder' to it and the first element of the stack. Then, /// it feeds this result into the function 'folder' along with the second element, and so on. It /// returns the final result. /// /// State type /// Stack item type /// Stack to fold /// Initial state /// Fold function /// Aggregate value [Pure] public static S FoldBack(this Stck stack, S state, Func folder) => List.foldBack(stack, state, folder); /// /// Applies a function 'folder' to each element of the collection whilst the predicate function /// returns true for the item being processed, threading an aggregate state through the /// computation. The fold function takes the state argument, and applies the function 'folder' /// to it and the first element of the stack. Then, it feeds this result into the function 'folder' /// along with the second element, and so on. It returns the final result. /// /// State type /// Stack item type /// Stack to fold /// Initial state /// Fold function /// Predicate function /// Aggregate value [Pure] public static S FoldWhile(this Stck stack, S state, Func folder, Func preditem) => List.foldWhile(stack, state, folder, preditem: preditem); /// /// Applies a function 'folder' to each element of the collection, threading an accumulator /// argument through the computation (and whilst the predicate function returns true when passed /// the aggregate state). The fold function takes the state argument, and applies the function /// 'folder' to it and the first element of the stack. Then, it feeds this result into the /// function 'folder' along with the second element, and so on. It returns the final result. /// /// State type /// Stack item type /// Stack to fold /// Initial state /// Fold function /// Predicate function /// Aggregate value [Pure] public static S FoldWhile(this Stck stack, S state, Func folder, Func predstate) => List.foldWhile(stack, state, folder, predstate: predstate); /// /// Applies a function 'folder' to each element of the collection (from last element to first) /// whilst the predicate function returns true for the item being processed, threading an /// aggregate state through the computation. The fold function takes the state argument, and /// applies the function 'folder' to it and the first element of the stack. Then, it feeds this /// result into the function 'folder' along with the second element, and so on. It returns the /// final result. /// /// State type /// Stack item type /// Stack to fold /// Initial state /// Fold function /// Predicate function /// Aggregate value [Pure] public static S FoldBackWhile(this Stck stack, S state, Func folder, Func preditem) => List.foldBackWhile(stack, state, folder, preditem: preditem); /// /// Applies a function 'folder' to each element of the collection (from last element to first), /// threading an accumulator argument through the computation (and whilst the predicate function /// returns true when passed the aggregate state). The fold function takes the state argument, /// and applies the function 'folder' to it and the first element of the stack. Then, it feeds /// this result into the function 'folder' along with the second element, and so on. It returns /// the final result. /// /// State type /// Stack item type /// Stack to fold /// Initial state /// Fold function /// Predicate function /// Aggregate value [Pure] public static S FoldBackWhile(this Stck stack, S state, Func folder, Func predstate) => List.foldBackWhile(stack, state, folder, predstate: predstate); /// /// Applies a function to each element of the collection, threading an accumulator argument /// through the computation. This function first applies the function to the first two /// elements of the stack. Then, it passes this result into the function along with the third /// element and so on. Finally, it returns the final result. /// /// Stack item type /// Stack /// Reduce function /// Aggregate value [Pure] public static T ReduceBack(Stck stack, Func reducer) => List.reduceBack(stack, reducer); /// /// Applies a function to each element of the collection (from last element to first), threading /// an accumulator argument through the computation. This function first applies the function /// to the first two elements of the stack. Then, it passes this result into the function along /// with the third element and so on. Finally, it returns the final result. /// /// Stack item type /// Stack to fold /// Reduce function /// Aggregate value [Pure] public static T Reduce(this Stck stack, Func reducer) => List.reduce(stack, reducer); /// /// Applies a function to each element of the collection, threading an accumulator argument /// through the computation. This function takes the state argument, and applies the function /// to it and the first element of the stack. Then, it passes this result into the function /// along with the second element, and so on. Finally, it returns the list of intermediate /// results and the final result. /// /// State type /// Stack item type /// Stack /// Initial state /// Folding function /// Aggregate state [Pure] public static Stck Scan(this Stck stack, S state, Func folder) => toStackRev(List.scan(stack, state, folder)); /// /// Applies a function to each element of the collection (from last element to first), /// threading an accumulator argument through the computation. This function takes the state /// argument, and applies the function to it and the first element of the stack. Then, it /// passes this result into the function along with the second element, and so on. Finally, /// it returns the list of intermediate results and the final result. /// /// State type /// Stack item type /// Stack /// Initial state /// Folding function /// Aggregate state [Pure] public static Stck ScanBack(this Stck stack, S state, Func folder) => toStackRev(List.scanBack(stack, state, folder)); /// /// Returns Some(x) for the first item in the stack that matches the predicate /// provided, None otherwise. /// /// Stack item type /// Stack /// Predicate /// Some(x) for the first item in the stack that matches the predicate /// provided, None otherwise. [Pure] public static Option Find(this Stck stack, Func pred) => List.find(stack, pred); /// /// Returns the number of items in the stack /// /// Stack item type /// Stack /// The number of items in the enumerable [Pure] public static int Length(this Stck stack) => List.length(stack); /// /// Invokes an action for each item in the stack in order /// /// Stack item type /// Stack to iterate /// Action to invoke with each item /// Unit public static Unit Iter(this Stck stack, Action action) => List.iter(stack, action); /// /// Invokes an action for each item in the stack in order and supplies /// a running index value. /// /// Stack item type /// Stack to iterate /// Action to invoke with each item /// Unit public static Unit Iter(this Stck stack, Action action) => List.iter(stack, action); /// /// Return an enumerable with all duplicate values removed /// /// Stack item type /// Stack /// An enumerable with all duplicate values removed [Pure] public static bool ForAll(this Stck stack, Func pred) => List.forall(stack, pred); /// /// Return an enumerable with all duplicate values removed /// /// Stack item type /// Stack /// An enumerable with all duplicate values removed [Pure] public static Stck Distinct(this Stck stack) => toStackRev(List.distinct(stack)); /// /// Return an enumerable with all duplicate values removed /// /// Stack item type /// Stack /// An enumerable with all duplicate values removed [Pure] public static Stck Distinct(this Stck stack) where EQ : Eq => toStackRev(List.distinct(stack)); /// /// Return an enumerable with all duplicate values removed /// /// Stack item type /// Stack /// An enumerable with all duplicate values removed [Pure] public static Stck Distinct(this Stck stack, Func keySelector, Option> compare = default(Option>)) => toStackRev(List.distinct(stack, keySelector, compare)); /// /// Returns a new enumerable with the first 'count' items from the stack /// /// Stack item type /// Stack /// Number of items to take /// A new enumerable with the first 'count' items from the enumerable provided [Pure] public static Stck Take(this Stck stack, int count) => toStackRev(List.take(stack, count)); /// /// Iterate the stack, yielding items if they match the predicate provided, and stopping /// as soon as one doesn't /// /// Stack item type /// Stack /// Number of items to take /// A new enumerable with the first items that match the predicate [Pure] public static Stck TakeWhile(this Stck stack, Func pred) => toStackRev(List.takeWhile(stack, pred)); /// /// Iterate the stack, yielding items if they match the predicate provided, and stopping /// as soon as one doesn't An index value is also provided to the predicate function. /// /// Stack item type /// Stack /// Number of items to take /// A new enumerable with the first items that match the predicate [Pure] public static Stck TakeWhile(this Stck stack, Func pred) => toStackRev(List.takeWhile(stack, pred)); /// /// Returns true if any item in the stack matches the predicate provided /// /// Stack item type /// Stack /// Predicate /// True if any item in the stack matches the predicate provided [Pure] public static bool Exists(this Stck stack, Func pred) => List.exists(stack, pred); } ================================================ FILE: LanguageExt.Core/Immutable Collections/Stack/Stack.Module.cs ================================================ using System; using System.Collections.Generic; using static LanguageExt.Prelude; using System.Diagnostics.Contracts; using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt; /// /// Functional module for working with the Stck T type /// public static class Stack { /// /// Create a new stack from a single element /// /// Item to populate the singleton stack /// Type of the items /// Constructed stack collection public static Stck singleton(A item) => [item]; /// /// Create a new stack from an existing span /// /// Items to populate the stack /// Type of the items /// Constructed stack collection public static Stck createRange(IEnumerable items) => new (items); /// /// Create a new stack from an existing span /// /// Items to populate the stack /// Type of the items /// Constructed stack collection public static Stck createRange(ReadOnlySpan items) => items.IsEmpty ? Stck.Empty : new (items); /// /// Reverses the order of the items in the stack /// /// [Pure] public static Stck rev(Stck stack) => stack.Reverse(); /// /// True if the stack is empty /// [Pure] public static bool isEmpty(Stck stack) => stack.IsEmpty; /// /// Clear the stack (returns Empty) /// /// Stck.Empty of T [Pure] public static Stck clear(Stck stack) => stack.Clear(); /// /// Return the item on the top of the stack without affecting the stack itself /// NOTE: Will throw an ExpectedException if the stack is empty /// /// Stack is empty /// Top item value [Pure] public static T peek(Stck stack) => stack.Peek(); /// /// Peek and match /// /// Handler if there is a value on the top of the stack /// Handler if the stack is empty /// Untouched stack [Pure] public static Stck peek(Stck stack, Action Some, Action None) => stack.Peek(Some, None); /// /// Peek and match /// /// Return type /// Handler if there is a value on the top of the stack /// Handler if the stack is empty /// Return value from Some or None [Pure] public static R peek(Stck stack, Func Some, Func None) => stack.Peek(Some, None); /// /// Safely return the item on the top of the stack without affecting the stack itself /// /// Returns the top item value, or None [Pure] public static Option trypeek(Stck stack) => stack.TryPeek(); /// /// Pop an item off the top of the stack /// NOTE: Will throw an ExpectedException if the stack is empty /// /// Stack is empty /// Stack with the top item popped [Pure] public static Stck pop(Stck stack) => stack.Pop(); /// /// Safe pop /// /// Tuple of popped stack and optional top-of-stack value [Pure] public static (Stck, Option) trypop(Stck stack) => stack.TryPop(); /// /// Pop and match /// /// Handler if there is a value on the top of the stack /// Handler if the stack is empty /// Popped stack [Pure] public static Stck pop(Stck stack, Action Some, Action None) => stack.Pop(Some, None); /// /// Pop and match /// /// Return type /// Handler if there is a value on the top of the stack /// Handler if the stack is empty /// Return value from Some or None [Pure] public static R pop(Stck stack, Func, T, R> Some, Func None) => stack.Pop(Some, None); /// /// Push an item onto the stack /// /// Item to push /// New stack with the pushed item on top [Pure] public static Stck push(Stck stack, T value) => stack.Push(value); /// /// Projects the values in the stack using a map function into a new enumerable (Select in LINQ). /// /// Stack item type /// Return enumerable item type /// Stack to map /// Map function /// Mapped enumerable [Pure] public static Stck map(Stck stack, Func map) => toStackRev(List.map(stack, map)); /// /// Projects the values in the stack into a new enumerable using a map function, which is also given an index value /// (Select in LINQ - note that the order of the arguments of the map function are the other way around, here the index /// is the first argument). /// /// Stack item type /// Return enumerable item type /// Stack to map /// Map function /// Mapped enumerable [Pure] public static Stck map(Stck stack, Func map) => toStackRev(List.map(stack, map)); /// /// Removes items from the stack that do not match the given predicate (Where in LINQ) /// /// Stack item type /// Stack to filter /// Predicate function /// Filtered stack [Pure] public static Stck filter(Stck stack, Func predicate) => toStackRev(List.filter(stack, predicate)); /// /// Applies the given function 'selector' to each element of the stack. Returns an enumerable comprised of /// the results for each element where the function returns Some(f(x)). /// /// Stack item type /// Stack /// Selector function /// Mapped and filtered enumerable [Pure] public static Stck choose(Stck stack, Func> selector) => toStackRev(List.choose(stack, selector)); /// /// Applies the given function 'selector' to each element of the stack. Returns an enumerable comprised of /// the results for each element where the function returns Some(f(x)). /// An index value is passed through to the selector function also. /// /// Stack item type /// Stack /// Selector function /// Mapped and filtered enumerable [Pure] public static Stck choose(Stck stack, Func> selector) => toStackRev(List.choose(stack, selector)); /// /// For each element of the stack, applies the given function. Concatenates all the results and /// returns the combined list. /// /// Stack item type /// Return enumerable item type /// Stack to map /// Map function /// Mapped enumerable [Pure] public static Stck collect(Stck stack, Func> map) => toStackRev(List.collect(stack, map)); /// /// Append another stack to the top of this stack /// The rhs will be reversed and pushed onto 'this' stack. That will /// maintain the order of the items in the resulting stack. So the top /// of 'rhs' will be the top of the newly created stack. 'this' stack /// will be under the 'rhs' stack. /// /// Stack to append /// Appended stacks [Pure] public static Stck append(Stck lhs, IEnumerable rhs) => toStackRev(List.append(lhs, rhs)); /// /// Applies a function 'folder' to each element of the collection, threading an accumulator /// argument through the computation. The fold function takes the state argument, and /// applies the function 'folder' to it and the first element of the stack. Then, it feeds this /// result into the function 'folder' along with the second element, and so on. It returns the /// final result. (Aggregate in LINQ) /// /// State type /// Stack item type /// Stack to fold /// Initial state /// Fold function /// Aggregate value [Pure] public static S fold(Stck stack, S state, Func folder) => List.fold(stack, state, folder); /// /// Applies a function 'folder' to each element of the collection (from last element to first), /// threading an aggregate state through the computation. The fold function takes the state /// argument, and applies the function 'folder' to it and the first element of the stack. Then, /// it feeds this result into the function 'folder' along with the second element, and so on. It /// returns the final result. /// /// State type /// Stack item type /// Stack to fold /// Initial state /// Fold function /// Aggregate value [Pure] public static S foldBack(Stck stack, S state, Func folder) => List.foldBack(stack, state, folder); /// /// Applies a function 'folder' to each element of the collection whilst the predicate function /// returns true for the item being processed, threading an aggregate state through the /// computation. The fold function takes the state argument, and applies the function 'folder' /// to it and the first element of the stack. Then, it feeds this result into the function 'folder' /// along with the second element, and so on. It returns the final result. /// /// State type /// Stack item type /// Stack to fold /// Initial state /// Fold function /// Predicate function /// Aggregate value [Pure] public static S foldWhile(Stck stack, S state, Func folder, Func preditem) => List.foldWhile(stack, state, folder, preditem: preditem); /// /// Applies a function 'folder' to each element of the collection, threading an accumulator /// argument through the computation (and whilst the predicate function returns true when passed /// the aggregate state). The fold function takes the state argument, and applies the function /// 'folder' to it and the first element of the stack. Then, it feeds this result into the /// function 'folder' along with the second element, and so on. It returns the final result. /// /// State type /// Stack item type /// Stack to fold /// Initial state /// Fold function /// Predicate function /// Aggregate value [Pure] public static S foldWhile(Stck stack, S state, Func folder, Func predstate) => List.foldWhile(stack, state, folder, predstate: predstate); /// /// Applies a function 'folder' to each element of the collection (from last element to first) /// whilst the predicate function returns true for the item being processed, threading an /// aggregate state through the computation. The fold function takes the state argument, and /// applies the function 'folder' to it and the first element of the stack. Then, it feeds this /// result into the function 'folder' along with the second element, and so on. It returns the /// final result. /// /// State type /// Stack item type /// Stack to fold /// Initial state /// Fold function /// Predicate function /// Aggregate value [Pure] public static S foldBackWhile(Stck stack, S state, Func folder, Func preditem) => List.foldBackWhile(stack, state, folder, preditem: preditem); /// /// Applies a function 'folder' to each element of the collection (from last element to first), /// threading an accumulator argument through the computation (and whilst the predicate function /// returns true when passed the aggregate state). The fold function takes the state argument, /// and applies the function 'folder' to it and the first element of the stack. Then, it feeds /// this result into the function 'folder' along with the second element, and so on. It returns /// the final result. /// /// State type /// Stack item type /// Stack to fold /// Initial state /// Fold function /// Predicate function /// Aggregate value [Pure] public static S foldBackWhile(Stck stack, S state, Func folder, Func predstate) => List.foldBackWhile(stack, state, folder, predstate: predstate); /// /// Applies a function to each element of the collection (from last element to first), threading /// an accumulator argument through the computation. This function first applies the function /// to the first two elements of the stack. Then, it passes this result into the function along /// with the third element and so on. Finally, it returns the final result. /// /// Stack item type /// Stack to fold /// Reduce function /// Aggregate value [Pure] public static T reduce(Stck stack, Func reducer) => List.reduce(stack, reducer); /// /// Applies a function to each element of the collection, threading an accumulator argument /// through the computation. This function first applies the function to the first two /// elements of the stack. Then, it passes this result into the function along with the third /// element and so on. Finally, it returns the final result. /// /// Stack item type /// Stack /// Reduce function /// Aggregate value [Pure] public static T reduceBack(Stck stack, Func reducer) => List.reduceBack(stack, reducer); /// /// Applies a function to each element of the collection, threading an accumulator argument /// through the computation. This function takes the state argument, and applies the function /// to it and the first element of the stack. Then, it passes this result into the function /// along with the second element, and so on. Finally, it returns the list of intermediate /// results and the final result. /// /// State type /// Stack item type /// Stack /// Initial state /// Folding function /// Aggregate state [Pure] public static Stck scan(Stck stack, S state, Func folder) => toStackRev(List.scan(stack, state, folder)); /// /// Applies a function to each element of the collection (from last element to first), /// threading an accumulator argument through the computation. This function takes the state /// argument, and applies the function to it and the first element of the stack. Then, it /// passes this result into the function along with the second element, and so on. Finally, /// it returns the list of intermediate results and the final result. /// /// State type /// Stack item type /// Stack /// Initial state /// Folding function /// Aggregate state [Pure] public static Stck scanBack(Stck stack, S state, Func folder) => toStackRev(List.scanBack(stack, state, folder)); /// /// Returns Some(x) for the first item in the stack that matches the predicate /// provided, None otherwise. /// /// Stack item type /// Stack /// Predicate /// Some(x) for the first item in the stack that matches the predicate /// provided, None otherwise. [Pure] public static Option find(Stck stack, Func pred) => List.find(stack, pred); /// /// Joins a stack and and enumerable together either into a single enumerable /// using the join function provided /// /// First stack to join /// Second list to join /// Join function /// Joined enumerable [Pure] public static Stck zip(Stck stack, IEnumerable other, Func zipper) => toStackRev(List.zip(stack, other, zipper)); /// /// Returns the number of items in the stack /// /// Stack item type /// Stack /// The number of items in the enumerable [Pure] public static int length(Stck stack) => List.length(stack); /// /// Invokes an action for each item in the stack in order /// /// Stack item type /// Stack to iterate /// Action to invoke with each item /// Unit public static Unit iter(Stck stack, Action action) => List.iter(stack, action); /// /// Invokes an action for each item in the stack in order and supplies /// a running index value. /// /// Stack item type /// Stack to iterate /// Action to invoke with each item /// Unit public static Unit iter(Stck stack, Action action) => List.iter(stack, action); /// /// Returns true if all items in the stack match a predicate (Any in LINQ) /// /// Stack item type /// Stack to test /// Predicate /// True if all items in the stack match the predicate [Pure] public static bool forall(Stck stack, Func pred) => List.forall(stack, pred); /// /// Return an enumerable with all duplicate values removed /// /// Stack item type /// Stack /// An enumerable with all duplicate values removed [Pure] public static Stck distinct(Stck stack) => toStackRev(List.distinct(stack)); /// /// Return an enumerable with all duplicate values removed /// /// Stack item type /// Stack /// An enumerable with all duplicate values removed [Pure] public static Stck distinct(Stck stack) where EQ : Eq => toStackRev(List.distinct(stack)); /// /// Return an enumerable with all duplicate values removed /// /// Stack item type /// Stack /// An enumerable with all duplicate values removed [Pure] public static Stck distinct(Stck stack, Func keySelector, Option> compare = default(Option>)) => toStackRev(List.distinct(stack, keySelector, compare)); /// /// Returns a new enumerable with the first 'count' items from the stack /// /// Stack item type /// Stack /// Number of items to take /// A new enumerable with the first 'count' items from the enumerable provided [Pure] public static Stck take(Stck stack, int count) => toStackRev(List.take(stack, count)); /// /// Iterate the stack, yielding items if they match the predicate provided, and stopping /// as soon as one doesn't /// /// Stack item type /// Stack /// Number of items to take /// A new enumerable with the first items that match the predicate [Pure] public static Stck takeWhile(Stck stack, Func pred) => toStackRev(List.takeWhile(stack, pred)); /// /// Iterate the stack, yielding items if they match the predicate provided, and stopping /// as soon as one doesn't An index value is also provided to the predicate function. /// /// Stack item type /// Stack /// Number of items to take /// A new enumerable with the first items that match the predicate [Pure] public static Stck takeWhile(Stck stack, Func pred) => toStackRev(List.takeWhile(stack, pred)); /// /// Returns true if any item in the stack matches the predicate provided /// /// Stack item type /// Stack /// Predicate /// True if any item in the stack matches the predicate provided [Pure] public static bool exists(Stck stack, Func pred) => List.exists(stack, pred); } ================================================ FILE: LanguageExt.Core/Immutable Collections/Stack/Stck.Internal.cs ================================================ using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics.Contracts; using LanguageExt.ClassInstances; using LanguageExt.Common; namespace LanguageExt { /// /// Immutable stack /// /// Stack element type [Serializable] internal class StckInternal : IEnumerable { public static readonly StckInternal Empty = new (); readonly A? value; readonly StckInternal? tail; int hashCode; /// /// Default ctor /// internal StckInternal() { } /// /// Ctor for Push /// /// /// internal StckInternal(A value, StckInternal tail) { Count = tail.Count + 1; this.tail = tail; this.value = value; } /// /// Ctor that takes an initial state as an IEnumerable T /// public StckInternal(A[] initial) { tail = new StckInternal(); foreach (var item in initial) { value = item; tail = tail.Push(item); Count++; } tail = tail.Pop(); } /// /// Ctor that takes an initial state as an IEnumerable T /// public StckInternal(ReadOnlySpan initial) { tail = new StckInternal(); foreach (var item in initial) { value = item; tail = tail.Push(item); Count++; } tail = tail.Pop(); } /// /// Ctor that takes an initial state as a Lst T /// internal StckInternal(Lst initial) { tail = new StckInternal(); foreach (var item in initial) { value = item; tail = tail.Push(item); Count++; } tail = tail.Pop(); } /// /// Number of items in the stack /// [Pure] public int Count { get; } /// /// Reverses the order of the items in the stack /// /// [Pure] public StckInternal Reverse() { var s = new StckInternal(); foreach (var item in this) { s = s.Push(item); } return s; } /// /// True if the stack is empty /// [Pure] public bool IsEmpty => Count == 0; /// /// Clear the stack (returns Empty) /// /// Stck.Empty of T [Pure] public StckInternal Clear() => Empty; /// /// Get enumerator /// /// IEnumerator of T [Pure] public IEnumerator GetEnumerator() => // ReSharper disable once NotDisposedResourceIsReturned AsIterable().GetEnumerator(); /// /// Returns the stack as an IEnumerable. The first item in the enumerable /// will be the item at the top of the stack. /// /// IEnumerable of T [Pure] public Iterable AsIterable() { IEnumerable Yield() { var self = this; while (self!.Count != 0) { yield return self.value!; self = self.tail; } } return Iterable.createRange(Yield()); } /// /// Return the item on the top of the stack without affecting the stack itself /// NOTE: Will throw an ExpectedException if the stack is empty /// /// Stack is empty /// Top item value [Pure] public A Peek() { if (Count > 0) { return value!; } else { throw Exceptions.SequenceEmpty; } } /// /// Peek and match /// /// Handler if there is a value on the top of the stack /// Handler if the stack is empty /// Untouched stack (this) [Pure] public StckInternal Peek(Action Some, Action None) { if (Count > 0) { Some(value!); } else { None(); } return this; } /// /// Peek and match /// /// Return type /// Handler if there is a value on the top of the stack /// Handler if the stack is empty /// Return value from Some or None [Pure] public R Peek(Func Some, Func None) => Count > 0 ? Some(value!) : None(); /// /// Safely return the item on the top of the stack without affecting the stack itself /// /// Returns the top item value, or None [Pure] public Option TryPeek() => Count > 0 ? Prelude.Some(value!) : Prelude.None; /// /// Pop an item off the top of the stack /// NOTE: Will throw an ExpectedException if the stack is empty /// /// Stack is empty /// Stack with the top item popped [Pure] public StckInternal Pop() { if (Count > 0) { return tail!; } else { throw Exceptions.SequenceEmpty; } } /// /// Safe pop /// /// Tuple of popped stack and optional top-of-stack value [Pure] public (StckInternal, Option) TryPop() => Count > 0 ? (tail!, Option.Some(value!)) : (this, Option.None); /// /// Pop and match /// /// Handler if there is a value on the top of the stack /// Handler if the stack is empty /// Popped stack [Pure] public StckInternal Pop(Action Some, Action None) { if (Count > 0) { Some(value!); return tail!; } else { None(); return this; } } /// /// Pop and match /// /// Return type /// Handler if there is a value on the top of the stack /// Handler if the stack is empty /// Return value from Some or None [Pure] public R Pop(Func, A, R> Some, Func None) => Count > 0 ? Some(tail!, value!) : None(); /// /// Push an item onto the stack /// /// Item to push /// New stack with the pushed item on top [Pure] public StckInternal Push(A value) => new (value, this); /// /// Get enumerator /// /// IEnumerator of T [Pure] IEnumerator IEnumerable.GetEnumerator() => AsIterable().GetEnumerator(); /// /// Append another stack to the top of this stack /// The rhs will be reversed and pushed onto 'this' stack. That will /// maintain the order of the items in the resulting stack. So the top /// of 'rhs' will be the top of the newly created stack. 'this' stack /// will be under the 'rhs' stack. /// [Pure] public static StckInternal operator +(StckInternal lhs, StckInternal rhs) => lhs.Combine(rhs); /// /// Append another stack to the top of this stack /// The rhs will be reversed and pushed onto 'this' stack. That will /// maintain the order of the items in the resulting stack. So the top /// of 'rhs' will be the top of the newly created stack. 'this' stack /// will be under the 'rhs' stack. /// /// Stack to append /// Appended stacks [Pure] public StckInternal Combine(StckInternal rhs) { var self = this; foreach (var item in rhs.Reverse()) { self = self.Push(item); } return self; } public override int GetHashCode() => hashCode == 0 ? hashCode = FNV32.Hash, A>(this) : hashCode; } } ================================================ FILE: LanguageExt.Core/Immutable Collections/Stack/Stck.cs ================================================ using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics.Contracts; using System.Linq; using System.Runtime.CompilerServices; using LanguageExt.ClassInstances; using LanguageExt.Common; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; /// /// Immutable stack /// /// Stack element type [Serializable] [CollectionBuilder(typeof(Stack), nameof(Stack.createRange))] public readonly struct Stck : IEnumerable, IEquatable>, Monoid> { public static Stck Empty { get; } = new(StckInternal.Empty); readonly StckInternal value; StckInternal Value => value ?? StckInternal.Empty; /// /// Default ctor /// internal Stck(StckInternal value) => this.value = value; /// /// Ctor that takes an initial state as an IEnumerable T /// public Stck(IEnumerable initial) { var xs = initial.ToArray(); value = xs.Length == 0 ? StckInternal.Empty : new StckInternal(xs); } /// /// Ctor that takes an initial state as an IEnumerable T /// public Stck(ReadOnlySpan initial) => value = initial.IsEmpty ? StckInternal.Empty : new StckInternal(initial); /// /// Reference version for use in pattern-matching /// /// /// /// Empty collection = null /// Singleton collection = A /// More = (A, Seq〈A〉) -- head and tail /// /// var res = stack.Case switch /// { /// /// (var x, var xs) => ..., /// A value => ..., /// _ => ... /// } /// /// [Pure] public object? Case => IsEmpty ? null : toSeq(Value).Case; /// /// Reverses the order of the items in the stack /// /// [Pure] public Stck Reverse() => new (Value.Reverse()); /// /// Is the stack empty /// [Pure] public bool IsEmpty { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => value?.IsEmpty ?? true; } /// /// Number of items in the stack /// [Pure] public int Count { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => value?.Count ?? 0; } /// /// Alias of Count /// [Pure] public int Length { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => value?.Count ?? 0; } /// /// Clear the stack (returns Empty) /// /// Stck.Empty of T [Pure] public Stck Clear() => Empty; /// /// Get enumerator /// /// IEnumerator of T [Pure] public IEnumerator GetEnumerator() => // ReSharper disable once NotDisposedResourceIsReturned AsIterable().GetEnumerator(); /// /// Returns the stack as a Seq. The first item in the sequence /// will be the item at the top of the stack. /// /// IEnumerable of T [Pure] public Seq ToSeq() => toSeq(Value); /// /// Format the collection as `[a, b, c, ...]` /// The elipsis is used for collections over 50 items /// To get a formatted string with all the items, use `ToFullString` /// or `ToFullArrayString`. /// [Pure] public override string ToString() => CollectionFormat.ToShortArrayString(this, Count); /// /// Format the collection as `a, b, c, ...` /// [Pure] public string ToFullString(string separator = ", ") => CollectionFormat.ToFullString(this, separator); /// /// Format the collection as `[a, b, c, ...]` /// [Pure] public string ToFullArrayString(string separator = ", ") => CollectionFormat.ToFullArrayString(this, separator); /// /// Returns the stack as an IEnumerable. The first item in the enumerable /// will be the item at the top of the stack. /// /// IEnumerable of T [Pure] public Iterable AsIterable() => Iterable.createRange(Value); /// /// Impure iteration of the bound value in the structure /// /// /// Returns the original unmodified structure /// public Stck Do(Action f) { this.Iter(f); return this; } /// /// Return the item on the top of the stack without affecting the stack itself /// NOTE: Will throw an ExpectedException if the stack is empty /// /// Stack is empty /// Top item value [Pure] public A Peek() => Value.Peek(); /// /// Peek and match /// /// Handler if there is a value on the top of the stack /// Handler if the stack is empty /// Untouched stack (this) [Pure] public Stck Peek(Action Some, Action None) => new (Value.Peek(Some, None)); /// /// Peek and match /// /// Return type /// Handler if there is a value on the top of the stack /// Handler if the stack is empty /// Return value from Some or None [Pure] public R Peek(Func Some, Func None) => Value.Peek(Some, None); /// /// Safely return the item on the top of the stack without affecting the stack itself /// /// Returns the top item value, or None [Pure] public Option TryPeek() => Value.TryPeek(); /// /// Pop an item off the top of the stack /// NOTE: Will throw an ExpectedException if the stack is empty /// /// Stack is empty /// Stack with the top item popped [Pure] public Stck Pop() => new (Value.Pop()); /// /// Safe pop /// /// Tuple of popped stack and optional top-of-stack value [Pure] public (Stck Stack, Option Value) TryPop() => Value.TryPop().MapFirst(x => new Stck(x)); /// /// Pop and match /// /// Handler if there is a value on the top of the stack /// Handler if the stack is empty /// Popped stack [Pure] public Stck Pop(Action Some, Action None) => new (Value.Pop(Some, None)); /// /// Pop and match /// /// Return type /// Handler if there is a value on the top of the stack /// Handler if the stack is empty /// Return value from Some or None [Pure] public R Pop(Func, A, R> Some, Func None) => Value.Pop((s, t) => Some(new Stck(s), t), None); /// /// Push an item onto the stack /// /// Item to push /// New stack with the pushed item on top [Pure] public Stck Push(A value) => new (Value.Push(value)); /// /// Get enumerator /// /// IEnumerator of T [Pure] IEnumerator IEnumerable.GetEnumerator() => // ReSharper disable once NotDisposedResourceIsReturned AsIterable().GetEnumerator(); /// /// Implicit conversion from an untyped empty list /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator Stck(SeqEmpty _) => Empty; /// /// Append another stack to the top of this stack /// The rhs will be reversed and pushed onto 'this' stack. That will /// maintain the order of the items in the resulting stack. So the top /// of 'rhs' will be the top of the newly created stack. 'this' stack /// will be under the 'rhs' stack. /// [Pure] public static Stck operator +(Stck lhs, Stck rhs) => lhs.Combine(rhs); /// /// Append another stack to the top of this stack /// The rhs will be reversed and pushed onto 'this' stack. That will /// maintain the order of the items in the resulting stack. So the top /// of 'rhs' will be the top of the newly created stack. 'this' stack /// will be under the 'rhs' stack. /// /// Stack to append /// Appended stacks [Pure] public Stck Combine(Stck rhs) => new (Value.Combine(rhs.Value)); /// /// Subtract one stack from another: lhs except rhs /// [Pure] public static Stck operator -(Stck lhs, Stck rhs) => lhs.Subtract(rhs); /// /// Append another stack to the top of this stack /// The rhs will be reversed and pushed onto 'this' stack. That will /// maintain the order of the items in the resulting stack. So the top /// of 'rhs' will be the top of the newly created stack. 'this' stack /// will be under the 'rhs' stack. /// /// Stack to append /// Appended stacks [Pure] public Stck Subtract(Stck rhs) => new (Enumerable.Except(this, rhs)); [Pure] public static bool operator ==(Stck lhs, Stck rhs) => lhs.Equals(rhs); [Pure] public static bool operator !=(Stck lhs, Stck rhs) => !(lhs == rhs); [Pure] public override int GetHashCode() => Value.GetHashCode(); [Pure] public override bool Equals(object? obj) => obj is Stck @as && Equals(@as); [Pure] public bool Equals(Stck other) => GetHashCode() == other.GetHashCode() && EqEnumerable.Equals(Value, other.Value); } ================================================ FILE: LanguageExt.Core/Immutable Collections/TrackingHashMap/TrackingHashMap.Eq.cs ================================================ using LanguageExt.ClassInstances; using static LanguageExt.Prelude; using System; using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics.Contracts; using LanguageExt.Traits; using System.Linq; using System.Runtime.CompilerServices; namespace LanguageExt; /// /// Unsorted immutable hash-map that tracks changes. /// /// /// Changes are accessible via the `Changes` property. It is a `HashMap` of `Change` values from either the initial /// empty state of the collection, or since the last call to `Snapshot()`. /// /// The fact that the changes are represented as a single-value `HashMap` shows that the tracked changes are not an /// ever increasing log of changes, but instead a morphism between one previous state of the `TrackingHashMap` and /// another. Therefore there's at most one morphism for each key, and potentially none. /// /// The morphisms are: /// /// * `EntryAdded` /// * `EntryMapped` /// * `EntryRemoved` /// /// A new 'zero-changes starting-state' can be created by calling `Snapshot()`. `Snapshot` creates the first /// snapshot (effectively clears the `Changes` to zero), and `Changes` will collect the difference from this point /// to any morphed future-state as collection-transforming operations are performed /// /// Key type /// Value [CollectionBuilder(typeof(TrackingHashMap), nameof(TrackingHashMap.createRange))] public readonly struct TrackingHashMap : IReadOnlyDictionary, IEnumerable<(K Key, V Value)>, IEquatable>, Monoid> where EqK : Eq { public static TrackingHashMap Empty { get; } = new(TrieMap.Empty); readonly TrieMap value; readonly TrieMap> changes; internal TrieMap Value => value ?? TrieMap.Empty; internal TrieMap> ChangesInternal => changes ?? TrieMap>.Empty; public HashMap> Changes => new (ChangesInternal); internal TrackingHashMap(TrieMap value, TrieMap> changes) { this.value = value; this.changes = changes; } public TrackingHashMap(IEnumerable<(K Key, V Value)> items) : this(items, true) { } public TrackingHashMap(IEnumerable<(K Key, V Value)> items, bool tryAdd) { value = new TrieMap(items, tryAdd); changes = TrieMap>.Empty; } public TrackingHashMap(ReadOnlySpan<(K Key, V Value)> items) : this(items, true) { } public TrackingHashMap(ReadOnlySpan<(K Key, V Value)> items, bool tryAdd) { value = new TrieMap(items, tryAdd); changes = TrieMap>.Empty; } /// /// Creates a 'zero change' snapshot. *The data does not change*! /// /// Useful for creating new starting points for capturing the difference between two snapshots of the /// `TrackingHashMap`. `Snapshot` creates the first snapshot (effectively clears the `Changes` to zero), and /// `Changes` will collect the difference from this point to any morphed future point as collection- /// transforming operations are performed /// Map with changes zeroed [Pure] public TrackingHashMap Snapshot() => new (Value, TrieMap>.Empty); /// /// Item at index lens /// [Pure] public static Lens, V> item(K key) => Lens, V>.New( Get: la => la[key], Set: a => la => la.AddOrUpdate(key, a) ); /// /// Item or none at index lens /// [Pure] public static Lens, Option> itemOrNone(K key) => Lens, Option>.New( Get: la => la.Find(key), Set: a => la => a.Match(Some: x => la.AddOrUpdate(key, x), None: () => la.Remove(key)) ); TrackingHashMap Wrap((TrieMap Map, TrieMap> Changes) pair) => new (pair.Map, ChangesInternal.Merge(pair.Changes)); TrackingHashMap Wrap(K key, (TrieMap Map, Change Change) pair) => new(pair.Map, ChangesInternal.AddOrUpdate(key, Some: ex => ex.Combine(pair.Change), pair.Change)); /// /// 'this' accessor /// /// Key /// Optional value [Pure] public V this[K key] => Value[key]; /// /// Is the map empty /// [Pure] public bool IsEmpty { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => value?.IsEmpty ?? true; } /// /// Number of items in the map /// [Pure] public int Count { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => value?.Count ?? 0; } /// /// Alias of Count /// [Pure] public int Length { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => value?.Count ?? 0; } /// /// Atomically filter out items that return false when a predicate is applied /// /// Predicate /// New map with items filtered [Pure] public TrackingHashMap Filter(Func pred) => Wrap(Value.FilterWithLog(pred)); /// /// Atomically filter out items that return false when a predicate is applied /// /// Predicate /// New map with items filtered [Pure] public TrackingHashMap Filter(Func pred) => Wrap(Value.FilterWithLog(pred)); /// /// Atomically adds a new item to the map /// /// Null is not allowed for a Key or a Value /// Key /// Value /// Throws ArgumentException if the key already exists /// Throws ArgumentNullException the key or value are null /// New Map with the item added [Pure] public TrackingHashMap Add(K key, V value) => Wrap(key, Value.AddWithLog(key, value)); /// /// Atomically adds a new item to the map. /// If the key already exists, then the new item is ignored /// /// Null is not allowed for a Key or a Value /// Key /// Value /// Throws ArgumentNullException the key or value are null /// New Map with the item added [Pure] public TrackingHashMap TryAdd(K key, V value) => Wrap(key, Value.TryAddWithLog(key, value)); /// /// Atomically adds a new item to the map. /// If the key already exists, the new item replaces it. /// /// Null is not allowed for a Key or a Value /// Key /// Value /// Throws ArgumentNullException the key or value are null /// New Map with the item added [Pure] public TrackingHashMap AddOrUpdate(K key, V value) => Wrap(key, Value.AddOrUpdateWithLog(key, value)); /// /// Retrieve a value from the map by key, map it to a new value, /// put it back. If it doesn't exist, add a new one based on None result. /// /// Key to find /// Throws Exception if None returns null /// Throws Exception if Some returns null /// New map with the mapped value [Pure] public TrackingHashMap AddOrUpdate(K key, Func Some, Func None) => Wrap(key, Value.AddOrUpdateWithLog(key, Some, None)); /// /// Retrieve a value from the map by key, map it to a new value, /// put it back. If it doesn't exist, add a new one based on None result. /// /// Key to find /// Throws ArgumentNullException if None is null /// Throws Exception if Some returns null /// New map with the mapped value [Pure] public TrackingHashMap AddOrUpdate(K key, Func Some, V None) => Wrap(key, Value.AddOrUpdateWithLog(key, Some, None)); /// /// Atomically adds a range of items to the map. /// /// Null is not allowed for a Key or a Value /// Range of tuples to add /// Throws ArgumentException if any of the keys already exist /// Throws ArgumentNullException the keys or values are null /// New Map with the items added [Pure] public TrackingHashMap AddRange(IEnumerable> range) => Wrap(Value.AddRangeWithLog(range)); /// /// Atomically adds a range of items to the map. /// /// Null is not allowed for a Key or a Value /// Range of tuples to add /// Throws ArgumentException if any of the keys already exist /// Throws ArgumentNullException the keys or values are null /// New Map with the items added [Pure] public TrackingHashMap AddRange(IEnumerable<(K Key, V Value)> range) => Wrap(Value.AddRangeWithLog(range)); /// /// Atomically adds a range of items to the map. If any of the keys exist already /// then they're ignored. /// /// Null is not allowed for a Key or a Value /// Range of tuples to add /// Throws ArgumentNullException the keys or values are null /// New Map with the items added [Pure] public TrackingHashMap TryAddRange(IEnumerable> range) => Wrap(Value.TryAddRangeWithLog(range)); /// /// Atomically adds a range of items to the map. If any of the keys exist already /// then they're ignored. /// /// Null is not allowed for a Key or a Value /// Range of tuples to add /// Throws ArgumentNullException the keys or values are null /// New Map with the items added [Pure] public TrackingHashMap TryAddRange(IEnumerable<(K Key, V Value)> range) => Wrap(Value.TryAddRangeWithLog(range)); /// /// Atomically adds a range of items to the map. If any of the keys exist already /// then they're ignored. /// /// Null is not allowed for a Key or a Value /// Range of KeyValuePairs to add /// Throws ArgumentNullException the keys or values are null /// New Map with the items added [Pure] public TrackingHashMap TryAddRange(IEnumerable> range) => Wrap(Value.TryAddRangeWithLog(range)); /// /// Atomically adds a range of items to the map. If any of the keys exist already /// then they're replaced. /// /// Null is not allowed for a Key or a Value /// Range of tuples to add /// Throws ArgumentNullException the keys or values are null /// New Map with the items added [Pure] public TrackingHashMap AddOrUpdateRange(IEnumerable> range) => Wrap(Value.AddOrUpdateRangeWithLog(range)); /// /// Atomically adds a range of items to the map. If any of the keys exist already /// then they're replaced. /// /// Null is not allowed for a Key or a Value /// Range of tuples to add /// Throws ArgumentNullException the keys or values are null /// New Map with the items added [Pure] public TrackingHashMap AddOrUpdateRange(IEnumerable<(K Key, V Value)> range) => Wrap(Value.AddOrUpdateRangeWithLog(range)); /// /// Atomically adds a range of items to the map. If any of the keys exist already /// then they're replaced. /// /// Null is not allowed for a Key or a Value /// Range of KeyValuePairs to add /// Throws ArgumentNullException the keys or values are null /// New Map with the items added [Pure] public TrackingHashMap AddOrUpdateRange(IEnumerable> range) => Wrap(Value.AddOrUpdateRangeWithLog(range)); /// /// Atomically removes an item from the map /// If the key doesn't exists, the request is ignored. /// /// Key /// New map with the item removed [Pure] public TrackingHashMap Remove(K key) => Wrap(key, Value.RemoveWithLog(key)); /// /// Retrieve a value from the map by key /// /// Key to find /// Found value [Pure] public Option Find(K key) => Value.Find(key); /// /// Retrieve a value from the map by key as an enumerable /// /// Key to find /// Found value [Pure] public Seq FindSeq(K key) => Value.FindAll(key); /// /// Retrieve a value from the map by key and pattern match the /// result. /// /// Key to find /// Found value [Pure] public R Find(K key, Func Some, Func None) => Value.Find(key, Some, None); /// /// Try to find the key in the map, if it doesn't exist, add a new /// item by invoking the delegate provided. /// /// Key to find /// Delegate to get the value /// Updated map and added value [Pure] public (TrackingHashMap Map, V Value) FindOrAdd(K key, Func None) { var (x, y, cs) = Value.FindOrAddWithLog(key, None); return (Wrap(key, (x, cs)), y); } /// /// Try to find the key in the map, if it doesn't exist, add a new /// item provided. /// /// Key to find /// Delegate to get the value /// Updated map and added value [Pure] public (TrackingHashMap, V Value) FindOrAdd(K key, V value) { var (x, y, cs) = Value.FindOrAddWithLog(key, value); return (Wrap(key, (x, cs)), y); } /// /// Try to find the key in the map, if it doesn't exist, add a new /// item by invoking the delegate provided. /// /// Key to find /// Delegate to get the value /// Updated map and added value [Pure] public (TrackingHashMap Map, Option Value) FindOrMaybeAdd(K key, Func> None) { var (x, y, cs) = Value.FindOrMaybeAddWithLog(key, None); return (Wrap(key, (x, cs)), y); } /// /// Try to find the key in the map, if it doesn't exist, add a new /// item by invoking the delegate provided. /// /// Key to find /// Delegate to get the value /// Updated map and added value [Pure] public (TrackingHashMap Map, Option Value) FindOrMaybeAdd(K key, Option None) { var (x, y, cs) = Value.FindOrMaybeAddWithLog(key, None); return (Wrap(key, (x, cs)), y); } /// /// Atomically updates an existing item /// /// Null is not allowed for a Key or a Value /// Key /// Value /// Throws ArgumentNullException the key or value are null /// New Map with the item added [Pure] public TrackingHashMap SetItem(K key, V value) => Wrap(key, Value.SetItemWithLog(key, value)); /// /// Retrieve a value from the map by key, map it to a new value, /// put it back. /// /// Key to set /// Throws ArgumentException if the item isn't found /// Throws Exception if Some returns null /// New map with the mapped value [Pure] public TrackingHashMap SetItem(K key, Func Some) => Wrap(key, Value.SetItemWithLog(key, Some)); /// /// Atomically updates an existing item, unless it doesn't exist, in which case /// it is ignored /// /// Null is not allowed for a Key or a Value /// Key /// Value /// Throws ArgumentNullException the value is null /// New Map with the item added [Pure] public TrackingHashMap TrySetItem(K key, V value) => Wrap(key, Value.TrySetItemWithLog(key, value)); /// /// Atomically sets an item by first retrieving it, applying a map, and then putting it back. /// Silently fails if the value doesn't exist /// /// Key to set /// delegate to map the existing value to a new one before setting /// Throws Exception if Some returns null /// Throws ArgumentNullException the key or value are null /// New map with the item set [Pure] public TrackingHashMap TrySetItem(K key, Func Some) => Wrap(key, Value.TrySetItemWithLog(key, Some)); /// /// Checks for existence of a key in the map /// /// Key to check /// True if an item with the key supplied is in the map [Pure] public bool ContainsKey(K key) => Value.ContainsKey(key); /// /// Checks for existence of a key in the map /// /// Key to check /// True if an item with the key supplied is in the map [Pure] public bool Contains(K key, V value) => Value.Contains(key, value); /// /// Checks for existence of a value in the map /// /// Value to check /// True if an item with the value supplied is in the map [Pure] public bool Contains(V value) => Value.Contains(value); /// /// Checks for existence of a value in the map /// /// Value to check /// True if an item with the value supplied is in the map [Pure] public bool Contains(V value) where EqV : Eq => Value.Contains(value); /// /// Checks for existence of a key in the map /// /// Key to check /// True if an item with the key supplied is in the map [Pure] public bool Contains(K key, V value) where EqV : Eq => Value.Contains(key, value); /// /// Clears all items from the map /// /// Functionally equivalent to calling Map.empty as the original structure is untouched /// Empty map [Pure] public TrackingHashMap Clear() => Wrap(Value.ClearWithLog()); /// /// Atomically adds a range of items to the map /// /// Range of KeyValuePairs to add /// Throws ArgumentException if any of the keys already exist /// New Map with the items added [Pure] public TrackingHashMap AddRange(IEnumerable> pairs) => Wrap(Value.AddRangeWithLog(pairs)); /// /// Atomically sets a series of items using the KeyValuePairs provided /// /// Items to set /// Throws ArgumentException if any of the keys aren't in the map /// New map with the items set [Pure] public TrackingHashMap SetItems(IEnumerable> items) => Wrap(Value.SetItemsWithLog(items)); /// /// Atomically sets a series of items using the Tuples provided. /// /// Items to set /// Throws ArgumentException if any of the keys aren't in the map /// New map with the items set [Pure] public TrackingHashMap SetItems(IEnumerable> items) => Wrap(Value.SetItemsWithLog(items)); /// /// Atomically sets a series of items using the Tuples provided. /// /// Items to set /// Throws ArgumentException if any of the keys aren't in the map /// New map with the items set [Pure] public TrackingHashMap SetItems(IEnumerable<(K Key, V Value)> items) => Wrap(Value.SetItemsWithLog(items)); /// /// Atomically sets a series of items using the KeyValuePairs provided. If any of the /// items don't exist then they're silently ignored. /// /// Items to set /// New map with the items set [Pure] public TrackingHashMap TrySetItems(IEnumerable> items) => Wrap(Value.TrySetItemsWithLog(items)); /// /// Atomically sets a series of items using the Tuples provided If any of the /// items don't exist then they're silently ignored. /// /// Items to set /// New map with the items set [Pure] public TrackingHashMap TrySetItems(IEnumerable> items) => Wrap(Value.TrySetItemsWithLog(items)); /// /// Atomically sets a series of items using the Tuples provided If any of the /// items don't exist then they're silently ignored. /// /// Items to set /// New map with the items set [Pure] public TrackingHashMap TrySetItems(IEnumerable<(K Key, V Value)> items) => Wrap(Value.TrySetItemsWithLog(items)); /// /// Atomically sets a series of items using the keys provided to find the items /// and the Some delegate maps to a new value. If the items don't exist then /// they're silently ignored. /// /// Keys of items to set /// Function map the existing item to a new one /// New map with the items set [Pure] public TrackingHashMap TrySetItems(IEnumerable keys, Func Some) => Wrap(Value.TrySetItemsWithLog(keys, Some)); /// /// Atomically removes a set of keys from the map /// /// Keys to remove /// New map with the items removed [Pure] public TrackingHashMap RemoveRange(IEnumerable keys) => Wrap(Value.RemoveRangeWithLog(keys)); /// /// Returns true if a Key/Value pair exists in the map /// /// Pair to find /// True if exists, false otherwise [Pure] public bool Contains(KeyValuePair pair) => Value.Contains(pair.Key, pair.Value); /// /// Enumerable of map keys /// [Pure] public Iterable Keys => Value.Keys; /// /// Enumerable of map values /// [Pure] public Iterable Values => Value.Values; /// /// Convert the map to an IDictionary /// /// [Pure] public IReadOnlyDictionary ToDictionary() => this; /// /// Map the map the a dictionary /// [Pure] public IDictionary ToDictionary( Func<(K Key, V Value), KR> keySelector, Func<(K Key, V Value), VR> valueSelector) where KR : notnull => AsEnumerable().ToDictionary(x => keySelector(x), x => valueSelector(x)); /// /// GetEnumerator - IEnumerable interface /// public IEnumerator<(K Key, V Value)> GetEnumerator() => Value.GetEnumerator(); /// /// GetEnumerator - IEnumerable interface /// IEnumerator IEnumerable.GetEnumerator() => Value.GetEnumerator(); [Pure] public Seq<(K Key, V Value)> ToSeq() => toSeq(AsEnumerable()); /// /// Allocation free conversion to a HashMap /// [Pure] public HashMap ToHashMap() => new (value); /// /// Format the collection as `[(key: value), (key: value), (key: value), ...]` /// The elipsis is used for collections over 50 items /// To get a formatted string with all the items, use `ToFullString` /// or `ToFullArrayString`. /// [Pure] public override string ToString() => CollectionFormat.ToShortArrayString(AsEnumerable().Map(kv => $"({kv.Key}: {kv.Value})"), Count); /// /// Format the collection as `(key: value), (key: value), (key: value), ...` /// [Pure] public string ToFullString(string separator = ", ") => CollectionFormat.ToFullString(AsEnumerable().Map(kv => $"({kv.Key}: {kv.Value})"), separator); /// /// Format the collection as `[(key: value), (key: value), (key: value), ...]` /// [Pure] public string ToFullArrayString(string separator = ", ") => CollectionFormat.ToFullArrayString(AsEnumerable().Map(kv => $"({kv.Key}: {kv.Value})"), separator); [Pure] public Iterable<(K Key, V Value)> AsEnumerable() => Value.AsIterable(); /// /// Implicit conversion from an untyped empty list /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator TrackingHashMap(SeqEmpty _) => Empty; /// /// Equality of keys and values with `EqDefault〈V〉` used for values /// [Pure] public static bool operator ==(TrackingHashMap lhs, TrackingHashMap rhs) => lhs.Equals(rhs); /// /// In-equality of keys and values with `EqDefault〈V〉` used for values /// [Pure] public static bool operator !=(TrackingHashMap lhs, TrackingHashMap rhs) => !(lhs == rhs); [Pure] public static TrackingHashMap operator +(TrackingHashMap lhs, TrackingHashMap rhs) => lhs.Combine(rhs); [Pure] public TrackingHashMap Combine(TrackingHashMap rhs) => Wrap(Value.AppendWithLog(rhs.Value)); [Pure] public static TrackingHashMap operator -(TrackingHashMap lhs, TrackingHashMap rhs) => lhs.Subtract(rhs); [Pure] public TrackingHashMap Subtract(TrackingHashMap rhs) => Wrap(Value.SubtractWithLog(rhs.Value)); /// /// Returns True if 'other' is a proper subset of this set /// /// True if 'other' is a proper subset of this set [Pure] public bool IsProperSubsetOf(IEnumerable<(K Key, V Value)> other) => Value.IsProperSubsetOf(other); /// /// Returns True if 'other' is a proper subset of this set /// /// True if 'other' is a proper subset of this set [Pure] public bool IsProperSubsetOf(IEnumerable other) => Value.IsProperSubsetOf(other); /// /// Returns True if 'other' is a proper superset of this set /// /// True if 'other' is a proper superset of this set [Pure] public bool IsProperSupersetOf(IEnumerable<(K Key, V Value)> other) => Value.IsProperSupersetOf(other); /// /// Returns True if 'other' is a proper superset of this set /// /// True if 'other' is a proper superset of this set [Pure] public bool IsProperSupersetOf(IEnumerable other) => Value.IsProperSupersetOf(other); /// /// Returns True if 'other' is a superset of this set /// /// True if 'other' is a superset of this set [Pure] public bool IsSubsetOf(IEnumerable<(K Key, V Value)> other) => Value.IsSubsetOf(other); /// /// Returns True if 'other' is a superset of this set /// /// True if 'other' is a superset of this set [Pure] public bool IsSubsetOf(IEnumerable other) => Value.IsSubsetOf(other); /// /// Returns True if 'other' is a superset of this set /// /// True if 'other' is a superset of this set [Pure] public bool IsSubsetOf(TrackingHashMap other) => Value.IsSubsetOf(other.Value); /// /// Returns True if 'other' is a superset of this set /// /// True if 'other' is a superset of this set [Pure] public bool IsSupersetOf(IEnumerable<(K Key, V Value)> other) => Value.IsSupersetOf(other); /// /// Returns True if 'other' is a superset of this set /// /// True if 'other' is a superset of this set [Pure] public bool IsSupersetOf(IEnumerable rhs) => Value.IsSupersetOf(rhs); /// /// Returns the elements that are in both this and other /// [Pure] public TrackingHashMap Intersect(IEnumerable rhs) => Wrap(Value.IntersectWithLog(rhs)); /// /// Returns the elements that are in both this and other /// [Pure] public TrackingHashMap Intersect(IEnumerable<(K Key, V Value)> rhs) => Wrap(Value.IntersectWithLog(rhs)); /// /// Returns the elements that are in both this and other /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public TrackingHashMap Intersect(IEnumerable<(K Key, V Value)> rhs, WhenMatched Merge) => Wrap(Value.IntersectWithLog(new TrieMap(rhs), Merge)); /// /// Returns the elements that are in both this and other /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public TrackingHashMap Intersect(HashMap rhs, WhenMatched Merge) => Wrap(Value.IntersectWithLog(rhs.Value, Merge)); /// /// Returns the elements that are in both this and other /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public TrackingHashMap Intersect(TrackingHashMap rhs, WhenMatched Merge) => Wrap(Value.IntersectWithLog(rhs.Value, Merge)); /// /// Returns True if other overlaps this set /// [Pure] public bool Overlaps(IEnumerable<(K Key, V Value)> other) => Value.Overlaps(other); /// /// Returns True if other overlaps this set /// [Pure] public bool Overlaps(IEnumerable other) => Value.Overlaps(other); /// /// Returns this - other. Only the items in this that are not in /// other will be returned. /// [Pure] public TrackingHashMap Except(IEnumerable rhs) => Wrap(Value.ExceptWithLog(rhs)); /// /// Returns this - other. Only the items in this that are not in /// other will be returned. /// [Pure] public TrackingHashMap Except(IEnumerable<(K Key, V Value)> rhs) => Wrap(Value.ExceptWithLog(rhs)); /// /// Only items that are in one set or the other will be returned. /// If an item is in both, it is dropped. /// [Pure] public TrackingHashMap SymmetricExcept(TrackingHashMap rhs) => Wrap(Value.SymmetricExceptWithLog(rhs.Value)); /// /// Only items that are in one set or the other will be returned. /// If an item is in both, it is dropped. /// [Pure] public TrackingHashMap SymmetricExcept(IEnumerable<(K Key, V Value)> rhs) => Wrap(Value.SymmetricExceptWithLog(rhs)); /// /// Finds the union of two sets and produces a new set with /// the results /// /// Other set to union with /// A set which contains all items from both sets [Pure] public TrackingHashMap Union(IEnumerable<(K, V)> rhs) => this.TryAddRange(rhs); /// /// Union two maps. /// /// /// The `WhenMatched` merge function is called when keys are present in both map to allow resolving to a /// sensible value. /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public TrackingHashMap Union(IEnumerable<(K Key, V Value)> other, WhenMatched Merge) => Wrap(Value.UnionWithLog(other, static (_, v) => v, static (_, v) => v, Merge)); /// /// Union two maps. /// /// /// The `WhenMatched` merge function is called when keys are present in both map to allow resolving to a /// sensible value. /// /// /// The `WhenMissing` function is called when there is a key in the right-hand side, but not the left-hand-side. /// This allows the `V2` value-type to be mapped to the target `V` value-type. /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public TrackingHashMap Union(IEnumerable<(K Key, W Value)> other, WhenMissing MapRight, WhenMatched Merge) => Wrap(Value.UnionWithLog(other, static (_, v) => v, MapRight, Merge)); /// /// Equality of keys and values with `EqDefault〈V〉` used for values /// [Pure] public override bool Equals(object? obj) => obj is TrackingHashMap hm && Equals(hm); /// /// Equality of keys and values with `EqDefault〈V〉` used for values /// [Pure] public bool Equals(TrackingHashMap other) => Value.Equals>(other.Value); /// /// Equality of keys and values with `EqV` used for values /// [Pure] public bool Equals(TrackingHashMap other) where EqV : Eq => Value.Equals(other.Value); /// /// Equality of keys only /// [Pure] public bool EqualsKeys(TrackingHashMap other) => Value.Equals>(other.Value); [Pure] public override int GetHashCode() => Value.GetHashCode(); /// /// Impure iteration of the bound values in the structure /// /// /// Returns the original unmodified structure /// public TrackingHashMap Do(Action f) { this.Iter(f); return this; } /// /// Atomically filter out items that return false when a predicate is applied /// /// Predicate /// New map with items filtered [Pure] [EditorBrowsable(EditorBrowsableState.Never)] public TrackingHashMap Where(Func pred) => Filter(pred); /// /// Atomically filter out items that return false when a predicate is applied /// /// Predicate /// New map with items filtered [Pure] [EditorBrowsable(EditorBrowsableState.Never)] public TrackingHashMap Where(Func pred) => Filter(pred); /// /// Return true if all items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] public bool ForAll(Func pred) { foreach (var item in AsEnumerable()) { if (!pred(item.Key, item.Value)) return false; } return true; } /// /// Return true if all items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] public bool ForAll(Func<(K Key, V Value), bool> pred) => AsEnumerable().Map(kv => (kv.Key, kv.Value)).ForAll(pred); /// /// Return true if *all* items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] public bool ForAll(Func, bool> pred) => AsEnumerable().Map(kv => new KeyValuePair(kv.Key, kv.Value)).ForAll(pred); /// /// Return true if all items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] public bool ForAll(Func pred) => Values.ForAll(pred); /// /// Return true if *any* items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied public bool Exists(Func pred) { foreach (var item in AsEnumerable()) { if (pred(item.Key, item.Value)) return true; } return false; } /// /// Return true if *any* items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] public bool Exists(Func<(K Key, V Value), bool> pred) => AsEnumerable().Map(kv => (kv.Key, kv.Value)).Exists(pred); /// /// Return true if *any* items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] public bool Exists(Func, bool> pred) => AsEnumerable().Map(kv => new KeyValuePair(kv.Key, kv.Value)).Exists(pred); /// /// Return true if *any* items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] public bool Exists(Func pred) => Values.Exists(pred); /// /// Atomically iterate through all key/value pairs in the map (in order) and execute an /// action on each /// /// Action to execute /// Unit public Unit Iter(Action action) { foreach (var item in this) { action(item.Key, item.Value); } return unit; } /// /// Atomically iterate through all values in the map (in order) and execute an /// action on each /// /// Action to execute /// Unit public Unit Iter(Action action) { foreach (var item in this) { action(item.Value); } return unit; } /// /// Atomically iterate through all key/value pairs (as tuples) in the map (in order) /// and execute an action on each /// /// Action to execute /// Unit public Unit Iter(Action> action) { foreach (var item in this) { action(new Tuple(item.Key, item.Value)); } return unit; } /// /// Atomically iterate through all key/value pairs (as tuples) in the map (in order) /// and execute an action on each /// /// Action to execute /// Unit public Unit Iter(Action<(K Key, V Value)> action) { foreach (var item in this) { action(item); } return unit; } /// /// Atomically iterate through all key/value pairs in the map (in order) and execute an /// action on each /// /// Action to execute /// Unit public Unit Iter(Action> action) { foreach (var item in this) { action(new KeyValuePair(item.Key, item.Value)); } return unit; } /// /// Atomically folds all items in the map (in order) using the folder function provided. /// /// State type /// Initial state /// Fold function /// Folded state [Pure] public S Fold(S state, Func folder) => AsEnumerable().Fold(state, (s, x) => folder(s, x.Key, x.Value)); [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] IEnumerator> IEnumerable>.GetEnumerator() => AsEnumerable().Map(p => new KeyValuePair(p.Key, p.Value)).GetEnumerator(); [Pure] IEnumerable IReadOnlyDictionary.Keys => Keys; [Pure] IEnumerable IReadOnlyDictionary.Values => Values; [Pure] public bool TryGetValue(K key, out V value) { var v = Find(key); if (v.IsSome) { value = (V)v; return true; } else { value = default!; return false; } } /// /// Get a IReadOnlyDictionary for this map. No mapping is required, so this is very fast. /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public IReadOnlyDictionary ToReadOnlyDictionary() => this; /// /// Atomically folds all items in the map (in order) using the folder function provided. /// /// State type /// Initial state /// Fold function /// Folded state [Pure] public S Fold(S state, Func folder) => Values.Fold(state, folder); [Pure] [Obsolete(Change.UseCollectionIntialiser)] public static implicit operator TrackingHashMap(ValueTuple<(K, V)> items) => [items.Item1]; [Pure] [Obsolete(Change.UseCollectionIntialiser)] public static implicit operator TrackingHashMap(((K, V), (K, V)) items) => [items.Item1, items.Item2]; [Pure] [Obsolete(Change.UseCollectionIntialiser)] public static implicit operator TrackingHashMap(((K, V), (K, V), (K, V)) items) => [items.Item1, items.Item2, items.Item3]; [Pure] [Obsolete(Change.UseCollectionIntialiser)] public static implicit operator TrackingHashMap(((K, V), (K, V), (K, V), (K, V)) items) => [items.Item1, items.Item2, items.Item3, items.Item4]; [Pure] [Obsolete(Change.UseCollectionIntialiser)] public static implicit operator TrackingHashMap(((K, V), (K, V), (K, V), (K, V), (K, V)) items) => [items.Item1, items.Item2, items.Item3, items.Item4, items.Item5]; [Pure] [Obsolete(Change.UseCollectionIntialiser)] public static implicit operator TrackingHashMap(((K, V), (K, V), (K, V), (K, V), (K, V), (K, V)) items) => [items.Item1, items.Item2, items.Item3, items.Item4, items.Item5, items.Item6]; [Pure] [Obsolete(Change.UseCollectionIntialiser)] public static implicit operator TrackingHashMap(((K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V)) items) => [items.Item1, items.Item2, items.Item3, items.Item4, items.Item5, items.Item6, items.Item7]; [Pure] [Obsolete(Change.UseCollectionIntialiser)] public static implicit operator TrackingHashMap(((K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V)) items) => [items.Item1, items.Item2, items.Item3, items.Item4, items.Item5, items.Item6, items.Item7, items.Item8]; [Pure] [Obsolete(Change.UseCollectionIntialiser)] public static implicit operator TrackingHashMap(((K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V)) items) => [items.Item1, items.Item2, items.Item3, items.Item4, items.Item5, items.Item6, items.Item7, items.Item8, items.Item9]; [Pure] [Obsolete(Change.UseCollectionIntialiser)] public static implicit operator TrackingHashMap(((K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V)) items) => [items.Item1, items.Item2, items.Item3, items.Item4, items.Item5, items.Item6, items.Item7, items.Item8, items.Item9, items.Item10]; [Pure] [Obsolete(Change.UseCollectionIntialiser)] public static implicit operator TrackingHashMap(((K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V)) items) => [items.Item1, items.Item2, items.Item3, items.Item4, items.Item5, items.Item6, items.Item7, items.Item8, items.Item9, items.Item10, items.Item11]; [Pure] [Obsolete(Change.UseCollectionIntialiser)] public static implicit operator TrackingHashMap(((K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V)) items) => [items.Item1, items.Item2, items.Item3, items.Item4, items.Item5, items.Item6, items.Item7, items.Item8, items.Item9, items.Item10, items.Item11, items.Item12]; [Pure] [Obsolete(Change.UseCollectionIntialiser)] public static implicit operator TrackingHashMap(((K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V)) items) => [items.Item1, items.Item2, items.Item3, items.Item4, items.Item5, items.Item6, items.Item7, items.Item8, items.Item9, items.Item10, items.Item11, items.Item12, items.Item13]; [Pure] [Obsolete(Change.UseCollectionIntialiser)] public static implicit operator TrackingHashMap(((K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V)) items) => [items.Item1, items.Item2, items.Item3, items.Item4, items.Item5, items.Item6, items.Item7, items.Item8, items.Item9, items.Item10, items.Item11, items.Item12, items.Item13, items.Item14]; [Pure] [Obsolete(Change.UseCollectionIntialiser)] public static implicit operator TrackingHashMap(((K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V)) items) => [items.Item1, items.Item2, items.Item3, items.Item4, items.Item5, items.Item6, items.Item7, items.Item8, items.Item9, items.Item10, items.Item11, items.Item12, items.Item13, items.Item14, items.Item15]; [Pure] [Obsolete(Change.UseCollectionIntialiser)] public static implicit operator TrackingHashMap(((K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V)) items) => [items.Item1, items.Item2, items.Item3, items.Item4, items.Item5, items.Item6, items.Item7, items.Item8, items.Item9, items.Item10, items.Item11, items.Item12, items.Item13, items.Item14, items.Item15, items.Item16]; } ================================================ FILE: LanguageExt.Core/Immutable Collections/TrackingHashMap/TrackingHashMap.Extensions.Eq.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using LanguageExt; using static LanguageExt.Prelude; using System.ComponentModel; using System.Diagnostics.Contracts; using LanguageExt.Traits; namespace LanguageExt; public static partial class TrackingHashMapExtensions { /// /// Create an immutable tracking hash-map /// [Pure] public static TrackingHashMap ToTrackingHashMap(this IEnumerable<(K, V)> items) where EqK : Eq => TrackingHashMap.createRange(items); /// /// Create an immutable tracking hash-map /// [Pure] public static TrackingHashMap ToTrackingHashMap(this IEnumerable> items) where EqK : Eq => TrackingHashMap.createRange(items); /// /// Create an immutable tracking hash-map /// [Pure] public static TrackingHashMap ToTrackingHashMap(this IEnumerable> items) where EqK : Eq => TrackingHashMap.createRange(items); } ================================================ FILE: LanguageExt.Core/Immutable Collections/TrackingHashMap/TrackingHashMap.Extensions.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using static LanguageExt.Prelude; using System.Diagnostics.Contracts; namespace LanguageExt; public static partial class TrackingHashMapExtensions { /// /// Create an immutable tracking hash-map /// [Pure] public static TrackingHashMap ToTrackingHashMap(this IEnumerable<(K, V)> items) => TrackingHashMap.createRange(items); /// /// Create an immutable tracking hash-map /// [Pure] public static TrackingHashMap ToTrackingHashMap(this IEnumerable> items) => TrackingHashMap.createRange(items); /// /// Create an immutable tracking hash-map /// [Pure] public static TrackingHashMap ToTrackingHashMap(this IEnumerable> items) => TrackingHashMap.createRange(items); /// /// Create an immutable tracking hash-map /// [Pure] public static TrackingHashMap> ToTrackingHashMap(this IEnumerable<(K1, K2, V)> items) => items.AsIterable().Fold(TrackingHashMap>(), (s, x) => s.AddOrUpdate(x.Item1, x.Item2, x.Item3)); /// /// Create an immutable tracking hash-map /// [Pure] public static TrackingHashMap> ToTrackingHashMap(this IEnumerable> items) => items.AsIterable().Fold(TrackingHashMap>(), (s, x) => s.AddOrUpdate(x.Item1, x.Item2, x.Item3)); /// /// Create an immutable tracking hash-map /// [Pure] public static TrackingHashMap>> ToTrackingHashMap(this IEnumerable<(K1, K2, K3, V)> items) => items.AsIterable().Fold(TrackingHashMap>>(), (s, x) => s.AddOrUpdate(x.Item1, x.Item2, x.Item3, x.Item4)); /// /// Create an immutable tracking hash-map /// [Pure] public static TrackingHashMap>> ToTrackingHashMap(this IEnumerable> items) => items.AsIterable().Fold(TrackingHashMap>>(), (s, x) => s.AddOrUpdate(x.Item1, x.Item2, x.Item3, x.Item4)); /// /// Create an immutable tracking hash-map /// [Pure] public static TrackingHashMap>>> ToTrackingHashMap(this IEnumerable<(K1, K2, K3, K4, V)> items) => items.AsIterable().Fold(TrackingHashMap>>>(), (s, x) => s.AddOrUpdate(x.Item1, x.Item2, x.Item3, x.Item4, x.Item5)); /// /// Create an immutable tracking hash-map /// [Pure] public static TrackingHashMap>>> ToTrackingHashMap(this IEnumerable> items) => items.AsIterable().Fold(TrackingHashMap>>>(), (s, x) => s.AddOrUpdate(x.Item1, x.Item2, x.Item3, x.Item4, x.Item5)); /// /// Number of items in the map /// [Pure] public static int Count(this TrackingHashMap self) => self.Count; [Pure] public static int Sum(this TrackingHashMap self) => self.Values.Sum(); [Pure] public static Option Find(this TrackingHashMap> self, A outerKey, B innerKey) => self.Find(outerKey, b => b.Find(innerKey), () => None); [Pure] public static Option Find(this TrackingHashMap>> self, A aKey, B bKey, C cKey) => self.Find(aKey, b => b.Find(bKey, c => c.Find(cKey), () => None), () => None); [Pure] public static R Find(this TrackingHashMap> self, A outerKey, B innerKey, Func Some, Func None) => self.Find(outerKey, b => b.Find(innerKey, Some, None), None); [Pure] public static R Find(this TrackingHashMap>> self, A aKey, B bKey, C cKey, Func Some, Func None) => self.Find(aKey, b => b.Find(bKey, c => c.Find(cKey, Some, None), None), None); [Pure] public static R Find(this TrackingHashMap>>> self, A aKey, B bKey, C cKey, D dKey, Func Some, Func None) => self.Find(aKey, b => b.Find(bKey, c => c.Find(cKey, d => d.Find(dKey, Some, None), None), None), None); [Pure] public static TrackingHashMap> AddOrUpdate(this TrackingHashMap> self, A outerKey, B innerKey, Func Some, Func None) => self.AddOrUpdate( outerKey, b => b.AddOrUpdate(innerKey, Some, None), () => TrackingHashMap((innerKey, None())) ); [Pure] public static TrackingHashMap> AddOrUpdate(this TrackingHashMap> self, A outerKey, B innerKey, T value) => self.AddOrUpdate( outerKey, b => b.AddOrUpdate(innerKey, _ => value, value), () => TrackingHashMap((innerKey, value)) ); [Pure] public static TrackingHashMap>> AddOrUpdate(this TrackingHashMap>> self, A aKey, B bKey, C cKey, T value) => self.AddOrUpdate( aKey, bKey, c => c.AddOrUpdate(cKey, _ => value, value), () => TrackingHashMap((cKey, value)) ); [Pure] public static TrackingHashMap>> AddOrUpdate(this TrackingHashMap>> self, A aKey, B bKey, C cKey, Func Some, Func None) => self.AddOrUpdate( aKey, bKey, c => c.AddOrUpdate(cKey, Some, None), () => TrackingHashMap((cKey, None())) ); [Pure] public static TrackingHashMap>>> AddOrUpdate(this TrackingHashMap>>> self, A aKey, B bKey, C cKey, D dKey, T value) => self.AddOrUpdate( aKey, bKey, cKey, d => d.AddOrUpdate(dKey, _ => value, value), () => TrackingHashMap((dKey, value)) ); [Pure] public static TrackingHashMap>>> AddOrUpdate(this TrackingHashMap>>> self, A aKey, B bKey, C cKey, D dKey, Func Some, Func None) => self.AddOrUpdate( aKey, bKey, cKey, d => d.AddOrUpdate(dKey, Some, None), () => TrackingHashMap((dKey, None())) ); [Pure] public static TrackingHashMap> Remove(this TrackingHashMap> self, A outerKey, B innerKey) { var b = self.Find(outerKey); if (b.IsSome) { var bv = b.Value.Remove(innerKey); if (bv.Count() == 0) { return self.Remove(outerKey); } else { return self.SetItem(outerKey, bv); } } else { return self; } } [Pure] public static TrackingHashMap>> Remove(this TrackingHashMap>> self, A aKey, B bKey, C cKey) { var b = self.Find(aKey); if (b.IsSome) { var c = b.Value.Find(bKey); if (c.IsSome) { var cv = c.Value.Remove(cKey); if (cv.Count() == 0) { var bv = b.Value.Remove(bKey); if (b.Value.Count() == 0) { return self.Remove(aKey); } else { return self.SetItem(aKey, bv); } } else { return self.SetItem(aKey, b.Value.SetItem(bKey, cv)); } } else { return self; } } else { return self; } } } ================================================ FILE: LanguageExt.Core/Immutable Collections/TrackingHashMap/TrackingHashMap.Module.Eq.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics.Contracts; using LanguageExt.Traits; namespace LanguageExt; /// /// Immutable tracking hash-map module /// public static partial class TrackingHashMap { /// /// Creates a singleton TrackingHashMap /// [Pure] public static TrackingHashMap singleton((K, V) value) where EqK : Eq => [value]; /// /// Creates a singleton TrackingHashMap /// [Pure] public static TrackingHashMap singleton(K key, V value) where EqK : Eq => [(key, value)]; /// /// Creates a new empty TrackingHashMap /// [Pure] public static TrackingHashMap empty() where EqK : Eq => TrackingHashMap.Empty; /// /// Creates a new empty TrackingHashMap /// [Pure] public static TrackingHashMap create() where EqK : Eq => TrackingHashMap.Empty; /// /// Creates a new Map seeded with the keyValues provided /// [Pure] public static TrackingHashMap create(Tuple head, params Tuple[] tail) where EqK : Eq => empty().AddRange(head.Cons(tail)); /// /// Creates a new Map seeded with the keyValues provided /// [Pure] public static TrackingHashMap create((K, V) head, params (K, V)[] tail) where EqK : Eq => empty().AddRange(head.Cons(tail)); /// /// Creates a new Map seeded with the keyValues provided /// [Pure] public static TrackingHashMap create(KeyValuePair head, params KeyValuePair[] tail) where EqK : Eq => empty().AddRange(head.Cons(tail)); /// /// Creates a new Map seeded with the keyValues provided /// [Pure] public static TrackingHashMap createRange(IEnumerable> keyValues) where EqK : Eq => empty().AddRange(keyValues); /// /// Creates a new Map seeded with the keyValues provided /// [Pure] public static TrackingHashMap createRange(IEnumerable<(K, V)> keyValues) where EqK : Eq => new (keyValues); /// /// Creates a new Map seeded with the keyValues provided /// [Pure] public static TrackingHashMap createRange(ReadOnlySpan<(K, V)> keyValues) where EqK : Eq => keyValues.IsEmpty ? TrackingHashMap.Empty : new(keyValues); /// /// Creates a new Map seeded with the keyValues provided /// [Pure] public static TrackingHashMap createRange(IEnumerable> keyValues) where EqK : Eq => empty().AddRange(keyValues); /// /// Atomically adds a new item to the map /// /// Null is not allowed for a Key or a Value /// Key /// Value /// Throws ArgumentException if the key already exists /// Throws ArgumentNullException the key or value are null /// New Map with the item added [Pure] public static TrackingHashMap add(TrackingHashMap map, K key, V value) where EqK : Eq => map.Add(key, value); /// /// Atomically adds a new item to the map. /// If the key already exists, then the new item is ignored /// /// Null is not allowed for a Key or a Value /// Key /// Value /// Throws ArgumentNullException the key or value are null /// New Map with the item added [Pure] public static TrackingHashMap tryAdd(TrackingHashMap map, K key, V value) where EqK : Eq => map.TryAdd(key, value); /// /// Atomically adds a new item to the map. /// If the key already exists, the new item replaces it. /// /// Null is not allowed for a Key or a Value /// Key /// Value /// Throws ArgumentNullException the key or value are null /// New Map with the item added [Pure] public static TrackingHashMap addOrUpdate(TrackingHashMap map, K key, V value) where EqK : Eq => map.AddOrUpdate(key, value); /// /// Retrieve a value from the map by key, map it to a new value, /// put it back. If it doesn't exist, add a new one based on None result. /// /// Key to find /// Throws Exception if None returns null /// Throws Exception if Some returns null /// New map with the mapped value [Pure] public static TrackingHashMap addOrUpdate(TrackingHashMap map, K key, Func Some, Func None) where EqK : Eq => map.AddOrUpdate(key, Some, None); /// /// Retrieve a value from the map by key, map it to a new value, /// put it back. If it doesn't exist, add a new one based on None result. /// /// Key to find /// Throws ArgumentNullException if None is null /// Throws Exception if Some returns null /// New map with the mapped value [Pure] public static TrackingHashMap addOrUpdate(TrackingHashMap map, K key, Func Some, V None) where EqK : Eq => map.AddOrUpdate(key, Some, None); /// /// Atomically adds a range of items to the map. /// /// Null is not allowed for a Key or a Value /// Range of tuples to add /// Throws ArgumentException if any of the keys already exist /// Throws ArgumentNullException the keys or values are null /// New Map with the items added [Pure] public static TrackingHashMap addRange(TrackingHashMap map, IEnumerable> keyValues) where EqK : Eq => map.AddRange(keyValues); /// /// Atomically adds a range of items to the map. /// /// Null is not allowed for a Key or a Value /// Range of tuples to add /// Throws ArgumentException if any of the keys already exist /// Throws ArgumentNullException the keys or values are null /// New Map with the items added [Pure] public static TrackingHashMap addRange(TrackingHashMap map, IEnumerable<(K, V)> keyValues) where EqK : Eq => map.AddRange(keyValues); /// /// Atomically adds a range of items to the map. /// /// Null is not allowed for a Key or a Value /// Range of tuples to add /// Throws ArgumentException if any of the keys already exist /// Throws ArgumentNullException the keys or values are null /// New Map with the items added [Pure] public static TrackingHashMap addRange(TrackingHashMap map, IEnumerable> keyValues) where EqK : Eq => map.AddRange(keyValues); /// /// Atomically adds a range of items to the map. If any of the keys exist already /// then they're ignored. /// /// Null is not allowed for a Key or a Value /// Range of tuples to add /// Throws ArgumentNullException the keys or values are null /// New Map with the items added [Pure] public static TrackingHashMap tryAddRange(TrackingHashMap map, IEnumerable> keyValues) where EqK : Eq => map.TryAddRange(keyValues); /// /// Atomically adds a range of items to the map. If any of the keys exist already /// then they're ignored. /// /// Null is not allowed for a Key or a Value /// Range of tuples to add /// Throws ArgumentNullException the keys or values are null /// New Map with the items added [Pure] public static TrackingHashMap tryAddRange(TrackingHashMap map, IEnumerable<(K, V)> keyValues) where EqK : Eq => map.TryAddRange(keyValues); /// /// Atomically adds a range of items to the map. If any of the keys exist already /// then they're ignored. /// /// Null is not allowed for a Key or a Value /// Range of KeyValuePairs to add /// Throws ArgumentNullException the keys or values are null /// New Map with the items added [Pure] public static TrackingHashMap tryAddRange(TrackingHashMap map, IEnumerable> keyValues) where EqK : Eq => map.TryAddRange(keyValues); /// /// Atomically adds a range of items to the map. If any of the keys exist already /// then they're replaced. /// /// Range of tuples to add /// New Map with the items added [Pure] public static TrackingHashMap addOrUpdateRange(TrackingHashMap map, IEnumerable> range) where EqK : Eq => map.AddOrUpdateRange(range); /// /// Atomically adds a range of items to the map. If any of the keys exist already /// then they're replaced. /// /// Range of tuples to add /// New Map with the items added [Pure] public static TrackingHashMap addOrUpdateRange(TrackingHashMap map, IEnumerable<(K, V)> range) where EqK : Eq => map.AddOrUpdateRange(range); /// /// Atomically adds a range of items to the map. If any of the keys exist already /// then they're replaced. /// /// Null is not allowed for a Key or a Value /// Range of KeyValuePairs to add /// Throws ArgumentNullException the keys or values are null /// New Map with the items added [Pure] public static TrackingHashMap addOrUpdateRange(TrackingHashMap map, IEnumerable> range) where EqK : Eq => map.AddOrUpdateRange(range); /// /// Atomically removes an item from the map /// If the key doesn't exists, the request is ignored. /// /// Key /// New map with the item removed [Pure] public static TrackingHashMap remove(TrackingHashMap map, K key) where EqK : Eq => map.Remove(key); /// /// Checks for existence of a key in the map /// /// Key to check /// True if an item with the key supplied is in the map [Pure] public static bool containsKey(TrackingHashMap map, K key) where EqK : Eq => map.ContainsKey(key); /// /// Checks for existence of a key in the map /// /// Key to check /// True if an item with the key supplied is in the map [Pure] public static bool contains(TrackingHashMap map, KeyValuePair kv) where EqK : Eq => map.Contains(kv.Key, kv.Value); /// /// Checks for existence of a key in the map /// /// Key to check /// True if an item with the key supplied is in the map [Pure] public static bool contains(TrackingHashMap map, Tuple kv) where EqK : Eq => map.Contains(kv.Item1, kv.Item2); /// /// Checks for existence of a key in the map /// /// Key to check /// True if an item with the key supplied is in the map [Pure] public static bool contains(TrackingHashMap map, (K, V) kv) where EqK : Eq => map.Contains(kv.Item1, kv.Item2); /// /// Atomically updates an existing item /// /// Null is not allowed for a Key or a Value /// Key /// Value /// Throws ArgumentNullException the key or value are null /// New Map with the item added [Pure] public static TrackingHashMap setItem(TrackingHashMap map, K key, V value) where EqK : Eq => map.SetItem(key, value); /// /// Atomically updates an existing item, unless it doesn't exist, in which case /// it is ignored /// /// Null is not allowed for a Key or a Value /// Key /// Value /// Throws ArgumentNullException the value is null /// New Map with the item added [Pure] public static TrackingHashMap trySetItem(TrackingHashMap map, K key, V value) where EqK : Eq => map.TrySetItem(key, value); /// /// Atomically sets an item by first retrieving it, applying a map (Some), and then putting /// it back. Silently fails if the value doesn't exist. /// /// Key to set /// Throws Exception if Some returns null /// delegate to map the existing value to a new one before setting /// New map with the item set [Pure] public static TrackingHashMap trySetItem(TrackingHashMap map, K key, Func Some) where EqK : Eq => map.TrySetItem(key, Some); /// /// Atomically sets a series of items using the Tuples provided /// /// Items to set /// Throws ArgumentException if any of the keys aren't in the map /// New map with the items set [Pure] public static TrackingHashMap setItems(TrackingHashMap map, IEnumerable> items) where EqK : Eq => map.SetItems(items); /// /// Atomically sets a series of items using the Tuples provided /// /// Items to set /// Throws ArgumentException if any of the keys aren't in the map /// New map with the items set [Pure] public static TrackingHashMap setItems(TrackingHashMap map, IEnumerable<(K, V)> items) where EqK : Eq => map.SetItems(items); /// /// Atomically sets a series of items using the KeyValuePairs provided /// /// Items to set /// Throws ArgumentException if any of the keys aren't in the map /// New map with the items set [Pure] public static TrackingHashMap setItems(TrackingHashMap map, IEnumerable> items) where EqK : Eq => map.SetItems(items); /// /// Atomically sets a series of items using the Tuples provided. /// /// Items to set /// Throws ArgumentException if any of the keys aren't in the map /// New map with the items set [Pure] public static TrackingHashMap trySetItems(TrackingHashMap map, IEnumerable> items) where EqK : Eq => map.SetItems(items); /// /// Atomically sets a series of items using the Tuples provided. /// /// Items to set /// Throws ArgumentException if any of the keys aren't in the map /// New map with the items set [Pure] public static TrackingHashMap trySetItems(TrackingHashMap map, IEnumerable<(K, V)> items) where EqK : Eq => map.SetItems(items); /// /// Atomically sets a series of items using the KeyValuePairs provided. If any of the /// items don't exist then they're silently ignored. /// /// Items to set /// New map with the items set [Pure] public static TrackingHashMap trySetItems(TrackingHashMap map, IEnumerable> items) where EqK : Eq => map.TrySetItems(items); /// /// Atomically sets a series of items using the keys provided to find the items /// and the Some delegate maps to a new value. If the items don't exist then /// they're silently ignored. /// /// Keys of items to set /// Function map the existing item to a new one /// New map with the items set [Pure] public static TrackingHashMap trySetItems(TrackingHashMap map, IEnumerable keys, Func Some) where EqK : Eq => map.TrySetItems(keys, Some); /// /// Retrieve a value from the map by key /// /// Key to find /// Found value [Pure] public static Option find(TrackingHashMap map, K key) where EqK : Eq => map.Find(key); /// /// Retrieve a value from the map by key as an enumerable /// /// Key to find /// Found value [Pure] public static IEnumerable findSeq(TrackingHashMap map, K key) where EqK : Eq => map.FindSeq(key); /// /// Retrieve a value from the map by key and pattern match the /// result. /// /// Key to find /// Found value [Pure] public static R find(TrackingHashMap map, K key, Func Some, Func None) where EqK : Eq => map.Find(key, Some, None); /// /// Retrieve a value from the map by key, map it to a new value, /// put it back. /// /// Key to find /// New map with the mapped value [Pure] public static TrackingHashMap setItem(TrackingHashMap map, K key, Func mapper) where EqK : Eq => map.SetItem(key, mapper); /// /// Atomically iterate through all key/value pairs in the map (in order) and execute an /// action on each /// /// Action to execute /// Unit public static Unit iter(TrackingHashMap map, Action action) where EqK : Eq => map.Iter(action); /// /// Atomically iterate through all key/value pairs in the map (in order) and execute an /// action on each /// /// Action to execute /// Unit public static Unit iter(TrackingHashMap map, Action action) where EqK : Eq => map.Iter(action); /// /// Return true if all items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] public static bool forall(TrackingHashMap map, Func pred) where EqK : Eq => map.ForAll(pred); /// /// Return true if all items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] public static bool forall(TrackingHashMap map, Func pred) where EqK : Eq => map.ForAll(pred); /// /// Return true if all items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] public static bool forall(TrackingHashMap map, Func<(K Key, V Value), bool> pred) where EqK : Eq => map.ForAll(pred); /// /// Return true if all items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] public static bool forall(TrackingHashMap map, Func, bool> pred) where EqK : Eq => map.ForAll(pred); /// /// Atomically filter out items that return false when a predicate is applied /// /// Predicate /// New map with items filtered [Pure] public static TrackingHashMap filter(TrackingHashMap map, Func predicate) where EqK : Eq => map.Filter(predicate); /// /// Atomically filter out items that return false when a predicate is applied /// /// Predicate /// New map with items filtered [Pure] public static TrackingHashMap filter(TrackingHashMap map, Func predicate) where EqK : Eq => map.Filter(predicate); /// /// Number of items in the map /// [Pure] public static int length(TrackingHashMap map) where EqK : Eq => map.Count; /// /// Atomically folds all items in the map (in order) using the folder function provided. /// /// State type /// Initial state /// Fold function /// Folded state [Pure] public static S fold(TrackingHashMap map, S state, Func folder) where EqK : Eq => map.Fold(state, folder); /// /// Atomically folds all items in the map (in order) using the folder function provided. /// /// State type /// Initial state /// Fold function /// Folded state [Pure] public static S fold(TrackingHashMap map, S state, Func folder) where EqK : Eq => map.Fold(state, folder); /// /// Return true if *any* items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] public static bool exists(TrackingHashMap map, Func pred) where EqK : Eq => map.Exists(pred); /// /// Return true if *any* items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] public static bool exists(TrackingHashMap map, Func<(K Key, V Value), bool> pred) where EqK : Eq => map.Exists(pred); /// /// Return true if *any* items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] public static bool exists(TrackingHashMap map, Func, bool> pred) where EqK : Eq => map.Exists(pred); /// /// Return true if *any* items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] public static bool exists(TrackingHashMap map, Func pred) where EqK : Eq => map.Exists(pred); } ================================================ FILE: LanguageExt.Core/Immutable Collections/TrackingHashMap/TrackingHashMap.Module.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics.Contracts; namespace LanguageExt; /// /// Immutable tracking hash-map module /// public static partial class TrackingHashMap { /// /// Creates a new empty TrackingHashMap /// [Pure] public static TrackingHashMap empty() => TrackingHashMap.Empty; /// /// Creates a singleton TrackingHashMap /// [Pure] public static TrackingHashMap singleton((K, V) value) => [value]; /// /// Creates a singleton TrackingHashMap /// [Pure] public static TrackingHashMap singleton(K key, V value) => [(key, value)]; /// /// Creates a new empty TrackingHashMap /// [Pure] public static TrackingHashMap create() => TrackingHashMap.Empty; /// /// Creates a new Map seeded with the keyValues provided /// [Pure] public static TrackingHashMap create(Tuple head, params Tuple[] tail) => empty().AddRange(head.Cons(tail)); /// /// Creates a new Map seeded with the keyValues provided /// [Pure] public static TrackingHashMap create((K, V) head, params (K, V)[] tail) => empty().AddRange(head.Cons(tail)); /// /// Creates a new Map seeded with the keyValues provided /// [Pure] public static TrackingHashMap create(KeyValuePair head, params KeyValuePair[] tail) => empty().AddRange(head.Cons(tail)); /// /// Creates a new Map seeded with the keyValues provided /// [Pure] public static TrackingHashMap createRange(IEnumerable> keyValues) => empty().AddRange(keyValues); /// /// Creates a new Map seeded with the keyValues provided /// [Pure] public static TrackingHashMap createRange(IEnumerable<(K, V)> keyValues) => new (keyValues); /// /// Creates a new Map seeded with the keyValues provided /// [Pure] public static TrackingHashMap createRange(ReadOnlySpan<(K, V)> keyValues) => keyValues.IsEmpty ? TrackingHashMap.Empty : new (keyValues); /// /// Creates a new Map seeded with the keyValues provided /// [Pure] public static TrackingHashMap createRange(IEnumerable> keyValues) => empty().AddRange(keyValues); /// /// Atomically adds a new item to the map /// /// Null is not allowed for a Key or a Value /// Key /// Value /// Throws ArgumentException if the key already exists /// Throws ArgumentNullException the key or value are null /// New Map with the item added [Pure] public static TrackingHashMap add(TrackingHashMap map, K key, V value) => map.Add(key, value); /// /// Atomically adds a new item to the map. /// If the key already exists, then the new item is ignored /// /// Null is not allowed for a Key or a Value /// Key /// Value /// Throws ArgumentNullException the key or value are null /// New Map with the item added [Pure] public static TrackingHashMap tryAdd(TrackingHashMap map, K key, V value) => map.TryAdd(key, value); /// /// Atomically adds a new item to the map. /// If the key already exists, the new item replaces it. /// /// Null is not allowed for a Key or a Value /// Key /// Value /// Throws ArgumentNullException the key or value are null /// New Map with the item added [Pure] public static TrackingHashMap addOrUpdate(TrackingHashMap map, K key, V value) => map.AddOrUpdate(key, value); /// /// Retrieve a value from the map by key, map it to a new value, /// put it back. If it doesn't exist, add a new one based on None result. /// /// Key to find /// Throws Exception if None returns null /// Throws Exception if Some returns null /// New map with the mapped value [Pure] public static TrackingHashMap addOrUpdate(TrackingHashMap map, K key, Func Some, Func None) => map.AddOrUpdate(key, Some, None); /// /// Retrieve a value from the map by key, map it to a new value, /// put it back. If it doesn't exist, add a new one based on None result. /// /// Key to find /// Throws ArgumentNullException if None is null /// Throws Exception if Some returns null /// New map with the mapped value [Pure] public static TrackingHashMap addOrUpdate(TrackingHashMap map, K key, Func Some, V None) => map.AddOrUpdate(key, Some, None); /// /// Atomically adds a range of items to the map. /// /// Null is not allowed for a Key or a Value /// Range of tuples to add /// Throws ArgumentException if any of the keys already exist /// Throws ArgumentNullException the keys or values are null /// New Map with the items added [Pure] public static TrackingHashMap addRange(TrackingHashMap map, IEnumerable> keyValues) => map.AddRange(keyValues); /// /// Atomically adds a range of items to the map. /// /// Null is not allowed for a Key or a Value /// Range of tuples to add /// Throws ArgumentException if any of the keys already exist /// Throws ArgumentNullException the keys or values are null /// New Map with the items added [Pure] public static TrackingHashMap addRange(TrackingHashMap map, IEnumerable<(K, V)> keyValues) => map.AddRange(keyValues); /// /// Atomically adds a range of items to the map. /// /// Null is not allowed for a Key or a Value /// Range of tuples to add /// Throws ArgumentException if any of the keys already exist /// Throws ArgumentNullException the keys or values are null /// New Map with the items added [Pure] public static TrackingHashMap addRange(TrackingHashMap map, IEnumerable> keyValues) => map.AddRange(keyValues); /// /// Atomically adds a range of items to the map. If any of the keys exist already /// then they're ignored. /// /// Null is not allowed for a Key or a Value /// Range of tuples to add /// Throws ArgumentNullException the keys or values are null /// New Map with the items added [Pure] public static TrackingHashMap tryAddRange(TrackingHashMap map, IEnumerable> keyValues) => map.TryAddRange(keyValues); /// /// Atomically adds a range of items to the map. If any of the keys exist already /// then they're ignored. /// /// Null is not allowed for a Key or a Value /// Range of tuples to add /// Throws ArgumentNullException the keys or values are null /// New Map with the items added [Pure] public static TrackingHashMap tryAddRange(TrackingHashMap map, IEnumerable<(K, V)> keyValues) => map.TryAddRange(keyValues); /// /// Atomically adds a range of items to the map. If any of the keys exist already /// then they're ignored. /// /// Null is not allowed for a Key or a Value /// Range of KeyValuePairs to add /// Throws ArgumentNullException the keys or values are null /// New Map with the items added [Pure] public static TrackingHashMap tryAddRange(TrackingHashMap map, IEnumerable> keyValues) => map.TryAddRange(keyValues); /// /// Atomically adds a range of items to the map. If any of the keys exist already /// then they're replaced. /// /// Range of tuples to add /// New Map with the items added [Pure] public static TrackingHashMap addOrUpdateRange(TrackingHashMap map, IEnumerable> range) => map.AddOrUpdateRange(range); /// /// Atomically adds a range of items to the map. If any of the keys exist already /// then they're replaced. /// /// Range of tuples to add /// New Map with the items added [Pure] public static TrackingHashMap addOrUpdateRange(TrackingHashMap map, IEnumerable<(K, V)> range) => map.AddOrUpdateRange(range); /// /// Atomically adds a range of items to the map. If any of the keys exist already /// then they're replaced. /// /// Null is not allowed for a Key or a Value /// Range of KeyValuePairs to add /// Throws ArgumentNullException the keys or values are null /// New Map with the items added [Pure] public static TrackingHashMap addOrUpdateRange(TrackingHashMap map, IEnumerable> range) => map.AddOrUpdateRange(range); /// /// Atomically removes an item from the map /// If the key doesn't exists, the request is ignored. /// /// Key /// New map with the item removed [Pure] public static TrackingHashMap remove(TrackingHashMap map, K key) => map.Remove(key); /// /// Checks for existence of a key in the map /// /// Key to check /// True if an item with the key supplied is in the map [Pure] public static bool containsKey(TrackingHashMap map, K key) => map.ContainsKey(key); /// /// Checks for existence of a key in the map /// /// Key to check /// True if an item with the key supplied is in the map [Pure] public static bool contains(TrackingHashMap map, KeyValuePair kv) => map.Contains(kv.Key, kv.Value); /// /// Checks for existence of a key in the map /// /// Key to check /// True if an item with the key supplied is in the map [Pure] public static bool contains(TrackingHashMap map, Tuple kv) => map.Contains(kv.Item1, kv.Item2); /// /// Checks for existence of a key in the map /// /// Key to check /// True if an item with the key supplied is in the map [Pure] public static bool contains(TrackingHashMap map, (K, V) kv) => map.Contains(kv.Item1, kv.Item2); /// /// Atomically updates an existing item /// /// Null is not allowed for a Key or a Value /// Key /// Value /// Throws ArgumentNullException the key or value are null /// New Map with the item added [Pure] public static TrackingHashMap setItem(TrackingHashMap map, K key, V value) => map.SetItem(key, value); /// /// Atomically updates an existing item, unless it doesn't exist, in which case /// it is ignored /// /// Null is not allowed for a Key or a Value /// Key /// Value /// Throws ArgumentNullException the value is null /// New Map with the item added [Pure] public static TrackingHashMap trySetItem(TrackingHashMap map, K key, V value) => map.TrySetItem(key, value); /// /// Atomically sets an item by first retrieving it, applying a map (Some), and then putting /// it back. Silently fails if the value doesn't exist. /// /// Key to set /// Throws Exception if Some returns null /// delegate to map the existing value to a new one before setting /// New map with the item set [Pure] public static TrackingHashMap trySetItem(TrackingHashMap map, K key, Func Some) => map.TrySetItem(key, Some); /// /// Atomically sets a series of items using the Tuples provided /// /// Items to set /// Throws ArgumentException if any of the keys aren't in the map /// New map with the items set [Pure] public static TrackingHashMap setItems(TrackingHashMap map, IEnumerable> items) => map.SetItems(items); /// /// Atomically sets a series of items using the Tuples provided /// /// Items to set /// Throws ArgumentException if any of the keys aren't in the map /// New map with the items set [Pure] public static TrackingHashMap setItems(TrackingHashMap map, IEnumerable<(K, V)> items) => map.SetItems(items); /// /// Atomically sets a series of items using the KeyValuePairs provided /// /// Items to set /// Throws ArgumentException if any of the keys aren't in the map /// New map with the items set [Pure] public static TrackingHashMap setItems(TrackingHashMap map, IEnumerable> items) => map.SetItems(items); /// /// Atomically sets a series of items using the Tuples provided. /// /// Items to set /// Throws ArgumentException if any of the keys aren't in the map /// New map with the items set [Pure] public static TrackingHashMap trySetItems(TrackingHashMap map, IEnumerable> items) => map.SetItems(items); /// /// Atomically sets a series of items using the Tuples provided. /// /// Items to set /// Throws ArgumentException if any of the keys aren't in the map /// New map with the items set [Pure] public static TrackingHashMap trySetItems(TrackingHashMap map, IEnumerable<(K, V)> items) => map.SetItems(items); /// /// Atomically sets a series of items using the KeyValuePairs provided. If any of the /// items don't exist then they're silently ignored. /// /// Items to set /// New map with the items set [Pure] public static TrackingHashMap trySetItems(TrackingHashMap map, IEnumerable> items) => map.TrySetItems(items); /// /// Atomically sets a series of items using the keys provided to find the items /// and the Some delegate maps to a new value. If the items don't exist then /// they're silently ignored. /// /// Keys of items to set /// Function map the existing item to a new one /// New map with the items set [Pure] public static TrackingHashMap trySetItems(TrackingHashMap map, IEnumerable keys, Func Some) => map.TrySetItems(keys, Some); /// /// Retrieve a value from the map by key /// /// Key to find /// Found value [Pure] public static Option find(TrackingHashMap map, K key) => map.Find(key); /// /// Retrieve a value from the map by key as an enumerable /// /// Key to find /// Found value [Pure] public static IEnumerable findSeq(TrackingHashMap map, K key) => map.FindSeq(key); /// /// Retrieve a value from the map by key and pattern match the /// result. /// /// Key to find /// Found value [Pure] public static R find(TrackingHashMap map, K key, Func Some, Func None) => map.Find(key, Some, None); /// /// Retrieve a value from the map by key, map it to a new value, /// put it back. /// /// Key to find /// New map with the mapped value [Pure] public static TrackingHashMap setItem(TrackingHashMap map, K key, Func mapper) => map.SetItem(key, mapper); /// /// Atomically iterate through all key/value pairs in the map (in order) and execute an /// action on each /// /// Action to execute /// Unit public static Unit iter(TrackingHashMap map, Action action) => map.Iter(action); /// /// Atomically iterate through all key/value pairs in the map (in order) and execute an /// action on each /// /// Action to execute /// Unit public static Unit iter(TrackingHashMap map, Action action) => map.Iter(action); /// /// Return true if all items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] public static bool forall(TrackingHashMap map, Func pred) => map.ForAll(pred); /// /// Return true if all items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] public static bool forall(TrackingHashMap map, Func pred) => map.ForAll(pred); /// /// Return true if all items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] public static bool forall(TrackingHashMap map, Func<(K Key, V Value), bool> pred) => map.ForAll(pred); /// /// Return true if all items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] public static bool forall(TrackingHashMap map, Func, bool> pred) => map.ForAll(pred); /// /// Atomically filter out items that return false when a predicate is applied /// /// Predicate /// New map with items filtered [Pure] public static TrackingHashMap filter(TrackingHashMap map, Func predicate) => map.Filter(predicate); /// /// Atomically filter out items that return false when a predicate is applied /// /// Predicate /// New map with items filtered [Pure] public static TrackingHashMap filter(TrackingHashMap map, Func predicate) => map.Filter(predicate); /// /// Number of items in the map /// [Pure] public static int length(TrackingHashMap map) => map.Count; /// /// Atomically folds all items in the map (in order) using the folder function provided. /// /// State type /// Initial state /// Fold function /// Folded state [Pure] public static S fold(TrackingHashMap map, S state, Func folder) => map.Fold(state, folder); /// /// Atomically folds all items in the map (in order) using the folder function provided. /// /// State type /// Initial state /// Fold function /// Folded state [Pure] public static S fold(TrackingHashMap map, S state, Func folder) => map.Fold(state, folder); /// /// Return true if *any* items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] public static bool exists(TrackingHashMap map, Func pred) => map.Exists(pred); /// /// Return true if *any* items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] public static bool exists(TrackingHashMap map, Func<(K Key, V Value), bool> pred) => map.Exists(pred); /// /// Return true if *any* items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] public static bool exists(TrackingHashMap map, Func, bool> pred) => map.Exists(pred); /// /// Return true if *any* items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] public static bool exists(TrackingHashMap map, Func pred) => map.Exists(pred); } ================================================ FILE: LanguageExt.Core/Immutable Collections/TrackingHashMap/TrackingHashMap.cs ================================================ #pragma warning disable CS0693 // Type parameter has the same name as the type parameter from outer type using LanguageExt.ClassInstances; using static LanguageExt.Prelude; using System; using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics.Contracts; using LanguageExt.Traits; using System.Linq; using System.Runtime.CompilerServices; namespace LanguageExt; /// /// Unsorted immutable hash-map that tracks changes. /// /// /// Changes are accessible via the `Changes` property. It is a `HashMap` of `Change` values from either the initial /// empty state of the collection, or since the last call to `Snapshot()`. /// /// The fact that the changes are represented as a single-value `HashMap` shows that the tracked changes are not an /// ever increasing log of changes, but instead a morphism between one previous state of the `TrackingHashMap` and /// another. Therefore there's at most one morphism for each key, and potentially none. /// /// The morphisms are: /// /// * `EntryAdded` /// * `EntryMapped` /// * `EntryRemoved` /// /// A new 'zero-changes starting-state' can be created by calling `Snapshot()`. `Snapshot` creates the first /// snapshot (effectively clears the `Changes` to zero), and `Changes` will collect the difference from this point /// to any morphed future-state as collection-transforming operations are performed /// /// Key type /// Value [CollectionBuilder(typeof(TrackingHashMap), nameof(TrackingHashMap.createRange))] public readonly struct TrackingHashMap : IReadOnlyDictionary, IEnumerable<(K Key, V Value)>, IEquatable>, Monoid> { public static TrackingHashMap Empty { get; } = new(TrieMap, K, V>.Empty); readonly TrieMap, K, V> value; readonly TrieMap, K, Change> changes; internal TrieMap, K, V> Value => value ?? TrieMap, K, V>.Empty; internal TrieMap, K, Change> ChangesInternal => changes ?? TrieMap, K, Change>.Empty; public HashMap> Changes => new (ChangesInternal); internal TrackingHashMap(TrieMap, K, V> value, TrieMap, K, Change> changes) { this.value = value; this.changes = changes; } public TrackingHashMap(IEnumerable<(K Key, V Value)> items) : this(items, true) { } public TrackingHashMap(IEnumerable<(K Key, V Value)> items, bool tryAdd) { value = new TrieMap, K, V>(items, tryAdd); changes = TrieMap, K, Change>.Empty; } public TrackingHashMap(ReadOnlySpan<(K Key, V Value)> items) : this(items, true) { } public TrackingHashMap(ReadOnlySpan<(K Key, V Value)> items, bool tryAdd) { value = new TrieMap, K, V>(items, tryAdd); changes = TrieMap, K, Change>.Empty; } /// /// Creates a 'zero change' snapshot. *The data does not change*! /// /// Useful for creating new starting points for capturing the difference between two snapshots of the /// `TrackingHashMap`. `Snapshot` creates the first snapshot (effectively clears the `Changes` to zero), and /// `Changes` will collect the difference from this point to any morphed future point as collection /// transforming operations are performed /// Map with changes zeroed [Pure] public TrackingHashMap Snapshot() => new (Value, TrieMap, K, Change>.Empty); /// /// Item at index lens /// [Pure] public static Lens, V> item(K key) => Lens, V>.New( Get: la => la[key], Set: a => la => la.AddOrUpdate(key, a) ); /// /// Item or none at index lens /// [Pure] public static Lens, Option> itemOrNone(K key) => Lens, Option>.New( Get: la => la.Find(key), Set: a => la => a.Match(Some: x => la.AddOrUpdate(key, x), None: () => la.Remove(key)) ); TrackingHashMap Wrap((TrieMap, K, V> Map, TrieMap, K, Change> Changes) pair) => new (pair.Map, ChangesInternal.Merge(pair.Changes)); TrackingHashMap Wrap(K key, (TrieMap, K, V> Map, Change Change) pair) => new (pair.Map, ChangesInternal.AddOrUpdate(key, Some: ex => ex.Combine(pair.Change), pair.Change)); /// /// 'this' accessor /// /// Key /// Optional value [Pure] public V this[K key] => Value[key]; /// /// Is the map empty /// [Pure] public bool IsEmpty { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => value?.IsEmpty ?? true; } /// /// Number of items in the map /// [Pure] public int Count { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => value?.Count ?? 0; } /// /// Alias of Count /// [Pure] public int Length { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => value?.Count ?? 0; } /// /// Atomically filter out items that return false when a predicate is applied /// /// Predicate /// New map with items filtered [Pure] public TrackingHashMap Filter(Func pred) => Wrap(Value.FilterWithLog(pred)); /// /// Atomically filter out items that return false when a predicate is applied /// /// Predicate /// New map with items filtered [Pure] public TrackingHashMap Filter(Func pred) => Wrap(Value.FilterWithLog(pred)); /// /// Atomically adds a new item to the map /// /// Null is not allowed for a Key or a Value /// Key /// Value /// Throws ArgumentException if the key already exists /// Throws ArgumentNullException the key or value are null /// New Map with the item added [Pure] public TrackingHashMap Add(K key, V value) => Wrap(key, Value.AddWithLog(key, value)); /// /// Atomically adds a new item to the map. /// If the key already exists, then the new item is ignored /// /// Null is not allowed for a Key or a Value /// Key /// Value /// Throws ArgumentNullException the key or value are null /// New Map with the item added [Pure] public TrackingHashMap TryAdd(K key, V value) => Wrap(key, Value.TryAddWithLog(key, value)); /// /// Atomically adds a new item to the map. /// If the key already exists, the new item replaces it. /// /// Null is not allowed for a Key or a Value /// Key /// Value /// Throws ArgumentNullException the key or value are null /// New Map with the item added [Pure] public TrackingHashMap AddOrUpdate(K key, V value) => Wrap(key, Value.AddOrUpdateWithLog(key, value)); /// /// Retrieve a value from the map by key, map it to a new value, /// put it back. If it doesn't exist, add a new one based on None result. /// /// Key to find /// Throws Exception if None returns null /// Throws Exception if Some returns null /// New map with the mapped value [Pure] public TrackingHashMap AddOrUpdate(K key, Func Some, Func None) => Wrap(key, Value.AddOrUpdateWithLog(key, Some, None)); /// /// Retrieve a value from the map by key, map it to a new value, /// put it back. If it doesn't exist, add a new one based on None result. /// /// Key to find /// Throws ArgumentNullException if None is null /// Throws Exception if Some returns null /// New map with the mapped value [Pure] public TrackingHashMap AddOrUpdate(K key, Func Some, V None) => Wrap(key, Value.AddOrUpdateWithLog(key, Some, None)); /// /// Atomically adds a range of items to the map. /// /// Null is not allowed for a Key or a Value /// Range of tuples to add /// Throws ArgumentException if any of the keys already exist /// Throws ArgumentNullException the keys or values are null /// New Map with the items added [Pure] public TrackingHashMap AddRange(IEnumerable> range) => Wrap(Value.AddRangeWithLog(range)); /// /// Atomically adds a range of items to the map. /// /// Null is not allowed for a Key or a Value /// Range of tuples to add /// Throws ArgumentException if any of the keys already exist /// Throws ArgumentNullException the keys or values are null /// New Map with the items added [Pure] public TrackingHashMap AddRange(IEnumerable<(K Key, V Value)> range) => Wrap(Value.AddRangeWithLog(range)); /// /// Atomically adds a range of items to the map. If any of the keys exist already /// then they're ignored. /// /// Null is not allowed for a Key or a Value /// Range of tuples to add /// Throws ArgumentNullException the keys or values are null /// New Map with the items added [Pure] public TrackingHashMap TryAddRange(IEnumerable> range) => Wrap(Value.TryAddRangeWithLog(range)); /// /// Atomically adds a range of items to the map. If any of the keys exist already /// then they're ignored. /// /// Null is not allowed for a Key or a Value /// Range of tuples to add /// Throws ArgumentNullException the keys or values are null /// New Map with the items added [Pure] public TrackingHashMap TryAddRange(IEnumerable<(K Key, V Value)> range) => Wrap(Value.TryAddRangeWithLog(range)); /// /// Atomically adds a range of items to the map. If any of the keys exist already /// then they're ignored. /// /// Null is not allowed for a Key or a Value /// Range of KeyValuePairs to add /// Throws ArgumentNullException the keys or values are null /// New Map with the items added [Pure] public TrackingHashMap TryAddRange(IEnumerable> range) => Wrap(Value.TryAddRangeWithLog(range)); /// /// Atomically adds a range of items to the map. If any of the keys exist already /// then they're replaced. /// /// Null is not allowed for a Key or a Value /// Range of tuples to add /// Throws ArgumentNullException the keys or values are null /// New Map with the items added [Pure] public TrackingHashMap AddOrUpdateRange(IEnumerable> range) => Wrap(Value.AddOrUpdateRangeWithLog(range)); /// /// Atomically adds a range of items to the map. If any of the keys exist already /// then they're replaced. /// /// Null is not allowed for a Key or a Value /// Range of tuples to add /// Throws ArgumentNullException the keys or values are null /// New Map with the items added [Pure] public TrackingHashMap AddOrUpdateRange(IEnumerable<(K Key, V Value)> range) => Wrap(Value.AddOrUpdateRangeWithLog(range)); /// /// Atomically adds a range of items to the map. If any of the keys exist already /// then they're replaced. /// /// Null is not allowed for a Key or a Value /// Range of KeyValuePairs to add /// Throws ArgumentNullException the keys or values are null /// New Map with the items added [Pure] public TrackingHashMap AddOrUpdateRange(IEnumerable> range) => Wrap(Value.AddOrUpdateRangeWithLog(range)); /// /// Atomically removes an item from the map /// If the key doesn't exists, the request is ignored. /// /// Key /// New map with the item removed [Pure] public TrackingHashMap Remove(K key) => Wrap(key, Value.RemoveWithLog(key)); /// /// Retrieve a value from the map by key /// /// Key to find /// Found value [Pure] public Option Find(K key) => Value.Find(key); /// /// Retrieve a value from the map by key as an enumerable /// /// Key to find /// Found value [Pure] public Seq FindSeq(K key) => Value.FindAll(key); /// /// Retrieve a value from the map by key and pattern match the /// result. /// /// Key to find /// Found value [Pure] public R Find(K key, Func Some, Func None) => Value.Find(key, Some, None); /// /// Try to find the key in the map, if it doesn't exist, add a new /// item by invoking the delegate provided. /// /// Key to find /// Delegate to get the value /// Updated map and added value [Pure] public (TrackingHashMap Map, V Value) FindOrAdd(K key, Func None) { var (x, y, cs) = Value.FindOrAddWithLog(key, None); return (Wrap(key, (x, cs)), y); } /// /// Try to find the key in the map, if it doesn't exist, add a new /// item provided. /// /// Key to find /// Delegate to get the value /// Updated map and added value [Pure] public (TrackingHashMap, V Value) FindOrAdd(K key, V value) { var (x, y, cs) = Value.FindOrAddWithLog(key, value); return (Wrap(key, (x, cs)), y); } /// /// Try to find the key in the map, if it doesn't exist, add a new /// item by invoking the delegate provided. /// /// Key to find /// Delegate to get the value /// Updated map and added value [Pure] public (TrackingHashMap Map, Option Value) FindOrMaybeAdd(K key, Func> None) { var (x, y, cs) = Value.FindOrMaybeAddWithLog(key, None); return (Wrap(key, (x, cs)), y); } /// /// Try to find the key in the map, if it doesn't exist, add a new /// item by invoking the delegate provided. /// /// Key to find /// Delegate to get the value /// Updated map and added value [Pure] public (TrackingHashMap Map, Option Value) FindOrMaybeAdd(K key, Option None) { var (x, y, cs) = Value.FindOrMaybeAddWithLog(key, None); return (Wrap(key, (x, cs)), y); } /// /// Atomically updates an existing item /// /// Null is not allowed for a Key or a Value /// Key /// Value /// Throws ArgumentNullException the key or value are null /// New Map with the item added [Pure] public TrackingHashMap SetItem(K key, V value) => Wrap(key, Value.SetItemWithLog(key, value)); /// /// Retrieve a value from the map by key, map it to a new value, /// put it back. /// /// Key to set /// Throws ArgumentException if the item isn't found /// Throws Exception if Some returns null /// New map with the mapped value [Pure] public TrackingHashMap SetItem(K key, Func Some) => Wrap(key, Value.SetItemWithLog(key, Some)); /// /// Atomically updates an existing item, unless it doesn't exist, in which case /// it is ignored /// /// Null is not allowed for a Key or a Value /// Key /// Value /// Throws ArgumentNullException the value is null /// New Map with the item added [Pure] public TrackingHashMap TrySetItem(K key, V value) => Wrap(key, Value.TrySetItemWithLog(key, value)); /// /// Atomically sets an item by first retrieving it, applying a map, and then putting it back. /// Silently fails if the value doesn't exist /// /// Key to set /// delegate to map the existing value to a new one before setting /// Throws Exception if Some returns null /// Throws ArgumentNullException the key or value are null /// New map with the item set [Pure] public TrackingHashMap TrySetItem(K key, Func Some) => Wrap(key, Value.TrySetItemWithLog(key, Some)); /// /// Checks for existence of a key in the map /// /// Key to check /// True if an item with the key supplied is in the map [Pure] public bool ContainsKey(K key) => Value.ContainsKey(key); /// /// Checks for existence of a key in the map /// /// Key to check /// True if an item with the key supplied is in the map [Pure] public bool Contains(K key, V value) => Value.Contains(key, value); /// /// Checks for existence of a value in the map /// /// Value to check /// True if an item with the value supplied is in the map [Pure] public bool Contains(V value) => Value.Contains(value); /// /// Checks for existence of a value in the map /// /// Value to check /// True if an item with the value supplied is in the map [Pure] public bool Contains(V value) where EqV : Eq => Value.Contains(value); /// /// Checks for existence of a key in the map /// /// Key to check /// True if an item with the key supplied is in the map [Pure] public bool Contains(K key, V value) where EqV : Eq => Value.Contains(key, value); /// /// Clears all items from the map /// /// Functionally equivalent to calling Map.empty as the original structure is untouched /// Empty map [Pure] public TrackingHashMap Clear() => Wrap(Value.ClearWithLog()); /// /// Atomically adds a range of items to the map /// /// Range of KeyValuePairs to add /// Throws ArgumentException if any of the keys already exist /// New Map with the items added [Pure] public TrackingHashMap AddRange(IEnumerable> pairs) => Wrap(Value.AddRangeWithLog(pairs)); /// /// Atomically sets a series of items using the KeyValuePairs provided /// /// Items to set /// Throws ArgumentException if any of the keys aren't in the map /// New map with the items set [Pure] public TrackingHashMap SetItems(IEnumerable> items) => Wrap(Value.SetItemsWithLog(items)); /// /// Atomically sets a series of items using the Tuples provided. /// /// Items to set /// Throws ArgumentException if any of the keys aren't in the map /// New map with the items set [Pure] public TrackingHashMap SetItems(IEnumerable> items) => Wrap(Value.SetItemsWithLog(items)); /// /// Atomically sets a series of items using the Tuples provided. /// /// Items to set /// Throws ArgumentException if any of the keys aren't in the map /// New map with the items set [Pure] public TrackingHashMap SetItems(IEnumerable<(K Key, V Value)> items) => Wrap(Value.SetItemsWithLog(items)); /// /// Atomically sets a series of items using the KeyValuePairs provided. If any of the /// items don't exist then they're silently ignored. /// /// Items to set /// New map with the items set [Pure] public TrackingHashMap TrySetItems(IEnumerable> items) => Wrap(Value.TrySetItemsWithLog(items)); /// /// Atomically sets a series of items using the Tuples provided If any of the /// items don't exist then they're silently ignored. /// /// Items to set /// New map with the items set [Pure] public TrackingHashMap TrySetItems(IEnumerable> items) => Wrap(Value.TrySetItemsWithLog(items)); /// /// Atomically sets a series of items using the Tuples provided If any of the /// items don't exist then they're silently ignored. /// /// Items to set /// New map with the items set [Pure] public TrackingHashMap TrySetItems(IEnumerable<(K Key, V Value)> items) => Wrap(Value.TrySetItemsWithLog(items)); /// /// Atomically sets a series of items using the keys provided to find the items /// and the Some delegate maps to a new value. If the items don't exist then /// they're silently ignored. /// /// Keys of items to set /// Function map the existing item to a new one /// New map with the items set [Pure] public TrackingHashMap TrySetItems(IEnumerable keys, Func Some) => Wrap(Value.TrySetItemsWithLog(keys, Some)); /// /// Atomically removes a set of keys from the map /// /// Keys to remove /// New map with the items removed [Pure] public TrackingHashMap RemoveRange(IEnumerable keys) => Wrap(Value.RemoveRangeWithLog(keys)); /// /// Returns true if a Key/Value pair exists in the map /// /// Pair to find /// True if exists, false otherwise [Pure] public bool Contains(KeyValuePair pair) => Value.Contains(pair.Key, pair.Value); /// /// Enumerable of map keys /// [Pure] public Iterable Keys => Value.Keys; /// /// Enumerable of map values /// [Pure] public Iterable Values => Value.Values; /// /// Convert the map to an IDictionary /// /// [Pure] public IReadOnlyDictionary ToDictionary() => this; /// /// Map the map the a dictionary /// [Pure] public IDictionary ToDictionary( Func<(K Key, V Value), KR> keySelector, Func<(K Key, V Value), VR> valueSelector) where KR : notnull => AsEnumerable().ToDictionary(x => keySelector(x), x => valueSelector(x)); /// /// GetEnumerator - IEnumerable interface /// public IEnumerator<(K Key, V Value)> GetEnumerator() => Value.GetEnumerator(); /// /// GetEnumerator - IEnumerable interface /// IEnumerator IEnumerable.GetEnumerator() => Value.GetEnumerator(); [Pure] public Seq<(K Key, V Value)> ToSeq() => toSeq(AsEnumerable()); /// /// Allocation free conversion to a HashMap /// [Pure] public HashMap ToHashMap() => new (value); /// /// Format the collection as `[(key: value), (key: value), (key: value), ...]` /// The elipsis is used for collections over 50 items /// To get a formatted string with all the items, use `ToFullString` /// or `ToFullArrayString`. /// [Pure] public override string ToString() => CollectionFormat.ToShortArrayString(AsEnumerable().Map(kv => $"({kv.Key}: {kv.Value})"), Count); /// /// Format the collection as `(key: value), (key: value), (key: value), ...` /// [Pure] public string ToFullString(string separator = ", ") => CollectionFormat.ToFullString(AsEnumerable().Map(kv => $"({kv.Key}: {kv.Value})"), separator); /// /// Format the collection as `[(key: value), (key: value), (key: value), ...]` /// [Pure] public string ToFullArrayString(string separator = ", ") => CollectionFormat.ToFullArrayString(AsEnumerable().Map(kv => $"({kv.Key}: {kv.Value})"), separator); [Pure] public Iterable<(K Key, V Value)> AsEnumerable() => Value.AsIterable(); /// /// Implicit conversion from an untyped empty list /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator TrackingHashMap(SeqEmpty _) => Empty; /// /// Equality of keys and values with `EqDefault〈V〉` used for values /// [Pure] public static bool operator ==(TrackingHashMap lhs, TrackingHashMap rhs) => lhs.Equals(rhs); /// /// In-equality of keys and values with `EqDefault〈V〉` used for values /// [Pure] public static bool operator !=(TrackingHashMap lhs, TrackingHashMap rhs) => !(lhs == rhs); [Pure] public static TrackingHashMap operator +(TrackingHashMap lhs, TrackingHashMap rhs) => lhs.Combine(rhs); [Pure] public TrackingHashMap Combine(TrackingHashMap rhs) => Wrap(Value.AppendWithLog(rhs.Value)); [Pure] public static TrackingHashMap operator -(TrackingHashMap lhs, TrackingHashMap rhs) => lhs.Subtract(rhs); [Pure] public TrackingHashMap Subtract(TrackingHashMap rhs) => Wrap(Value.SubtractWithLog(rhs.Value)); /// /// Returns True if 'other' is a proper subset of this set /// /// True if 'other' is a proper subset of this set [Pure] public bool IsProperSubsetOf(IEnumerable<(K Key, V Value)> other) => Value.IsProperSubsetOf(other); /// /// Returns True if 'other' is a proper subset of this set /// /// True if 'other' is a proper subset of this set [Pure] public bool IsProperSubsetOf(IEnumerable other) => Value.IsProperSubsetOf(other); /// /// Returns True if 'other' is a proper superset of this set /// /// True if 'other' is a proper superset of this set [Pure] public bool IsProperSupersetOf(IEnumerable<(K Key, V Value)> other) => Value.IsProperSupersetOf(other); /// /// Returns True if 'other' is a proper superset of this set /// /// True if 'other' is a proper superset of this set [Pure] public bool IsProperSupersetOf(IEnumerable other) => Value.IsProperSupersetOf(other); /// /// Returns True if 'other' is a superset of this set /// /// True if 'other' is a superset of this set [Pure] public bool IsSubsetOf(IEnumerable<(K Key, V Value)> other) => Value.IsSubsetOf(other); /// /// Returns True if 'other' is a superset of this set /// /// True if 'other' is a superset of this set [Pure] public bool IsSubsetOf(IEnumerable other) => Value.IsSubsetOf(other); /// /// Returns True if 'other' is a superset of this set /// /// True if 'other' is a superset of this set [Pure] public bool IsSubsetOf(TrackingHashMap other) => Value.IsSubsetOf(other.Value); /// /// Returns True if 'other' is a superset of this set /// /// True if 'other' is a superset of this set [Pure] public bool IsSupersetOf(IEnumerable<(K Key, V Value)> other) => Value.IsSupersetOf(other); /// /// Returns True if 'other' is a superset of this set /// /// True if 'other' is a superset of this set [Pure] public bool IsSupersetOf(IEnumerable rhs) => Value.IsSupersetOf(rhs); /// /// Returns the elements that are in both this and other /// [Pure] public TrackingHashMap Intersect(IEnumerable rhs) => Wrap(Value.IntersectWithLog(rhs)); /// /// Returns the elements that are in both this and other /// [Pure] public TrackingHashMap Intersect(IEnumerable<(K Key, V Value)> rhs) => Wrap(Value.IntersectWithLog(rhs)); /// /// Returns the elements that are in both this and other /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public TrackingHashMap Intersect(IEnumerable<(K Key, V Value)> rhs, WhenMatched Merge) => Wrap(Value.IntersectWithLog(new TrieMap, K, V>(rhs), Merge)); /// /// Returns the elements that are in both this and other /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public TrackingHashMap Intersect(HashMap rhs, WhenMatched Merge) => Wrap(Value.IntersectWithLog(rhs.Value, Merge)); /// /// Returns the elements that are in both this and other /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public TrackingHashMap Intersect(TrackingHashMap rhs, WhenMatched Merge) => Wrap(Value.IntersectWithLog(rhs.Value, Merge)); /// /// Returns True if other overlaps this set /// [Pure] public bool Overlaps(IEnumerable<(K Key, V Value)> other) => Value.Overlaps(other); /// /// Returns True if other overlaps this set /// [Pure] public bool Overlaps(IEnumerable other) => Value.Overlaps(other); /// /// Returns this - other. Only the items in this that are not in /// other will be returned. /// [Pure] public TrackingHashMap Except(IEnumerable rhs) => Wrap(Value.ExceptWithLog(rhs)); /// /// Returns this - other. Only the items in this that are not in /// other will be returned. /// [Pure] public TrackingHashMap Except(IEnumerable<(K Key, V Value)> rhs) => Wrap(Value.ExceptWithLog(rhs)); /// /// Only items that are in one set or the other will be returned. /// If an item is in both, it is dropped. /// [Pure] public TrackingHashMap SymmetricExcept(TrackingHashMap rhs) => Wrap(Value.SymmetricExceptWithLog(rhs.Value)); /// /// Only items that are in one set or the other will be returned. /// If an item is in both, it is dropped. /// [Pure] public TrackingHashMap SymmetricExcept(IEnumerable<(K Key, V Value)> rhs) => Wrap(Value.SymmetricExceptWithLog(rhs)); /// /// Finds the union of two sets and produces a new set with /// the results /// /// Other set to union with /// A set which contains all items from both sets [Pure] public TrackingHashMap Union(IEnumerable<(K, V)> rhs) => this.TryAddRange(rhs); /// /// Union two maps. /// /// /// The `WhenMatched` merge function is called when keys are present in both map to allow resolving to a /// sensible value. /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public TrackingHashMap Union(IEnumerable<(K Key, V Value)> other, WhenMatched Merge) => Wrap(Value.UnionWithLog(other, static (_, v) => v, static (_, v) => v, Merge)); /// /// Union two maps. /// /// /// The `WhenMatched` merge function is called when keys are present in both map to allow resolving to a /// sensible value. /// /// /// The `WhenMissing` function is called when there is a key in the right-hand side, but not the left-hand-side. /// This allows the `V2` value-type to be mapped to the target `V` value-type. /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public TrackingHashMap Union(IEnumerable<(K Key, W Value)> other, WhenMissing MapRight, WhenMatched Merge) => Wrap(Value.UnionWithLog(other, static (_, v) => v, MapRight, Merge)); /// /// Equality of keys and values with `EqDefault〈V〉` used for values /// [Pure] public override bool Equals(object? obj) => obj is TrackingHashMap hm && Equals(hm); /// /// Equality of keys and values with `EqDefault〈V〉` used for values /// [Pure] public bool Equals(TrackingHashMap other) => Value.Equals>(other.Value); /// /// Equality of keys and values with `EqV` used for values /// [Pure] public bool Equals(TrackingHashMap other) where EqV : Eq => Value.Equals(other.Value); /// /// Equality of keys only /// [Pure] public bool EqualsKeys(TrackingHashMap other) => Value.Equals>(other.Value); [Pure] public override int GetHashCode() => Value.GetHashCode(); /// /// Impure iteration of the bound values in the structure /// /// /// Returns the original unmodified structure /// public TrackingHashMap Do(Action f) { this.Iter(f); return this; } /// /// Atomically filter out items that return false when a predicate is applied /// /// Predicate /// New map with items filtered [Pure] [EditorBrowsable(EditorBrowsableState.Never)] public TrackingHashMap Where(Func pred) => Filter(pred); /// /// Atomically filter out items that return false when a predicate is applied /// /// Predicate /// New map with items filtered [Pure] [EditorBrowsable(EditorBrowsableState.Never)] public TrackingHashMap Where(Func pred) => Filter(pred); /// /// Return true if all items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] public bool ForAll(Func pred) { foreach (var item in AsEnumerable()) { if (!pred(item.Key, item.Value)) return false; } return true; } /// /// Return true if all items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] public bool ForAll(Func<(K Key, V Value), bool> pred) => AsEnumerable().Map(kv => (kv.Key, kv.Value)).ForAll(pred); /// /// Return true if *all* items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] public bool ForAll(Func, bool> pred) => AsEnumerable().Map(kv => new KeyValuePair(kv.Key, kv.Value)).ForAll(pred); /// /// Return true if all items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] public bool ForAll(Func pred) => Values.ForAll(pred); /// /// Return true if *any* items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied public bool Exists(Func pred) { foreach (var item in AsEnumerable()) { if (pred(item.Key, item.Value)) return true; } return false; } /// /// Return true if *any* items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] public bool Exists(Func<(K Key, V Value), bool> pred) => AsEnumerable().Map(kv => ( kv.Key, kv.Value)).Exists(pred); /// /// Return true if *any* items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] public bool Exists(Func, bool> pred) => AsEnumerable().Map(kv => new KeyValuePair(kv.Key, kv.Value)).Exists(pred); /// /// Return true if *any* items in the map return true when the predicate is applied /// /// Predicate /// True if all items in the map return true when the predicate is applied [Pure] public bool Exists(Func pred) => Values.Exists(pred); /// /// Atomically iterate through all key/value pairs in the map (in order) and execute an /// action on each /// /// Action to execute /// Unit public Unit Iter(Action action) { foreach (var item in this) { action(item.Key, item.Value); } return unit; } /// /// Atomically iterate through all values in the map (in order) and execute an /// action on each /// /// Action to execute /// Unit public Unit Iter(Action action) { foreach (var item in this) { action(item.Value); } return unit; } /// /// Atomically iterate through all key/value pairs (as tuples) in the map (in order) /// and execute an action on each /// /// Action to execute /// Unit public Unit Iter(Action> action) { foreach (var item in this) { action(new Tuple(item.Key, item.Value)); } return unit; } /// /// Atomically iterate through all key/value pairs (as tuples) in the map (in order) /// and execute an action on each /// /// Action to execute /// Unit public Unit Iter(Action<(K Key, V Value)> action) { foreach (var item in this) { action(item); } return unit; } /// /// Atomically iterate through all key/value pairs in the map (in order) and execute an /// action on each /// /// Action to execute /// Unit public Unit Iter(Action> action) { foreach (var item in this) { action(new KeyValuePair(item.Key, item.Value)); } return unit; } /// /// Atomically folds all items in the map (in order) using the folder function provided. /// /// State type /// Initial state /// Fold function /// Folded state [Pure] public S Fold(S state, Func folder) => AsEnumerable().Fold(state, (s, x) => folder(s, x.Key, x.Value)); /// /// Atomically folds all items in the map (in order) using the folder function provided. /// /// State type /// Initial state /// Fold function /// Folded state [Pure] public S Fold(S state, Func folder) => Values.Fold(state, folder); [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] IEnumerator> IEnumerable>.GetEnumerator() => AsEnumerable().Map(p => new KeyValuePair(p.Key, p.Value)).GetEnumerator(); [Pure] IEnumerable IReadOnlyDictionary.Keys => Keys; [Pure] IEnumerable IReadOnlyDictionary.Values => Values; [Pure] public bool TryGetValue(K key, out V value) { var v = Find(key); if (v.IsSome) { value = (V)v; return true; } else { value = default!; return false; } } /// /// Get a IReadOnlyDictionary for this map. No mapping is required, so this is very fast. /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public IReadOnlyDictionary ToReadOnlyDictionary() => this; [Pure] [Obsolete(Change.UseCollectionIntialiser)] public static implicit operator TrackingHashMap(ValueTuple<(K, V)> items) => [items.Item1]; [Pure] [Obsolete(Change.UseCollectionIntialiser)] public static implicit operator TrackingHashMap(((K, V), (K, V)) items) => [items.Item1, items.Item2]; [Pure] [Obsolete(Change.UseCollectionIntialiser)] public static implicit operator TrackingHashMap(((K, V), (K, V), (K, V)) items) => [items.Item1, items.Item2, items.Item3]; [Pure] [Obsolete(Change.UseCollectionIntialiser)] public static implicit operator TrackingHashMap(((K, V), (K, V), (K, V), (K, V)) items) => [items.Item1, items.Item2, items.Item3, items.Item4]; [Pure] [Obsolete(Change.UseCollectionIntialiser)] public static implicit operator TrackingHashMap(((K, V), (K, V), (K, V), (K, V), (K, V)) items) => [items.Item1, items.Item2, items.Item3, items.Item4, items.Item5]; [Pure] [Obsolete(Change.UseCollectionIntialiser)] public static implicit operator TrackingHashMap(((K, V), (K, V), (K, V), (K, V), (K, V), (K, V)) items) => [items.Item1, items.Item2, items.Item3, items.Item4, items.Item5, items.Item6]; [Pure] [Obsolete(Change.UseCollectionIntialiser)] public static implicit operator TrackingHashMap(((K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V)) items) => [items.Item1, items.Item2, items.Item3, items.Item4, items.Item5, items.Item6, items.Item7]; [Pure] [Obsolete(Change.UseCollectionIntialiser)] public static implicit operator TrackingHashMap(((K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V)) items) => [items.Item1, items.Item2, items.Item3, items.Item4, items.Item5, items.Item6, items.Item7, items.Item8]; [Pure] [Obsolete(Change.UseCollectionIntialiser)] public static implicit operator TrackingHashMap(((K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V)) items) => [items.Item1, items.Item2, items.Item3, items.Item4, items.Item5, items.Item6, items.Item7, items.Item8, items.Item9]; [Pure] [Obsolete(Change.UseCollectionIntialiser)] public static implicit operator TrackingHashMap(((K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V)) items) => [items.Item1, items.Item2, items.Item3, items.Item4, items.Item5, items.Item6, items.Item7, items.Item8, items.Item9, items.Item10]; [Pure] [Obsolete(Change.UseCollectionIntialiser)] public static implicit operator TrackingHashMap(((K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V)) items) => [items.Item1, items.Item2, items.Item3, items.Item4, items.Item5, items.Item6, items.Item7, items.Item8, items.Item9, items.Item10, items.Item11]; [Pure] [Obsolete(Change.UseCollectionIntialiser)] public static implicit operator TrackingHashMap(((K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V)) items) => [items.Item1, items.Item2, items.Item3, items.Item4, items.Item5, items.Item6, items.Item7, items.Item8, items.Item9, items.Item10, items.Item11, items.Item12]; [Pure] [Obsolete(Change.UseCollectionIntialiser)] public static implicit operator TrackingHashMap(((K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V)) items) => [items.Item1, items.Item2, items.Item3, items.Item4, items.Item5, items.Item6, items.Item7, items.Item8, items.Item9, items.Item10, items.Item11, items.Item12, items.Item13]; [Pure] [Obsolete(Change.UseCollectionIntialiser)] public static implicit operator TrackingHashMap(((K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V)) items) => [items.Item1, items.Item2, items.Item3, items.Item4, items.Item5, items.Item6, items.Item7, items.Item8, items.Item9, items.Item10, items.Item11, items.Item12, items.Item13, items.Item14]; [Pure] [Obsolete(Change.UseCollectionIntialiser)] public static implicit operator TrackingHashMap(((K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V)) items) => [items.Item1, items.Item2, items.Item3, items.Item4, items.Item5, items.Item6, items.Item7, items.Item8, items.Item9, items.Item10, items.Item11, items.Item12, items.Item13, items.Item14, items.Item15]; [Pure] [Obsolete(Change.UseCollectionIntialiser)] public static implicit operator TrackingHashMap(((K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V), (K, V)) items) => [items.Item1, items.Item2, items.Item3, items.Item4, items.Item5, items.Item6, items.Item7, items.Item8, items.Item9, items.Item10, items.Item11, items.Item12, items.Item13, items.Item14, items.Item15, items.Item16]; } ================================================ FILE: LanguageExt.Core/Immutable Collections/TrieMap/TrieMap.cs ================================================ using LanguageExt.Traits; using static LanguageExt.Prelude; using System; using System.Runtime.CompilerServices; using System.Linq; using System.Collections.Generic; using System.Collections; using LanguageExt.ClassInstances; using Array = System.Array; namespace LanguageExt; /// /// Implementation of the CHAMP trie hash map data structure (Compressed Hash Array Map Trie) /// [efficient-immutable-collections.pdf](https://michael.steindorfer.name/publications/phd-thesis-efficient-immutable-collections.pdf) /// /// /// Used by internally by `LanguageExt.HashMap` /// internal class TrieMap : IEnumerable<(K Key, V Value)>, IEquatable> where EqK : Eq { internal enum UpdateType { Add, TryAdd, AddOrUpdate, SetItem, TrySetItem } internal enum Tag { Entries, Collision, Empty } public static readonly TrieMap Empty = new (EmptyNode.Default, 0); internal static TrieMap EmptyForMutating => new (new EmptyNode(), 0); readonly Node Root; readonly int count; int hash; /// /// Ctor /// [MethodImpl(MethodImplOptions.AggressiveInlining)] TrieMap(Node root, int count) { Root = root; this.count = count; } public TrieMap(IEnumerable<(K Key, V Value)> items, bool tryAdd = true) { Root = EmptyNode.Default; var type = tryAdd ? UpdateType.TryAdd : UpdateType.AddOrUpdate; foreach (var item in items) { var h = (uint)EqK.GetHashCode(item.Key); Sec section = default; var (countDelta, newRoot, _, _) = Root.Update((type, true), item, h, section); count += countDelta; Root = newRoot; } } public TrieMap(ReadOnlySpan<(K Key, V Value)> items, bool tryAdd = true) { Root = EmptyNode.Default; var type = tryAdd ? UpdateType.TryAdd : UpdateType.AddOrUpdate; foreach (var item in items) { var h = (uint)EqK.GetHashCode(item.Key); Sec section = default; var (countDelta, newRoot, _, _) = Root.Update((type, true), item, h, section); count += countDelta; Root = newRoot; } } /// /// True if no items in the map /// public bool IsEmpty { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => count == 0; } /// /// Number of items in the map /// public int Count { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => count; } /// /// Add an item to the map /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public TrieMap Add(K key, V value) => Update(key, value, UpdateType.Add, false); /// /// Add an item to the map /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public (TrieMap Map, Change Change) AddWithLog(K key, V value) => UpdateWithLog(key, value, UpdateType.Add, false); /// /// Try to add an item to the map. If it already exists, do /// nothing. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public TrieMap TryAdd(K key, V value) => Update(key, value, UpdateType.TryAdd, false); /// /// Try to add an item to the map. If it already exists, do /// nothing. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public (TrieMap Map, Change Change) TryAddWithLog(K key, V value) => UpdateWithLog(key, value, UpdateType.TryAdd, false); /// /// Add an item to the map, if it exists update the value /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public TrieMap AddOrUpdate(K key, V value) => Update(key, value, UpdateType.AddOrUpdate, false); /// /// Add an item to the map, if it exists update the value /// [MethodImpl(MethodImplOptions.AggressiveInlining)] internal TrieMap AddOrUpdateInPlace(K key, V value) => Update(key, value, UpdateType.AddOrUpdate, true); /// /// Add an item to the map, if it exists update the value /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public (TrieMap Map, Change Change) AddOrUpdateWithLog(K key, V value) => UpdateWithLog(key, value, UpdateType.AddOrUpdate, false); /// /// Add an item to the map, if it exists update the value /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public TrieMap AddOrUpdate(K key, Func Some, Func None) { var (found, _, value) = FindInternal(key); return found ? AddOrUpdate(key, Some(value!)) : AddOrUpdate(key, None()); } /// /// Add an item to the map, if it exists update the value /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public (TrieMap Map, Change Change) AddOrUpdateWithLog(K key, Func Some, Func None) { var (found, _, value) = FindInternal(key); return found ? AddOrUpdateWithLog(key, Some(value!)) : AddOrUpdateWithLog(key, None()); } /// /// Add an item to the map, if it exists update the value /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public TrieMap AddOrMaybeUpdate(K key, Func Some, Func> None) { var (found, _, value) = FindInternal(key); return found ? AddOrUpdate(key, Some(value!)) : None().Map(x => AddOrUpdate(key, x)).IfNone(this); } /// /// Add an item to the map, if it exists update the value /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public (TrieMap Map, Change Change) AddOrMaybeUpdateWithLog(K key, Func Some, Func> None) { var (found, _, value) = FindInternal(key); return found ? AddOrUpdateWithLog(key, Some(value!)) : None().Map(x => AddOrUpdateWithLog(key, x)).IfNone((this, Change.None)); } /// /// Add an item to the map, if it exists update the value /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public TrieMap AddOrUpdate(K key, Func Some, V None) { var (found, _, value) = FindInternal(key); return found ? AddOrUpdate(key, Some(value!)) : AddOrUpdate(key, None); } /// /// Add an item to the map, if it exists update the value /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public (TrieMap Map, Change Change) AddOrUpdateWithLog(K key, Func Some, V None) { var (found, _, value) = FindInternal(key); return found ? AddOrUpdateWithLog(key, Some(value!)) : AddOrUpdateWithLog(key, None); } /// /// Add a range of values to the map /// If any items already exist an exception will be thrown /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public TrieMap AddRange(IEnumerable<(K Key, V Value)> items) { var self = this; foreach (var item in items) { self = self.Add(item.Key, item.Value); } return self; } /// /// Add a range of values to the map /// If any items already exist an exception will be thrown /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public (TrieMap Map, TrieMap> Changes) AddRangeWithLog(IEnumerable<(K Key, V Value)> items) { var self = this; var changes = TrieMap>.EmptyForMutating; foreach (var item in items) { var pair = self.AddWithLog(item.Key, item.Value); self = pair.Map; if (pair.Change.HasChanged) { changes = changes.AddOrUpdateInPlace(item.Key, pair.Change); } } return (self, changes); } /// /// Add a range of values to the map /// If any items already exist an exception will be thrown /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public TrieMap AddRange(IEnumerable> items) { var self = this; foreach (var item in items) { self = self.Add(item.Item1, item.Item2); } return self; } /// /// Add a range of values to the map /// If any items already exist an exception will be thrown /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public (TrieMap Map, TrieMap> Changes) AddRangeWithLog(IEnumerable> items) { var self = this; var changes = TrieMap>.EmptyForMutating; foreach (var item in items) { var pair = self.AddWithLog(item.Item1, item.Item2); self = pair.Map; if (pair.Change.HasChanged) { changes = changes.AddOrUpdateInPlace(item.Item1, pair.Change); } } return (self, changes); } /// /// Add a range of values to the map /// If any items already exist an exception will be thrown /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public TrieMap AddRange(IEnumerable> items) { var self = this; foreach (var item in items) { self = self.Add(item.Key, item.Value); } return self; } /// /// Add a range of values to the map /// If any items already exist an exception will be thrown /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public (TrieMap Map, TrieMap> Changes) AddRangeWithLog(IEnumerable> items) { var self = this; var changes = TrieMap>.EmptyForMutating; foreach (var item in items) { var pair = self.AddWithLog(item.Key, item.Value); self = pair.Map; if (pair.Change.HasChanged) { changes = changes.AddOrUpdateInPlace(item.Key, pair.Change); } } return (self, changes); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public TrieMap TryAddRange(IEnumerable<(K Key, V Value)> items) { var self = this; foreach (var item in items) { self = self.TryAdd(item.Key, item.Value); } return self; } /// /// Add a range of values to the map /// If any items already exist an exception will be thrown /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public (TrieMap Map, TrieMap> Changes) TryAddRangeWithLog(IEnumerable<(K Key, V Value)> items) { var self = this; var changes = TrieMap>.EmptyForMutating; foreach (var item in items) { var pair = self.TryAddWithLog(item.Key, item.Value); self = pair.Map; if (pair.Change.HasChanged) { changes = changes.AddOrUpdateInPlace(item.Key, pair.Change); } } return (self, changes); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public TrieMap TryAddRange(IEnumerable> items) { var self = this; foreach (var item in items) { self = self.TryAdd(item.Item1, item.Item2); } return self; } /// /// Add a range of values to the map /// If any items already exist an exception will be thrown /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public (TrieMap Map, TrieMap> Changes) TryAddRangeWithLog(IEnumerable> items) { var self = this; var changes = TrieMap>.EmptyForMutating; foreach (var item in items) { var pair = self.TryAddWithLog(item.Item1, item.Item2); self = pair.Map; if (pair.Change.HasChanged) { changes = changes.AddOrUpdateInPlace(item.Item1, pair.Change); } } return (self, changes); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public TrieMap TryAddRange(IEnumerable> items) { var self = this; foreach (var item in items) { self = self.TryAdd(item.Key, item.Value); } return self; } /// /// Add a range of values to the map /// If any items already exist an exception will be thrown /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public (TrieMap Map, TrieMap> Changes) TryAddRangeWithLog(IEnumerable> items) { var self = this; var changes = TrieMap>.EmptyForMutating; foreach (var item in items) { var pair = self.TryAddWithLog(item.Key, item.Value); self = pair.Map; if (pair.Change.HasChanged) { changes = changes.AddOrUpdateInPlace(item.Key, pair.Change); } } return (self, changes); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public TrieMap AddOrUpdateRange(IEnumerable<(K Key, V Value)> items) { var self = this; foreach (var item in items) { self = self.AddOrUpdate(item.Key, item.Value); } return self; } /// /// Add a range of values to the map /// If any items already exist an exception will be thrown /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public (TrieMap Map, TrieMap> Changes) AddOrUpdateRangeWithLog(IEnumerable<(K Key, V Value)> items) { var self = this; var changes = TrieMap>.EmptyForMutating; foreach (var item in items) { var pair = self.AddOrUpdateWithLog(item.Key, item.Value); self = pair.Map; if (pair.Change.HasChanged) { changes = changes.AddOrUpdateInPlace(item.Key, pair.Change); } } return (self, changes); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public TrieMap AddOrUpdateRange(IEnumerable> items) { var self = this; foreach (var item in items) { self = self.AddOrUpdate(item.Item1, item.Item2); } return self; } /// /// Add a range of values to the map /// If any items already exist an exception will be thrown /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public (TrieMap Map, TrieMap> Changes) AddOrUpdateRangeWithLog(IEnumerable> items) { var self = this; var changes = TrieMap>.EmptyForMutating; foreach (var item in items) { var pair = self.AddOrUpdateWithLog(item.Item1, item.Item2); self = pair.Map; if (pair.Change.HasChanged) { changes = changes.AddOrUpdateInPlace(item.Item1, pair.Change); } } return (self, changes); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public TrieMap AddOrUpdateRange(IEnumerable> items) { var self = this; foreach (var item in items) { self = self.AddOrUpdate(item.Key, item.Value); } return self; } /// /// Add a range of values to the map /// If any items already exist an exception will be thrown /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public (TrieMap Map, TrieMap> Changes) AddOrUpdateRangeWithLog(IEnumerable> items) { var self = this; var changes = TrieMap>.EmptyForMutating; foreach (var item in items) { var pair = self.AddOrUpdateWithLog(item.Key, item.Value); self = pair.Map; if (pair.Change.HasChanged) { changes = changes.AddOrUpdateInPlace(item.Key, pair.Change); } } return (self, changes); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public TrieMap SetItems(IEnumerable<(K Key, V Value)> items) { var self = this; foreach (var item in items) { self = self.SetItem(item.Key, item.Value); } return self; } /// /// Add a range of values to the map /// If any items already exist an exception will be thrown /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public (TrieMap Map, TrieMap> Changes) SetItemsWithLog(IEnumerable<(K Key, V Value)> items) { var self = this; var changes = TrieMap>.EmptyForMutating; foreach (var item in items) { var pair = self.SetItemWithLog(item.Key, item.Value); self = pair.Map; if (pair.Change.HasChanged) { changes = changes.AddOrUpdateInPlace(item.Key, pair.Change); } } return (self, changes); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public TrieMap SetItems(IEnumerable> items) { var self = this; foreach (var item in items) { self = self.SetItem(item.Key, item.Value); } return self; } /// /// Add a range of values to the map /// If any items already exist an exception will be thrown /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public (TrieMap Map, TrieMap> Changes) SetItemsWithLog(IEnumerable> items) { var self = this; var changes = TrieMap>.EmptyForMutating; foreach (var item in items) { var pair = self.SetItemWithLog(item.Key, item.Value); self = pair.Map; if (pair.Change.HasChanged) { changes = changes.AddOrUpdateInPlace(item.Key, pair.Change); } } return (self, changes); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public TrieMap SetItems(IEnumerable> items) { var self = this; foreach (var item in items) { self = self.SetItem(item.Item1, item.Item2); } return self; } /// /// Add a range of values to the map /// If any items already exist an exception will be thrown /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public (TrieMap Map, TrieMap> Changes) SetItemsWithLog(IEnumerable> items) { var self = this; var changes = TrieMap>.EmptyForMutating; foreach (var item in items) { var pair = self.SetItemWithLog(item.Item1, item.Item2); self = pair.Map; if (pair.Change.HasChanged) { changes = changes.AddOrUpdateInPlace(item.Item1, pair.Change); } } return (self, changes); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public TrieMap TrySetItems(IEnumerable<(K Key, V Value)> items) { var self = this; foreach (var item in items) { self = self.TrySetItem(item.Key, item.Value); } return self; } /// /// Add a range of values to the map /// If any items already exist an exception will be thrown /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public (TrieMap Map, TrieMap> Changes) TrySetItemsWithLog(IEnumerable<(K Key, V Value)> items) { var self = this; var changes = TrieMap>.EmptyForMutating; foreach (var item in items) { var pair = self.TrySetItemWithLog(item.Key, item.Value); self = pair.Map; if (pair.Change.HasChanged) { changes = changes.AddOrUpdateInPlace(item.Key, pair.Change); } } return (self, changes); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public TrieMap TrySetItems(IEnumerable> items) { var self = this; foreach (var item in items) { self = self.TrySetItem(item.Key, item.Value); } return self; } /// /// Add a range of values to the map /// If any items already exist an exception will be thrown /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public (TrieMap Map, TrieMap> Changes) TrySetItemsWithLog(IEnumerable> items) { var self = this; var changes = TrieMap>.EmptyForMutating; foreach (var item in items) { var pair = self.TrySetItemWithLog(item.Key, item.Value); self = pair.Map; if (pair.Change.HasChanged) { changes = changes.AddOrUpdateInPlace(item.Key, pair.Change); } } return (self, changes); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public TrieMap TrySetItems(IEnumerable> items) { var self = this; foreach (var item in items) { self = self.TrySetItem(item.Item1, item.Item2); } return self; } /// /// Add a range of values to the map /// If any items already exist an exception will be thrown /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public (TrieMap Map, TrieMap> Changes) TrySetItemsWithLog(IEnumerable> items) { var self = this; var changes = TrieMap>.EmptyForMutating; foreach (var item in items) { var pair = self.TrySetItemWithLog(item.Item1, item.Item2); self = pair.Map; if (pair.Change.HasChanged) { changes = changes.AddOrUpdateInPlace(item.Item1, pair.Change); } } return (self, changes); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public TrieMap TrySetItems(IEnumerable items, Func Some) { var self = this; foreach (var item in items) { self = self.TrySetItem(item, Some); } return self; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public (TrieMap Map, TrieMap> Changes) TrySetItemsWithLog(IEnumerable items, Func Some) { var self = this; var changes = TrieMap>.EmptyForMutating; foreach (var item in items) { var pair = self.TrySetItemWithLog(item, Some); self = pair.Map; if (pair.Change.HasChanged) { changes = changes.AddOrUpdateInPlace(item, pair.Change); } } return (self, changes); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public TrieMap RemoveRange(IEnumerable items) { var self = this; foreach (var item in items) { self = self.Remove(item); } return self; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public (TrieMap, TrieMap> Changes) RemoveRangeWithLog(IEnumerable items) { var self = this; var changes = TrieMap>.EmptyForMutating; foreach (var item in items) { var pair = self.RemoveWithLog(item); self = pair.Map; if (pair.Change.HasChanged) { changes = changes.AddOrUpdateInPlace(item, pair.Change); } } return (self, changes); } /// /// Set an item that already exists in the map /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public TrieMap SetItem(K key, V value) => Update(key, value, UpdateType.SetItem, false); /// /// Set an item that already exists in the map /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public (TrieMap Map, Change Change) SetItemWithLog(K key, V value) => UpdateWithLog(key, value, UpdateType.SetItem, false); /// /// Set an item that already exists in the map /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public TrieMap SetItem(K key, Func Some) { var value = Find(key).Map(Some).IfNone(() => throw new ArgumentException($"Key doesn't exist in map: {key}")); return SetItem(key, value); } /// /// Set an item that already exists in the map /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public (TrieMap Map, Change Change) SetItemWithLog(K key, Func Some) { var value = Find(key).Map(Some).IfNone(() => throw new ArgumentException($"Key doesn't exist in map: {key}")); return SetItemWithLog(key, value); } /// /// Try to set an item that already exists in the map. If none /// exists, do nothing. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public TrieMap TrySetItem(K key, V value) => Update(key, value, UpdateType.TrySetItem, false); /// /// Try to set an item that already exists in the map. If none /// exists, do nothing. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public (TrieMap Map, Change Change) TrySetItemWithLog(K key, V value) => UpdateWithLog(key, value, UpdateType.TrySetItem, false); /// /// Set an item that already exists in the map /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public TrieMap TrySetItem(K key, Func Some) => Find(key) .Map(Some) .Match(Some: v => SetItem(key, v), None: () => this); /// /// Set an item that already exists in the map /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public (TrieMap Map, Change Change) TrySetItemWithLog(K key, Func Some) => Find(key) .Map(Some) .Match(Some: v => SetItemWithLog(key, v), None: () => (this, Change.None)); /// /// Remove an item from the map /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public TrieMap Remove(K key) { var h = (uint)EqK.GetHashCode(key); Sec section = default; var (countDelta, newRoot, _) = Root.Remove(key, h, section); return ReferenceEquals(newRoot, Root) ? this : new TrieMap(newRoot, count + countDelta); } /// /// Remove an item from the map /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public (TrieMap Map, Change Change) RemoveWithLog(K key) { var h = (uint)EqK.GetHashCode(key); Sec section = default; var (countDelta, newRoot, old) = Root.Remove(key, h, section); return ReferenceEquals(newRoot, Root) ? (this, Change.None) : (new TrieMap(newRoot, count + countDelta), countDelta == 0 ? Change.None : Change.Removed(old!)); } /// /// Indexer /// public V this[K key] { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { var (found, _, value) = FindInternal(key); return found ? value! : throw new ArgumentException($"Key doesn't exist in map: {key}"); } } /// /// Get a key value pair from a key /// public (K Key, V Value) Get(K key) { var (found, nkey, value) = FindInternal(key); return found ? (nkey, value!) : throw new ArgumentException($"Key doesn't exist in map: {key}"); } /// /// Get a key value pair from a key /// public Option<(K Key, V Value)> GetOption(K key) { var (found, nkey, value) = FindInternal(key); return found ? Some((nkey, value!)) : default; } /// /// Get a key value pair from a key /// public Option GetKeyOption(K key) { var (found, nkey, _) = FindInternal(key); return found ? Some(nkey) : default; } /// /// Create an empty map /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public TrieMap Clear() => Empty; /// /// Create an empty map /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public (TrieMap Map, TrieMap>) ClearWithLog() => (Empty, Map(Change.Removed)); /// /// Get the hash code of the items in the map /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public override int GetHashCode() => hash == 0 ? (hash = FNV32.Hash, K, V>, (K, V)>(AsIterable())) : hash; /// /// Returns the whether the `key` exists in the map /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool ContainsKey(K key) => FindInternal(key).Found; /// /// Returns the whether the `value` exists in the map /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Contains(V value) => Contains>(value); /// /// Returns the whether the `value` exists in the map /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Contains(V value) where EqV: Eq => Values.Exists(v => EqV.Equals(v, value)); /// /// Returns the whether the `key` exists in the map /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Contains(K key, V value) => Contains>(key, value); /// /// Returns the whether the `key` exists in the map /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Contains(K key, V Value) where EqV : Eq => Find(key).Map(v => EqV.Equals(v, Value)).IfNone(false); /// /// Returns the value associated with `key`. Or None, if no key exists /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public Option Find(K key) { var (found, _, value) = FindInternal(key); return found ? Some(value!) : default; } /// /// Returns the value associated with `key`. Or None, if no key exists /// [MethodImpl(MethodImplOptions.AggressiveInlining)] (bool Found, K Key, V? Value) FindInternal(K key) { var h = (uint)EqK.GetHashCode(key); Sec section = default; return Root.Read(key, h, section); } /// /// Returns the value associated with `key` then match the result /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public R Find(K key, Func Some, Func None) { var (found, _, value) = FindInternal(key); return found ? Some(value!) : None(); } /// /// Tries to find the value, if not adds it and returns the update map and/or value /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public (TrieMap Map, V Value) FindOrAdd(K key, Func None) => Find(key, Some: v => (this, v), None: () => { var v = None(); return (Add(key, v), v); }); /// /// Tries to find the value, if not adds it and returns the update map and/or value /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public (TrieMap Map, V Value, Change Change) FindOrAddWithLog(K key, Func None) { var item = Find(key); if (item.IsSome) { return (this, item.Value!, Change.None); } else { var v = None(); var self = AddWithLog(key, v); return (self.Map, v, self.Change); } } /// /// Tries to find the value, if not adds it and returns the update map and/or value /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public (TrieMap Map, V Value) FindOrAdd(K key, V value) => Find(key, Some: v => (this, v), None: () => (Add(key, value), value)); /// /// Tries to find the value, if not adds it and returns the update map and/or value /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public (TrieMap Map, V Value, Change Change) FindOrAddWithLog(K key, V value) { var item = Find(key); if (item.IsSome) { return (this, item.Value!, Change.None); } else { var self = AddWithLog(key, value); return (self.Map, value, self.Change); } } /// /// Tries to find the value, if not adds it and returns the update map and/or value /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public (TrieMap Map, Option Value) FindOrMaybeAdd(K key, Func> None) => Find(key, Some: v => (this, v), None: () => { var v = None(); return v.IsSome ? (Add(key, (V)v), v) : (this, v); }); /// /// Tries to find the value, if not adds it and returns the update map and/or value /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public (TrieMap Map, V Value, Change Change) FindOrMaybeAddWithLog(K key, Func> None) { var item = Find(key); if (item.IsSome) { return (this, item.Value!, Change.None); } else { var v = None(); if (v.IsSome) { var self = AddWithLog(key, v.Value!); return (self.Map, v.Value!, self.Change); } else { return (this, item.Value!, Change.None); } } } /// /// Tries to find the value, if not adds it and returns the update map and/or value /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public (TrieMap Map, Option Value) FindOrMaybeAdd(K key, Option value) => Find(key, Some: v => (this, v), None: () => value.IsSome ? (Add(key, (V)value), value) : (this, value)); /// /// Tries to find the value, if not adds it and returns the update map and/or value /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public (TrieMap Map, Option Value, Change Change) FindOrMaybeAddWithLog(K key, Option value) { var item = Find(key); if (item.IsSome) { return (this, item.Value, Change.None); } else { if (value.IsSome) { var self = AddWithLog(key, value.Value!); return (self.Map, value.Value, self.Change); } else { return (this, item.Value, Change.None); } } } /// /// Returns the value associated with `key`. Or None, if no key exists /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public Seq FindAll(K key) => Find(key).ToSeq(); internal static TrieMap> FindChanges(TrieMap mx, TrieMap my) { var changes = TrieMap>.EmptyForMutating; foreach (var x in mx) { var y = my.Find(x.Key); if (y.IsSome) { if (!ReferenceEquals(x.Value, y.Value) || !EqDefault.Equals(x.Value, y.Value!) ) { if (!EqDefault.Equals(x.Value, y.Value!)) { changes = changes.AddOrUpdateInPlace(x.Key, Change.Mapped(x.Value, y.Value!)); } } } else { changes = changes.AddOrUpdateInPlace(x.Key, Change.Removed(x.Value)); } } foreach (var y in my) { var x = mx.Find(y.Key); if (x.IsNone) { changes = changes.AddOrUpdateInPlace(y.Key, Change.Added(y.Value)); } } return changes; } /// /// Map from V to U /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public TrieMap Map(Func f) => new (AsIterable().Select(kv => (kv.Key, f(kv.Value))), false); /// /// Map from V to U /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public (TrieMap Map, TrieMap> Changes) MapWithLog(Func f) { var target = TrieMap.EmptyForMutating; var changes = TrieMap>.EmptyForMutating; foreach (var pair in this) { var newv = f(pair.Value); target = target.AddOrUpdateInPlace(pair.Key, newv); changes = changes.AddOrUpdateInPlace(pair.Key, new EntryMapped(pair.Value, newv)); } return (target, changes); } /// /// Map from V to U /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public TrieMap Map(Func f) => new (AsIterable().Select(kv => (kv.Key, f(kv.Key, kv.Value))), false); /// /// Map from V to U /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public (TrieMap Map, TrieMap> Changes) MapWithLog(Func f) { var target = TrieMap.EmptyForMutating; var changes = TrieMap>.EmptyForMutating; foreach (var pair in this) { var newv = f(pair.Key, pair.Value); target = target.AddOrUpdateInPlace(pair.Key, newv); changes = changes.AddOrUpdateInPlace(pair.Key, new EntryMapped(pair.Value, newv)); } return (target, changes); } /// /// Filter /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public TrieMap Filter(Func f) => new (AsIterable().Filter(kv => f(kv.Value)), false); /// /// Map from V to U /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public (TrieMap Map, TrieMap> Changes) FilterWithLog(Func f) { var target = EmptyForMutating; var changes = TrieMap>.EmptyForMutating; foreach (var pair in this) { var pred = f(pair.Value); if (pred) { target = target.AddOrUpdateInPlace(pair.Key, pair.Value); } else { changes = changes.AddOrUpdateInPlace(pair.Key, Change.Removed(pair.Value)); } } return (target, changes); } /// /// Filter /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public TrieMap Filter(Func f) => new (AsIterable().Filter(kv => f(kv.Key, kv.Value)), false); /// /// Map from V to U /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public (TrieMap Map, TrieMap> Changes) FilterWithLog(Func f) { var target = EmptyForMutating; var changes = TrieMap>.EmptyForMutating; foreach (var pair in this) { var pred = f(pair.Key, pair.Value); if (pred) { target = target.AddOrUpdateInPlace(pair.Key, pair.Value); } else { changes = changes.AddOrUpdateInPlace(pair.Key, Change.Removed(pair.Value)); } } return (target, changes); } /// /// Associative union /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public TrieMap Append(TrieMap rhs) => TryAddRange(rhs.AsIterable()); /// /// Associative union /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public (TrieMap Map, TrieMap> Changes) AppendWithLog(TrieMap rhs) => TryAddRangeWithLog(rhs.AsIterable()); /// /// Subtract /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public TrieMap Subtract(TrieMap rhs) { var lhs = this; foreach (var item in rhs.Keys) { lhs = lhs.Remove(item); } return lhs; } /// /// Subtract /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public (TrieMap Map, TrieMap> Changes) SubtractWithLog(TrieMap rhs) { var changes = TrieMap>.EmptyForMutating; var lhs = this; foreach (var item in rhs.Keys) { var pair = lhs.RemoveWithLog(item); lhs = pair.Map; if (pair.Change.HasChanged) { changes = changes.AddOrUpdateInPlace(item, pair.Change); } } return (lhs, changes); } /// /// Union /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TrieMap operator +(TrieMap lhs, TrieMap rhs) => lhs.Append(rhs); /// /// Subtract /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TrieMap operator -(TrieMap lhs, TrieMap rhs) => lhs.Subtract(rhs); /// /// Equality /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(TrieMap lhs, TrieMap rhs) => lhs.Equals(rhs); /// /// Non equality /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(TrieMap lhs, TrieMap rhs) => !(lhs == rhs); /// /// Equality /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public override bool Equals(object? rhs) => rhs is TrieMap map && Equals>(map); /// /// Equality /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(TrieMap? rhs) => rhs is not null && Equals>(rhs); /// /// Equality /// public bool Equals(TrieMap? rhs) where EqV : Eq { if (ReferenceEquals(this, rhs)) return true; if (ReferenceEquals(rhs, null)) return false; if (Count != rhs.Count) return false; using var iterA = GetEnumerator(); using var iterB = rhs.GetEnumerator(); while (iterA.MoveNext() && iterB.MoveNext()) { if (!EqK.Equals(iterA.Current.Key, iterB.Current.Key)) return false; } using var iterA1 = GetEnumerator(); using var iterB1 = rhs.GetEnumerator(); while (iterA1.MoveNext() && iterB1.MoveNext()) { if (!EqV.Equals(iterA1.Current.Value, iterB1.Current.Value)) return false; } return true; } /// /// Update an item in the map /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public TrieMap Update(K key, V value) => Update(key, value, UpdateType.Add, false); /// /// Update an item in the map /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public (TrieMap Map, Change Change) UpdateWithLog(K key, V value) => UpdateWithLog(key, value, UpdateType.Add, false); /// /// Update an item in the map - can mutate if needed /// [MethodImpl(MethodImplOptions.AggressiveInlining)] TrieMap Update(K key, V value, UpdateType type, bool mutate) { var h = (uint)EqK.GetHashCode(key); Sec section = default; var (countDelta, newRoot, _, changed) = Root.Update((type, mutate), (key, value), h, section); return countDelta != 0 || changed ? new TrieMap(newRoot, count + countDelta) : this; } /// /// Update an item in the map - can mutate if needed /// [MethodImpl(MethodImplOptions.AggressiveInlining)] (TrieMap Map, Change Change) UpdateWithLog(K key, V value, UpdateType type, bool mutate) { var h = (uint)EqK.GetHashCode(key); Sec section = default; var (countDelta, newRoot, oldV, changed) = Root.Update((type, mutate), (key, value), h, section); return changed ? (new TrieMap(newRoot, count + countDelta), countDelta == 0 ? EqDefault.Equals(oldV!, value) ? Change.None : Change.Mapped(oldV, value) : Change.Added(value)) : (this, Change.None); } /// /// Returns True if 'other' is a proper subset of this set /// /// True if 'other' is a proper subset of this set public bool IsProperSubsetOf(IEnumerable<(K Key, V Value)> other) => IsProperSubsetOf(other.Select(x => x.Key)); /// /// Returns True if 'other' is a proper subset of this set /// /// True if 'other' is a proper subset of this set public bool IsProperSubsetOf(IEnumerable other) { if (IsEmpty) { return other.Any(); } var matches = 0; var extraFound = false; foreach (var item in other) { if (ContainsKey(item)) { matches++; } else { extraFound = true; } if (matches == Count && extraFound) { return true; } } return false; } /// /// Returns True if 'other' is a proper superset of this set /// /// True if 'other' is a proper superset of this set public bool IsProperSupersetOf(IEnumerable<(K Key, V Value)> other) => IsProperSupersetOf(other.Select(x => x.Key)); /// /// Returns True if 'other' is a proper superset of this set /// /// True if 'other' is a proper superset of this set public bool IsProperSupersetOf(IEnumerable other) { if (IsEmpty) { return false; } var matchCount = 0; foreach (var item in other) { matchCount++; if (!ContainsKey(item)) { return false; } } return Count > matchCount; } /// /// Returns True if 'other' is a superset of this set /// /// True if 'other' is a superset of this set public bool IsSubsetOf(IEnumerable<(K Key, V Value)> other) { if (IsEmpty) { return true; } var matches = 0; foreach (var item in other) { if (ContainsKey(item.Key)) { matches++; } } return matches == Count; } /// /// Returns True if 'other' is a superset of this set /// /// True if 'other' is a superset of this set public bool IsSubsetOf(IEnumerable other) { if (IsEmpty) { return true; } var matches = 0; foreach (var item in other) { if (ContainsKey(item)) { matches++; } } return matches == Count; } /// /// Returns True if 'other' is a superset of this set /// /// True if 'other' is a superset of this set public bool IsSubsetOf(TrieMap other) { if (IsEmpty) { // All empty sets are subsets return true; } if(Count > other.Count) { // A subset must be smaller or equal in size return false; } foreach(var item in this) { if(!other.Contains(item)) { return false; } } return true; } /// /// Returns True if 'other' is a superset of this set /// /// True if 'other' is a superset of this set public bool IsSupersetOf(IEnumerable<(K Key, V Value)> other) => IsSupersetOf(other.Select(x => x.Key)); /// /// Returns True if 'other' is a superset of this set /// /// True if 'other' is a superset of this set public bool IsSupersetOf(IEnumerable other) { foreach (var item in other) { if (!ContainsKey(item)) { return false; } } return true; } /// /// Returns True if other overlaps this set /// public bool Overlaps(IEnumerable<(K Key, V Value)> other) => Overlaps(other.Select(x => x.Key)); /// /// Returns True if other overlaps this set /// public bool Overlaps(IEnumerable other) { if (IsEmpty) { return false; } foreach (var item in other) { if (ContainsKey(item)) { return true; } } return false; } /// /// Returns the elements that are in both this and other /// public TrieMap Intersect(IEnumerable other) { var res = new List<(K, V)>(); foreach (var item in other) { var litem = GetOption(item); if (litem.IsSome) res.Add(((K, V))litem); } return new TrieMap(res); } /// /// Returns the elements that are in both this and other /// public (TrieMap Map, TrieMap> Changes) IntersectWithLog(IEnumerable other) { var set = new TrieSet(other); var changes = TrieMap>.EmptyForMutating; var res = EmptyForMutating; foreach (var item in this) { if (set.Contains(item.Key)) { res = res.AddOrUpdateInPlace(item.Key, item.Value); } else { changes = changes.AddOrUpdateInPlace(item.Key, Change.Removed(item.Value)); } } return (res, changes); } /// /// Returns the elements that are in both this and other /// public TrieMap Intersect(IEnumerable<(K Key, V Value)> other) { var res = new List<(K, V)>(); foreach (var item in other) { var litem = GetOption(item.Key); if (litem.IsSome) res.Add(((K, V))litem); } return new TrieMap(res); } /// /// Returns the elements that are in both this and other /// public (TrieMap Map, TrieMap> Changes) IntersectWithLog( IEnumerable<(K Key, V Value)> other) => IntersectWithLog(other.Select(pair => pair.Key)); /// /// Returns the elements that are in both this and other /// public TrieMap Intersect( IEnumerable<(K Key, V Value)> other, WhenMatched Merge) { var t = EmptyForMutating; foreach (var py in other) { var px = Find(py.Key); if (px.IsSome) { var r = Merge(py.Key, px.Value!, py.Value); t = t.AddOrUpdateInPlace(py.Key, r); } } return t; } /// /// Returns the elements that are in both this and other /// public (TrieMap Map, TrieMap> Changes) IntersectWithLog( TrieMap other, WhenMatched Merge) { var t = EmptyForMutating; var c = TrieMap>.EmptyForMutating; foreach (var px in this) { var py = other.Find(px.Key); if (py.IsSome) { var r = Merge(px.Key, px.Value, py.Value!); t = t.AddOrUpdateInPlace(px.Key, r); if (!EqDefault.Equals(px.Value, r)) { c = c.AddOrUpdateInPlace(px.Key, Change.Mapped(px.Value, r)); } } else { c = c.AddOrUpdateInPlace(px.Key, Change.Removed(px.Value)); } } return (t, c); } /// /// Returns this - other. Only the items in this that are not in /// other will be returned. /// public TrieMap Except(IEnumerable other) { var self = this; foreach (var item in other) { self = self.Remove(item); } return self; } /// /// Returns this - other. Only the items in this that are not in /// other will be returned. /// public (TrieMap Map, TrieMap> Changes) ExceptWithLog(IEnumerable other) { var changes = TrieMap>.EmptyForMutating; var self = this; foreach (var item in other) { var pair = self.RemoveWithLog(item); self = pair.Map; if (pair.Change.HasChanged) { changes = changes.AddOrUpdateInPlace(item, pair.Change); } } return (self, changes); } /// /// Returns this - other. Only the items in this that are not in /// other will be returned. /// public TrieMap Except(IEnumerable<(K Key, V Value)> other) { var self = this; foreach (var item in other) { self = self.Remove(item.Key); } return self; } /// /// Returns this - other. Only the items in this that are not in /// other will be returned. /// public (TrieMap Map, TrieMap> Changes) ExceptWithLog( IEnumerable<(K Key, V Value)> other) => ExceptWithLog(other.Select(p => p.Key)); /// /// Only items that are in one set or the other will be returned. /// If an item is in both, it is dropped. /// public TrieMap SymmetricExcept(TrieMap rhs) { var self = this; foreach (var item in rhs) { var pair = self.RemoveWithLog(item.Key); if (pair.Change.HasNoChange) { self = self.Add(item.Key, item.Value); } } return self; } /// /// Only items that are in one set or the other will be returned. /// If an item is in both, it is dropped. /// public (TrieMap Map, TrieMap> Changes) SymmetricExceptWithLog(TrieMap rhs) { var changes = TrieMap>.EmptyForMutating; var self = this; foreach (var item in rhs) { var pair = self.RemoveWithLog(item.Key); if (pair.Change.HasNoChange) { self = self.Add(item.Key, item.Value); changes = changes.AddOrUpdateInPlace(item.Key, Change.Added(item.Value)); } } return (self, changes); } /// /// Only items that are in one set or the other will be returned. /// If an item is in both, it is dropped. /// public TrieMap SymmetricExcept(IEnumerable<(K Key, V Value)> rhs) { var self = this; foreach (var item in rhs) { var pair = self.RemoveWithLog(item.Key); if (pair.Change.HasNoChange) { self = self.Add(item.Key, item.Value); } else { self = pair.Map; } } return self; } /// /// Only items that are in one set or the other will be returned. /// If an item is in both, it is dropped. /// public (TrieMap Map, TrieMap> Changes) SymmetricExceptWithLog(IEnumerable<(K Key, V Value)> rhs) { var changes = TrieMap>.EmptyForMutating; var self = this; foreach (var item in rhs) { var pair = self.RemoveWithLog(item.Key); if (pair.Change.HasNoChange) { self = self.Add(item.Key, item.Value); changes = changes.AddOrUpdateInPlace(item.Key, Change.Added(item.Value)); } else { self = pair.Map; changes = changes.AddOrUpdateInPlace(item.Key, pair.Change); } } return (self, changes); } /// /// Finds the union of two sets and produces a new set with /// the results /// /// Other set to union with /// A set which contains all items from both sets public TrieMap Union(IEnumerable<(K, V)> other) => TryAddRange(other); /// /// Finds the union of two sets and produces a new set with /// the results /// /// Other set to union with /// A set which contains all items from both sets public (TrieMap Map, TrieMap> Changes) UnionWithLog(IEnumerable<(K, V)> other) => TryAddRangeWithLog(other); /// /// Union two maps. /// /// /// The `WhenMatched` merge function is called when keys are present in both map to allow resolving to a /// sensible value. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public TrieMap Union( IEnumerable<(K Key, V Value)> other, WhenMatched Merge) => Union(other, MapLeft: static (_, v) => v, MapRight: static (_, v) => v, Merge); /// /// Union two maps. /// /// /// The `WhenMatched` merge function is called when keys are present in both map to allow resolving to a /// sensible value. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public (TrieMap Map, TrieMap> Changes) UnionWithLog( IEnumerable<(K Key, V Value)> other, WhenMatched Merge) => UnionWithLog(other, MapLeft: static (_, v) => v, MapRight: static (_, v) => v, Merge); /// /// Union two maps. /// /// /// The `WhenMatched` merge function is called when keys are present in both map to allow resolving to a /// sensible value. /// /// /// The `WhenMissing` function is called when there is a key in the right-hand side, but not the left-hand-side. /// This allows the `V2` value-type to be mapped to the target `V` value-type. /// public TrieMap Union( IEnumerable<(K Key, W Value)> other, WhenMissing MapRight, WhenMatched Merge) => Union(other, MapLeft: static (_, v) => v, MapRight, Merge); /// /// Union two maps. /// /// /// The `WhenMatched` merge function is called when keys are present in both map to allow resolving to a /// sensible value. /// /// /// The `WhenMissing` function is called when there is a key in the right-hand side, but not the left-hand-side. /// This allows the `V2` value-type to be mapped to the target `V` value-type. /// public (TrieMap Map, TrieMap> Changes) UnionWithLog( IEnumerable<(K Key, W Value)> other, WhenMissing MapRight, WhenMatched Merge) => UnionWithLog(other, MapLeft: static (_, v) => v, MapRight, Merge); /// /// Union two maps. /// /// /// The `WhenMatched` merge function is called when keys are present in both map to allow resolving to a /// sensible value. /// /// /// The `WhenMissing` function is called when there is a key in the left-hand side, but not the right-hand-side. /// This allows the `V` value-type to be mapped to the target `V2` value-type. /// public TrieMap Union( IEnumerable<(K Key, W Value)> other, WhenMissing MapLeft, WhenMatched Merge) => Union(other, MapLeft, MapRight: static (_, v2) => v2, Merge); /// /// Union two maps. /// /// /// The `WhenMatched` merge function is called when keys are present in both map to allow resolving to a /// sensible value. /// /// /// The `WhenMissing` function is called when there is a key in the left-hand side, but not the right-hand-side. /// This allows the `V` value-type to be mapped to the target `V2` value-type. /// public (TrieMap Map, TrieMap> Changes) UnionWithLog( IEnumerable<(K Key, W Value)> other, WhenMissing MapLeft, WhenMatched Merge) => UnionWithLog(other, MapLeft, MapRight: static (_, v2) => v2, Merge); /// /// Union two maps. /// /// /// The `WhenMatched` merge function is called when keys are present in both map to allow resolving to a /// sensible value. /// /// /// The `WhenMissing MapLeft` function is called when there is a key in the left-hand side, but not the /// right-hand-side. This allows the `V` value-type to be mapped to the target `R` value-type. /// /// /// The `WhenMissing MapRight` function is called when there is a key in the right-hand side, but not the /// left-hand-side. This allows the `V2` value-type to be mapped to the target `R` value-type. /// public TrieMap Union( IEnumerable<(K Key, W Value)> other, WhenMissing MapLeft, WhenMissing MapRight, WhenMatched Merge) { var t = TrieMap.EmptyForMutating; foreach(var (key, value) in other) { var px = Find(key); t = t.AddOrUpdateInPlace(key, px.IsSome ? Merge(key, px.Value!, value) : MapRight(key, value)); } foreach (var (key, value) in this) { if (t.ContainsKey(key)) continue; t = t.AddOrUpdateInPlace(key, MapLeft(key, value)); } return t; } /// /// Union two maps. /// /// /// The `WhenMatched` merge function is called when keys are present in both map to allow resolving to a /// sensible value. /// /// /// The `WhenMissing MapLeft` function is called when there is a key in the left-hand side, but not the /// right-hand-side. This allows the `V` value-type to be mapped to the target `R` value-type. /// /// /// The `WhenMissing MapRight` function is called when there is a key in the right-hand side, but not the /// left-hand-side. This allows the `V2` value-type to be mapped to the target `R` value-type. /// public (TrieMap Map, TrieMap> Changes) UnionWithLog( IEnumerable<(K Key, W Value)> other, WhenMissing MapLeft, WhenMissing MapRight, WhenMatched Merge) { var t = TrieMap.EmptyForMutating; var c = TrieMap>.EmptyForMutating; foreach(var (key, value) in other) { var px = Find(key); if (px.IsSome) { var r = Merge(key, px.Value!, value); t = t.AddOrUpdateInPlace(key, r); if (!EqDefault.Equals(px.Value, r)) { c = c.AddOrUpdateInPlace(key, Change.Mapped(px.Value, r)); } } else { var r = MapRight(key, value); t = t.AddOrUpdateInPlace(key, r); c = c.AddOrUpdateInPlace(key, Change.Added(r)); } } foreach (var (key, value) in this) { if (t.ContainsKey(key)) continue; var r = MapLeft(key, value); t = t.AddOrUpdateInPlace(key, r); if (!EqDefault.Equals(value, r)) { c = c.AddOrUpdateInPlace(key, Change.Mapped(value, r)); } } return (t, c); } /// /// Nodes in the CHAMP hash trie map can be in one of three states: /// /// Empty - nothing in the map /// Entries - contains items and sub-nodes /// Collision - keeps track of items that have different keys but the same hash /// /// internal interface Node : IEnumerable<(K, V)> { Tag Type { get; } (bool Found, K Key, V? Value) Read(K key, uint hash, Sec section); (int CountDelta, Node Node, V? Old, bool Changed) Update((UpdateType Type, bool Mutate) env, (K Key, V Value) change, uint hash, Sec section); (int CountDelta, Node Node, V? Old) Remove(K key, uint hash, Sec section); } /////////////////////////////////////////////////////////////////////////////////////////// // // NOTE: Here be dragons! The code below is has been optimised for performace. Yes, it's // ugly, yes there's repetition, but it's all to squeeze the last few nanoseconds of // performance out of the system. Don't hate me ;) // /// /// Contains items and sub-nodes /// internal class Entries : Node { public readonly uint EntryMap; public readonly uint NodeMap; public readonly (K Key, V Value)[] Items; public readonly Node[] Nodes; public Tag Type => Tag.Entries; public Entries(uint entryMap, uint nodeMap, (K, V)[] items, Node[] nodes) { EntryMap = entryMap; NodeMap = nodeMap; Items = items; Nodes = nodes; } public void Deconstruct(out uint entryMap, out uint nodeMap, out (K, V)[] items, out Node[] nodes) { entryMap = EntryMap; nodeMap = NodeMap; items = Items; nodes = Nodes; } public (int CountDelta, Node Node, V? Old) Remove(K key, uint hash, Sec section) { var hashIndex = Bit.Get(hash, section); var mask = Mask(hashIndex); if (Bit.Get(EntryMap, mask)) { // If key belongs to an entry var ind = Index(EntryMap, mask); if (EqK.Equals(Items[ind].Key, key)) { var v = Items[ind].Value; return (-1, new Entries( Bit.Set(EntryMap, mask, false), NodeMap, RemoveAt(Items, ind), Nodes), v ); } else { return (0, this, default); } } else if (Bit.Get(NodeMap, mask)) { //If key lies in a sub-node var ind = Index(NodeMap, mask); var (cd, subNode, v) = Nodes[ind].Remove(key, hash, section.Next()); if (cd == 0) return (0, this, default); switch (subNode.Type) { case Tag.Entries: var subEntries = (Entries)subNode; if (subEntries.Items.Length == 1 && subEntries.Nodes.Length == 0) { // If the node only has one subnode, make that subnode the new node if (Items.Length == 0 && Nodes.Length == 1) { // Build a new Entries for this level with the sublevel mask fixed return (cd, new Entries( Mask(Bit.Get((uint)EqK.GetHashCode(subEntries.Items[0].Key), section)), 0, Clone(subEntries.Items), Array.Empty() ), v); } else { return (cd, new Entries( Bit.Set(EntryMap, mask, true), Bit.Set(NodeMap, mask, false), Insert(Items, Index(EntryMap, mask), subEntries.Items[0]), RemoveAt(Nodes, ind)), v); } } else { var nodeCopy = Clone(Nodes); nodeCopy[ind] = subNode; return (cd, new Entries(EntryMap, NodeMap, Items, nodeCopy), v); } case Tag.Collision: var nodeCopy2 = Clone(Nodes); nodeCopy2[ind] = subNode; return (cd, new Entries(EntryMap, NodeMap, Items, nodeCopy2), v); default: return (0, this, default); } } else { return (0, this, default); } } public (bool Found, K Key, V? Value) Read(K key, uint hash, Sec section) { // var hashIndex = Bit.Get(hash, section); // Mask(hashIndex) var mask = (uint)(1 << (int)((hash & (uint)(Sec.Mask << section.Offset)) >> section.Offset)); // if(Bit.Get(EntryMap, mask)) if ((EntryMap & mask) == mask) { // var entryIndex = Index(EntryMap, mask); var entryIndex = BitCount((int)EntryMap & (((int)mask) - 1)); if (EqK.Equals(Items[entryIndex].Key, key)) { var item = Items[entryIndex]; return (true, item.Key, item.Value); } else { return default; } } // else if (Bit.Get(NodeMap, mask)) else if ((NodeMap & mask) == mask) { // var entryIndex = Index(NodeMap, mask); var entryIndex = BitCount((int)NodeMap & ((int)mask - 1)); return Nodes[entryIndex].Read(key, hash, section.Next()); } else { return default; } } public (int CountDelta, Node Node, V? Old, bool Changed) Update((UpdateType Type, bool Mutate) env, (K Key, V Value) change, uint hash, Sec section) { // var hashIndex = Bit.Get(hash, section); // var mask = Mask(hashIndex); var mask = (uint)(1 << (int)((hash & (uint)(Sec.Mask << section.Offset)) >> section.Offset)); //if (Bit.Get(EntryMap, mask)) if((EntryMap & mask) == mask) { //var entryIndex = Index(EntryMap, mask); var entryIndex = BitCount((int)EntryMap & ((int)mask - 1)); var currentEntry = Items[entryIndex]; if (EqK.Equals(currentEntry.Key, change.Key)) { if (env.Type == UpdateType.Add) { // Key already exists - so it's an error to add again throw new ArgumentException($"Key already exists in map: {change.Key}"); } else if (env.Type == UpdateType.TryAdd) { // Already added, so we don't continue to try return (0, this, default, false); } var (newItems, old) = SetItem(Items, entryIndex, change, env.Mutate); return (0, new Entries(EntryMap, NodeMap, newItems, Nodes), old.Value, true); } else { if (env.Type == UpdateType.SetItem) { // Key must already exist to set it throw new ArgumentException($"Key already exists in map: {change.Key}"); } else if (env.Type == UpdateType.TrySetItem) { // Key doesn't exist, so there's nothing to set return (0, this, default, false); } // Add var node = Merge(change, currentEntry, hash, (uint)EqK.GetHashCode(currentEntry.Key), section); //var newItems = Items.Filter(elem => !default(EqK).Equals(elem.Key, currentEntry.Key)).ToArray(); var newItems = new (K Key, V Value)[Items.Length - 1]; var i = 0; foreach(var elem in Items) { if(!EqK.Equals(elem.Key, currentEntry.Key)) { newItems[i] = elem; i++; } } //var newEntryMap = Bit.Set(EntryMap, mask, false); var newEntryMap = EntryMap & (~mask); // var newNodeMap = Bit.Set(NodeMap, mask, true); var newNodeMap = NodeMap | mask; // var nodeIndex = Index(NodeMap, mask); var nodeIndex = BitCount((int)NodeMap & ((int)mask - 1)); var newNodes = Insert(Nodes, nodeIndex, node); return (1, new Entries(newEntryMap, newNodeMap, newItems, newNodes), default, true); } } else if (Bit.Get(NodeMap, mask)) { // var nodeIndex = Index(NodeMap, mask); var nodeIndex = BitCount((int)NodeMap & ((int)mask - 1)); var nodeToUpdate = Nodes[nodeIndex]; var (cd, newNode, ov, ch) = nodeToUpdate.Update(env, change, hash, section.Next()); var (newNodes, _) = SetItem(Nodes, nodeIndex, newNode, env.Mutate); return (cd, new Entries(EntryMap, NodeMap, Items, newNodes), ov, ch); } else { if (env.Type == UpdateType.SetItem) { // Key must already exist to set it throw new ArgumentException($"Key doesn't exist in map: {change.Key}"); } else if (env.Type == UpdateType.TrySetItem) { // Key doesn't exist, so there's nothing to set return (0, this, default, false); } // var entryIndex = Index(EntryMap, mask); var entryIndex = BitCount((int)EntryMap & ((int)mask - 1)); // var entries = Bit.Set(EntryMap, mask, true); var entries = EntryMap | mask; var newItems = Insert(Items, entryIndex, change); return (1, new Entries(entries, NodeMap, newItems, Nodes), default, true); } } public IEnumerator<(K, V)> GetEnumerator() { foreach (var item in Items) { yield return item; } foreach (var node in Nodes) { foreach (var item in node) { yield return item; } } } IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } /// /// Contains items that share the same hash but have different keys /// internal class Collision : Node { public readonly (K Key, V Value)[] Items; public readonly uint Hash; public Tag Type => Tag.Collision; public Collision((K Key, V Value)[] items, uint hash) { Items = items; Hash = hash; } public (bool Found, K Key, V Value) Read(K key, uint hash, Sec section) { foreach (var kv in Items) { if (EqK.Equals(kv.Key, key)) { return (true, kv.Key, kv.Value); } } return default; } public (int CountDelta, Node Node, V? Old) Remove(K key, uint hash, Sec section) { var len = Items.Length; if (len == 0) return (0, this, default); else if (len == 1) return (-1, EmptyNode.Default, Items[0].Value); else if (len == 2) { var ((_, n, _, _), ov) = EqK.Equals(Items[0].Key, key) ? (EmptyNode.Default.Update((UpdateType.Add, false), Items[1], hash, default), Items[0].Value) : (EmptyNode.Default.Update((UpdateType.Add, false), Items[0], hash, default), Items[1].Value); return (-1, n, ov); } else { V? oldValue = default; IEnumerable<(K, V)> Yield((K Key, V Value)[] items, K ikey) { foreach (var item in items) { if (EqK.Equals(item.Key, ikey)) { oldValue = item.Value; } else { yield return item; } } } var nitems = Yield(Items, key).ToArray(); return (nitems.Length - Items.Length, new Collision(nitems, hash), oldValue); } } public (int CountDelta, Node Node, V? Old, bool Changed) Update((UpdateType Type, bool Mutate) env, (K Key, V Value) change, uint hash, Sec section) { var index = -1; for (var i = 0; i < Items.Length; i++) { if (EqK.Equals(Items[i].Key, change.Key)) { index = i; break; } } if (index >= 0) { if (env.Type == UpdateType.Add) { // Key already exists - so it's an error to add again throw new ArgumentException($"Key already exists in map: {change.Key}"); } else if (env.Type == UpdateType.TryAdd) { // Already added, so we don't continue to try return (0, this, default, false); } var (newArr, ov) = SetItem(Items, index, change, false); return (0, new Collision(newArr, hash), ov.Value, true); } else { if (env.Type == UpdateType.SetItem) { // Key must already exist to set it throw new ArgumentException($"Key doesn't exist in map: {change.Key}"); } else if (env.Type == UpdateType.TrySetItem) { // Key doesn't exist, so there's nothing to set return (0, this, default, false); } var nitems = new (K, V)[Items.Length + 1]; Array.Copy(Items, nitems, Items.Length); nitems[Items.Length] = change; return (1, new Collision(nitems, hash), default, true); } } public IEnumerator<(K, V)> GetEnumerator() => Items.AsEnumerable().GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => Items.AsEnumerable().GetEnumerator(); } /// /// Empty node /// internal class EmptyNode : Node { public static readonly EmptyNode Default = new EmptyNode(); public Tag Type => Tag.Empty; public (bool Found, K Key, V Value) Read(K key, uint hash, Sec section) => default; public (int CountDelta, Node Node, V? Old) Remove(K key, uint hash, Sec section) => (0, this, default); public (int CountDelta, Node Node, V? Old, bool Changed) Update((UpdateType Type, bool Mutate) env, (K Key, V Value) change, uint hash, Sec section) { if (env.Type == UpdateType.SetItem) { // Key must already exist to set it throw new ArgumentException($"Key doesn't exist in map: {change.Key}"); } else if (env.Type == UpdateType.TrySetItem) { // Key doesn't exist, so there's nothing to set return (0, this, default, false); } var dataMap = Mask(Bit.Get(hash, section)); return (1, new Entries(dataMap, 0, [change], []), default, true); } public IEnumerator<(K, V)> GetEnumerator() { yield break; } IEnumerator IEnumerable.GetEnumerator() { yield break; } } /// /// Merges two key-value pairs into a single Node /// static Node Merge((K, V) pair1, (K, V) pair2, uint pair1Hash, uint pair2Hash, Sec section) { if (section.Offset >= 25) { return new Collision([pair1, pair2], pair1Hash); } else { var nextLevel = section.Next(); var pair1Index = Bit.Get(pair1Hash, nextLevel); var pair2Index = Bit.Get(pair2Hash, nextLevel); if (pair1Index == pair2Index) { var node = Merge(pair1, pair2, pair1Hash, pair2Hash, nextLevel); var nodeMap = Mask(pair1Index); return new Entries(0, nodeMap, Array.Empty<(K, V)>(), [node]); } else { var dataMap = Mask(pair1Index); dataMap = Bit.Set(dataMap, Mask(pair2Index), true); return new Entries(dataMap, 0, pair1Index < pair2Index ? [pair1, pair2] : [pair2, pair1], Array.Empty()); } } } public Iterable<(K Key, V Value)> AsIterable() => Root.AsIterable(); public IEnumerator<(K Key, V Value)> GetEnumerator() => // ReSharper disable once NotDisposedResourceIsReturned Root.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => // ReSharper disable once NotDisposedResourceIsReturned Root.GetEnumerator(); /// /// Counts the number of 1-bits in bitmap /// [MethodImpl(MethodImplOptions.AggressiveInlining)] static int BitCount(int bits) { var c2 = bits - ((bits >> 1) & 0x55555555); var c4 = (c2 & 0x33333333) + ((c2 >> 2) & 0x33333333); var c8 = (c4 + (c4 >> 4)) & 0x0f0f0f0f; return (c8 * 0x01010101) >> 24; } /// /// Finds the number of set bits below the bit at `location` /// This function is used to find where in the array of entries or nodes /// the item should be inserted /// [MethodImpl(MethodImplOptions.AggressiveInlining)] static int Index(uint bits, int location) => BitCount((int)bits & (location - 1)); /// /// Finds the number of 1-bits below the bit at `location` /// This function is used to find where in the array of entries or nodes /// the item should be inserted /// [MethodImpl(MethodImplOptions.AggressiveInlining)] static int Index(uint bitmap, uint location) => BitCount((int)bitmap & ((int)location - 1)); /// /// Returns the value used to index into the bit vector /// [MethodImpl(MethodImplOptions.AggressiveInlining)] static uint Mask(int index) => (uint)(1 << index); /// /// Sets the item at index. If mutate is true it sets the /// value without copying the array, otherwise the operation is pure /// [MethodImpl(MethodImplOptions.AggressiveInlining)] static (A[] Items, A Old) SetItem(A[] items, int index, A value, bool mutate) { if (mutate) { var old = items[index]; items[index] = value; return (items, old); } else { var old = items[index]; var nitems = new A[items.Length]; Array.Copy(items, nitems, items.Length); nitems[index] = value; return (nitems, old); } } /// /// Clones part of an existing array /// [MethodImpl(MethodImplOptions.AggressiveInlining)] static A[] Clone(A[] items, int count) { var nitems = new A[count]; Array.Copy(items, nitems, count); return nitems; } /// /// Clones an existing array /// [MethodImpl(MethodImplOptions.AggressiveInlining)] static A[] Clone(A[] items) { var len = items.Length; var nitems = new A[len]; Array.Copy(items, nitems, len); return nitems; } /// /// Inserts a new item in the array (immutably) /// [MethodImpl(MethodImplOptions.AggressiveInlining)] static A[] Insert(A[] array, int index, A value) { var narray = new A[array.Length + 1]; Array.Copy(array, 0, narray, 0, index); Array.Copy(array, index, narray, index + 1, array.Length - index); narray[index] = value; return narray; } /// /// Returns a new array with the item at index removed /// [MethodImpl(MethodImplOptions.AggressiveInlining)] static A[] RemoveAt(A[] array, int index) { if (array.Length == 0) { return array; } var narray = new A[array.Length - 1]; if (index > 0) { Array.Copy(array, 0, narray, 0, index); } if (index + 1 < array.Length) { Array.Copy(array, index + 1, narray, index, array.Length - index - 1); } return narray; } public override string ToString() => count < 50 ? $"[{ string.Join(", ", AsIterable().Select(TupleToString)) }]" : $"[{ string.Join(", ", AsIterable().Select(TupleToString).Take(50)) } ... ]"; string TupleToString((K Key, V Value) tuple) => $"({tuple.Key}, {tuple.Value})"; public Iterable Keys => AsIterable().Map(kv => kv.Key); public Iterable Values => AsIterable().Map(kv => kv.Value); public bool TryGetValue(K key, out V value) { var ov = Find(key); if (ov.IsSome) { value = (V)ov; return true; } else { value = default!; return false; } } } internal readonly struct Sec { public const int Mask = 31; public readonly int Offset; [MethodImpl(MethodImplOptions.AggressiveInlining)] public Sec(int offset) => Offset = offset; [MethodImpl(MethodImplOptions.AggressiveInlining)] public Sec Next() => new (Offset + 5); } internal static class Bit { [MethodImpl(MethodImplOptions.AggressiveInlining)] public static uint Set(uint value, int bit, bool flag) => flag ? value | (uint)bit : value & ~(uint)bit; [MethodImpl(MethodImplOptions.AggressiveInlining)] public static uint Set(uint value, uint bit, bool flag) => flag ? value | bit : value & ~bit; [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool Get(uint value, int bit) => (value & (uint)bit) == (uint)bit; [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool Get(uint value, uint bit) => (value & bit) == bit; [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int Get(uint data, Sec section) => (int)((data & (uint)(Sec.Mask << section.Offset)) >> section.Offset); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static uint Set(uint data, Sec section, int value) { value <<= section.Offset; var offsetMask = (0xFFFF & Sec.Mask) << section.Offset; return data & ~(uint)offsetMask | (uint)value & (uint)offsetMask; } } internal static class TrieMapExtentsions { public static TrieMap Merge(this TrieMap lhs, TrieMap rhs) where EqK : Eq where V : Semigroup { var self = lhs; foreach (var iy in rhs) { var ix = self.Find(iy.Key); if (ix.IsSome) { self = self.SetItem(iy.Key, ix.Value!.Combine(iy.Value)); } else { self = self.Add(iy.Key, iy.Value); } } return self; } } ================================================ FILE: LanguageExt.Core/Immutable Collections/TrieSet/TrieSet.cs ================================================ #pragma warning disable CS0693 // Type parameter has the same name as the type parameter from outer type using LanguageExt.Traits; using static LanguageExt.Prelude; using System; using System.Runtime.CompilerServices; using System.Linq; using System.Collections.Generic; using System.Collections; namespace LanguageExt; /// /// Implementation of the CHAMP trie hash map data structure (Compressed Hash Array Map Trie) /// https://michael.steindorfer.name/publications/phd-thesis-efficient-immutable-collections.pdf /// /// /// Used by internally by `LanguageExt.HashSet` /// internal class TrieSet : IEquatable>, IReadOnlyCollection where EqK : Eq { internal enum UpdateType { Add, TryAdd, AddOrUpdate, SetItem, TrySetItem } internal enum Tag { Entries, Collision, Empty } public static readonly TrieSet Empty = new (EmptyNode.Default, 0); readonly Node Root; readonly int count; int hash; /// /// Ctor /// [MethodImpl(MethodImplOptions.AggressiveInlining)] TrieSet(Node root, int count) { Root = root; this.count = count; } public TrieSet(ReadOnlySpan items, bool tryAdd = true) { Root = EmptyNode.Default; var type = tryAdd ? UpdateType.TryAdd : UpdateType.AddOrUpdate; foreach (var item in items) { var hash = (uint)EqK.GetHashCode(item); Sec section = default; var (countDelta, newRoot) = Root.Update((type, true), item, hash, section); count += countDelta; Root = newRoot; } } public TrieSet(IEnumerable items, bool tryAdd = true) { Root = EmptyNode.Default; var type = tryAdd ? UpdateType.TryAdd : UpdateType.AddOrUpdate; foreach (var item in items) { var hash = (uint)EqK.GetHashCode(item); Sec section = default; var (countDelta, newRoot) = Root.Update((type, true), item, hash, section); count += countDelta; Root = newRoot; } } /// /// True if no items in the map /// public bool IsEmpty { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => count == 0; } /// /// Number of items in the map /// public int Count { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => count; } /// /// Add an item to the map /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public TrieSet Add(K key) => Update(key, UpdateType.Add, false); /// /// Try to add an item to the map. If it already exists, do /// nothing. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public TrieSet TryAdd(K key) => Update(key, UpdateType.TryAdd, false); /// /// Add an item to the map, if it exists update the value /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public TrieSet AddOrUpdate(K key) => Update(key, UpdateType.AddOrUpdate, false); /// /// Add a range of values to the map /// If any items already exist an exception will be thrown /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public TrieSet AddRange(IEnumerable items) { var self = this; foreach (var item in items) { self = self.Add(item); } return self; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public TrieSet TryAddRange(IEnumerable items) { var self = this; foreach (var item in items) { self = self.TryAdd(item); } return self; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public TrieSet AddOrUpdateRange(IEnumerable items) { var self = this; foreach (var item in items) { self = self.AddOrUpdate(item); } return self; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public TrieSet SetItems(IEnumerable items) { var self = this; foreach (var item in items) { self = self.SetItem(item); } return self; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public TrieSet TrySetItems(IEnumerable items) { var self = this; foreach (var item in items) { self = self.TrySetItem(item); } return self; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public TrieSet RemoveRange(IEnumerable items) { var self = this; foreach (var item in items) { self = self.Remove(item); } return self; } /// /// Set an item that already exists in the map /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public TrieSet SetItem(K key) => Update(key, UpdateType.SetItem, false); /// /// Try to set an item that already exists in the map. If none /// exists, do nothing. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public TrieSet TrySetItem(K key) => Update(key, UpdateType.TrySetItem, false); /// /// Update an item in the map /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public TrieSet Update(K key) => Update(key, UpdateType.Add, false); /// /// Remove an item from the map /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public TrieSet Remove(K key) { var hash = (uint)EqK.GetHashCode(key); Sec section = default; var (countDelta, newRoot) = Root.Remove(key, hash, section); return ReferenceEquals(newRoot, Root) ? this : new TrieSet(newRoot, count + countDelta); } /// /// Indexer /// public K this[K key] { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { var (found, nkey) = FindInternal(key); return found ? nkey : throw new ArgumentException($"Key doesn't exist in map: {key}"); } } /// /// Create an empty map /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public TrieSet Clear() => Empty; /// /// Get the hash code of the items in the map /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public override int GetHashCode() => hash == 0 ? (hash = FNV32.Hash(AsEnumerable())) : hash; /// /// Returns the whether the `key` exists in the map /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool ContainsKey(K key) => FindInternal(key).Found; /// /// Returns the value associated with `key`. Or None, if no key exists /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public Option Find(K key) { var (found, nkey) = FindInternal(key); return found ? Some(nkey) : default; } /// /// Returns the value associated with `key`. Or None, if no key exists /// [MethodImpl(MethodImplOptions.AggressiveInlining)] (bool Found, K Key) FindInternal(K key) { var hash = (uint)EqK.GetHashCode(key); Sec section = default; return Root.Read(key, hash, section); } /// /// Map from K to U /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public TrieSet Map(Func f) where EqU : Eq => new (AsEnumerable().Select(f), true); /// /// Map from K to K /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public TrieSet Map(Func f) => new (AsEnumerable().Select(f), true); /// /// Filter /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public TrieSet Filter(Func f) => new (AsEnumerable().Filter(f), false); /// /// Associative union /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public TrieSet Append(TrieSet rhs) => TryAddRange(rhs.AsEnumerable()); /// /// Subtract /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public TrieSet Subtract(TrieSet rhs) { var lhs = this; foreach (var item in rhs) { lhs = lhs.Remove(item); } return lhs; } /// /// Union /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TrieSet operator +(TrieSet lhs, TrieSet rhs) => lhs.Append(rhs); /// /// Subtract /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static TrieSet operator -(TrieSet lhs, TrieSet rhs) => lhs.Subtract(rhs); /// /// Equality /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(TrieSet lhs, TrieSet rhs) => lhs.Equals(rhs); /// /// Non equality /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(TrieSet lhs, TrieSet rhs) => !(lhs == rhs); /// /// Equality /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public override bool Equals(object? rhs) => rhs is TrieSet map && Equals(map); /// /// Equality /// public bool Equals(TrieSet? rhs) { if (ReferenceEquals(this, rhs)) return true; if (ReferenceEquals(rhs, null)) return false; if (Count != rhs.Count) return false; using var iterA = GetEnumerator(); using var iterB = rhs.GetEnumerator(); while (iterA.MoveNext() && iterB.MoveNext()) { if (!EqK.Equals(iterA.Current, iterB.Current)) return false; } return true; } /// /// Update an item in the map - can mutate if needed /// [MethodImpl(MethodImplOptions.AggressiveInlining)] TrieSet Update(K key, UpdateType type, bool mutate) { var hash = (uint)EqK.GetHashCode(key); Sec section = default; var (countDelta, newRoot) = Root.Update((type, mutate), key, hash, section); return ReferenceEquals(newRoot, Root) ? this : new TrieSet(newRoot, count + countDelta); } /// /// Returns True if 'other' is a proper subset of this set /// /// True if 'other' is a proper subset of this set public bool IsProperSubsetOf(IEnumerable other) { if (IsEmpty) { return other.Any(); } int matches = 0; bool extraFound = false; foreach (var item in other) { if (ContainsKey(item)) { matches++; } else { extraFound = true; } if (matches == Count && extraFound) { return true; } } return false; } /// /// Returns True if 'other' is a proper superset of this set /// /// True if 'other' is a proper superset of this set public bool IsProperSupersetOf(IEnumerable other) { if (IsEmpty) { return false; } int matchCount = 0; foreach (var item in other) { matchCount++; if (!ContainsKey(item)) { return false; } } return Count > matchCount; } /// /// Returns True if 'other' is a superset of this set /// /// True if 'other' is a superset of this set public bool IsSubsetOf(IEnumerable other) { if (IsEmpty) { return true; } int matches = 0; foreach (var item in other) { if (ContainsKey(item)) { matches++; } } return matches == Count; } /// /// Returns True if 'other' is a superset of this set /// /// True if 'other' is a superset of this set public bool IsSubsetOf(TrieSet other) { if (IsEmpty) { // All empty sets are subsets return true; } if(Count > other.Count) { // A subset must be smaller or equal in size return false; } foreach(var item in this) { if(!other.Contains(item)) { return false; } } return true; } /// /// Returns True if 'other' is a superset of this set /// /// True if 'other' is a superset of this set public bool IsSupersetOf(IEnumerable other) { foreach (var item in other) { if (!ContainsKey(item)) { return false; } } return true; } /// /// Returns True if other overlaps this set /// public bool Overlaps(IEnumerable other) { if (IsEmpty) { return false; } foreach (var item in other) { if (ContainsKey(item)) { return true; } } return false; } /// /// Returns the elements that are in both this and other /// public TrieSet Intersect(IEnumerable other) { var res = new List(); foreach (var item in other) { var litem = Find(item); if (litem.IsSome) res.Add((K)litem); } return new TrieSet(res); } /// /// Returns this - other. Only the items in this that are not in /// other will be returned. /// public TrieSet Except(IEnumerable other) { var self = this; foreach (var item in other) { self = self.Remove(item); } return self; } /// /// Only items that are in one set or the other will be returned. /// If an item is in both, it is dropped. /// public TrieSet SymmetricExcept(IEnumerable rhs) => SymmetricExcept(new TrieSet(rhs, true)); /// /// Only items that are in one set or the other will be returned. /// If an item is in both, it is dropped. /// public TrieSet SymmetricExcept(TrieSet rhs) { var res = new List(); foreach (var item in this) { if (!rhs.ContainsKey(item)) { res.Add(item); } } foreach (var item in rhs) { if (!ContainsKey(item)) { res.Add(item); } } return new TrieSet(res); } /// /// Finds the union of two sets and produces a new set with /// the results /// /// Other set to union with /// A set which contains all items from both sets public TrieSet Union(IEnumerable other) => this.TryAddRange(other); /// /// Nodes in the CHAMP hash trie map can be in one of three states: /// /// Empty - nothing in the map /// Entries - contains items and sub-nodes /// Collision - keeps track of items that have different keys but the same hash /// /// internal interface Node : IEnumerable { Tag Type { get; } (bool Found, K Key) Read(K key, uint hash, Sec section); (int CountDelta, Node Node) Update((UpdateType Type, bool Mutate) env, K change, uint hash, Sec section); (int CountDelta, Node Node) Remove(K key, uint hash, Sec section); } /////////////////////////////////////////////////////////////////////////////////////////// // // NOTE: Here be dragons! The code below is has been optimised for performace. Yes, it's // ugly, yes there's repetition, but it's all to squeeze the last few nanoseconds of // performance out of the system. Don't hate me ;) // /// /// Contains items and sub-nodes /// internal class Entries : Node { public readonly uint EntryMap; public readonly uint NodeMap; public readonly K[] Items; public readonly Node[] Nodes; public Tag Type => Tag.Entries; public Entries(uint entryMap, uint nodeMap, K[] items, Node[] nodes) { EntryMap = entryMap; NodeMap = nodeMap; Items = items; Nodes = nodes; } public void Deconstruct(out uint entryMap, out uint nodeMap, out K[] items, out Node[] nodes) { entryMap = EntryMap; nodeMap = NodeMap; items = Items; nodes = Nodes; } public (int CountDelta, Node Node) Remove(K key, uint hash, Sec section) { var hashIndex = Bit.Get(hash, section); var mask = Mask(hashIndex); if (Bit.Get(EntryMap, mask)) { // If key belongs to an entry var ind = Index(EntryMap, mask); if (EqK.Equals(Items[ind], key)) { return (-1, new Entries( Bit.Set(EntryMap, mask, false), NodeMap, RemoveAt(Items, ind), Nodes)); } else { return (0, this); } } else if (Bit.Get(NodeMap, mask)) { //If key lies in a sub-node var ind = Index(NodeMap, mask); var (cd, subNode) = Nodes[ind].Remove(key, hash, section.Next()); if (cd == 0) return (0, this); switch (subNode.Type) { case Tag.Entries: var subEntries = (Entries)subNode; if (subEntries.Items.Length == 1 && subEntries.Nodes.Length == 0) { // If the node only has one subnode, make that subnode the new node if (Items.Length == 0 && Nodes.Length == 1) { // Build a new Entries for this level with the sublevel mask fixed return (cd, new Entries( Mask(Bit.Get((uint)EqK.GetHashCode(subEntries.Items[0]), section)), 0, Clone(subEntries.Items), System.Array.Empty() )); } else { return (cd, new Entries( Bit.Set(EntryMap, mask, true), Bit.Set(NodeMap, mask, false), Insert(Items, Index(EntryMap, mask), subEntries.Items[0]), RemoveAt(Nodes, ind))); } } else { var nodeCopy = Clone(Nodes); nodeCopy[ind] = subNode; return (cd, new Entries(EntryMap, NodeMap, Items, nodeCopy)); } case Tag.Collision: var nodeCopy2 = Clone(Nodes); nodeCopy2[ind] = subNode; return (cd, new Entries(EntryMap, NodeMap, Items, nodeCopy2)); default: return (cd, this); } } else { return (0, this); } } public (bool Found, K Key) Read(K key, uint hash, Sec section) { // var hashIndex = Bit.Get(hash, section); // Mask(hashIndex) var mask = (uint)(1 << (int)((hash & (uint)(Sec.Mask << section.Offset)) >> section.Offset)); // if(Bit.Get(EntryMap, mask)) if ((EntryMap & mask) == mask) { // var entryIndex = Index(EntryMap, mask); var entryIndex = BitCount((int)EntryMap & (((int)mask) - 1)); if (EqK.Equals(Items[entryIndex], key)) { var item = Items[entryIndex]; return (true, item); } else { return default; } } // else if (Bit.Get(NodeMap, mask)) else if ((NodeMap & mask) == mask) { // var entryIndex = Index(NodeMap, mask); var entryIndex = BitCount((int)NodeMap & (((int)mask) - 1)); return Nodes[entryIndex].Read(key, hash, section.Next()); } else { return default; } } public (int CountDelta, Node Node) Update((UpdateType Type, bool Mutate) env, K change, uint hash, Sec section) { // var hashIndex = Bit.Get(hash, section); // var mask = Mask(hashIndex); var mask = (uint)(1 << (int)((hash & (uint)(Sec.Mask << section.Offset)) >> section.Offset)); //if (Bit.Get(EntryMap, mask)) if((EntryMap & mask) == mask) { //var entryIndex = Index(EntryMap, mask); var entryIndex = BitCount((int)EntryMap & (((int)mask) - 1)); var currentEntry = Items[entryIndex]; if (EqK.Equals(currentEntry, change)) { if (env.Type == UpdateType.Add) { // Key already exists - so it's an error to add again throw new ArgumentException($"Key already exists in map: {change}"); } else if (env.Type == UpdateType.TryAdd) { // Already added, so we don't continue to try return (0, this); } var newItems = SetItem(Items, entryIndex, change, env.Mutate); return (0, new Entries(EntryMap, NodeMap, newItems, Nodes)); } else { if (env.Type == UpdateType.SetItem) { // Key must already exist to set it throw new ArgumentException($"Key already exists in map: {change}"); } else if (env.Type == UpdateType.TrySetItem) { // Key doesn't exist, so there's nothing to set return (0, this); } // Add var node = Merge(change, currentEntry, hash, (uint)EqK.GetHashCode(currentEntry), section); //var newItems = Items.Filter(elem => !EqK.Equals(elem.Key, currentEntry.Key)).ToArray(); var newItems = new K[Items.Length - 1]; var i = 0; foreach(var elem in Items) { if(!EqK.Equals(elem, currentEntry)) { newItems[i] = elem; i++; } } //var newEntryMap = Bit.Set(EntryMap, mask, false); var newEntryMap = EntryMap & (~mask); // var newNodeMap = Bit.Set(NodeMap, mask, true); var newNodeMap = NodeMap | mask; // var nodeIndex = Index(NodeMap, mask); var nodeIndex = BitCount((int)NodeMap & (((int)mask) - 1)); var newNodes = Insert(Nodes, nodeIndex, node); return (1, new Entries( newEntryMap, newNodeMap, newItems, newNodes)); } } else if (Bit.Get(NodeMap, mask)) { // var nodeIndex = Index(NodeMap, mask); var nodeIndex = BitCount((int)NodeMap & (((int)mask) - 1)); var nodeToUpdate = Nodes[nodeIndex]; var (cd, newNode) = nodeToUpdate.Update(env, change, hash, section.Next()); var newNodes = SetItem(Nodes, nodeIndex, newNode, env.Mutate); return (cd, new Entries(EntryMap, NodeMap, Items, newNodes)); } else { if (env.Type == UpdateType.SetItem) { // Key must already exist to set it throw new ArgumentException($"Key doesn't exist in map: {change}"); } else if (env.Type == UpdateType.TrySetItem) { // Key doesn't exist, so there's nothing to set return (0, this); } // var entryIndex = Index(EntryMap, mask); var entryIndex = BitCount((int)EntryMap & (((int)mask) - 1)); // var entries = Bit.Set(EntryMap, mask, true); var entries = EntryMap | mask; var newItems = Insert(Items, entryIndex, change); return (1, new Entries(entries, NodeMap, newItems, Nodes)); } } public IEnumerator GetEnumerator() { foreach (var item in Items) { yield return item; } foreach (var node in Nodes) { foreach (var item in node) { yield return item; } } } IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } /// /// Contains items that share the same hash but have different keys /// internal class Collision : Node { public readonly K[] Items; public readonly uint Hash; public Tag Type => Tag.Collision; public Collision(K[] items, uint hash) { Items = items; Hash = hash; } public (bool Found, K Key) Read(K key, uint hash, Sec section) { foreach (var kv in Items) { if (EqK.Equals(kv, key)) { return (true, kv); } } return default; } public (int CountDelta, Node Node) Remove(K key, uint hash, Sec section) { var len = Items.Length; if (len == 0) return (0, this); else if (len == 1) return (-1, EmptyNode.Default); else if (len == 2) { var (_, n) = EqK.Equals(Items[0], key) ? EmptyNode.Default.Update((UpdateType.Add, false), Items[1], hash, default) : EmptyNode.Default.Update((UpdateType.Add, false), Items[0], hash, default); return (-1, n); } else { IEnumerable Yield(K[] items, K ikey) { foreach (var item in items) { if (!EqK.Equals(item, ikey)) { yield return item; } } } var nitems = Yield(Items, key).ToArray(); return (nitems.Length - Items.Length, new Collision(nitems, hash)); } } public (int CountDelta, Node Node) Update((UpdateType Type, bool Mutate) env, K change, uint hash, Sec section) { var index = -1; for (var i = 0; i < Items.Length; i++) { if (EqK.Equals(Items[i], change)) { index = i; break; } } if (index >= 0) { if (env.Type == UpdateType.Add) { // Key already exists - so it's an error to add again throw new ArgumentException($"Key already exists in map: {change}"); } else if (env.Type == UpdateType.TryAdd) { // Already added, so we don't continue to try return (0, this); } var newArr = SetItem(Items, index, change, false); return (0, new Collision(newArr, hash)); } else { if (env.Type == UpdateType.SetItem) { // Key must already exist to set it throw new ArgumentException($"Key doesn't exist in map: {change}"); } else if (env.Type == UpdateType.TrySetItem) { // Key doesn't exist, so there's nothing to set return (0, this); } var nitems = new K[Items.Length + 1]; System.Array.Copy(Items, nitems, Items.Length); nitems[Items.Length] = change; return (1, new Collision(nitems, hash)); } } public IEnumerator GetEnumerator() => Items.AsEnumerable().GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => Items.AsEnumerable().GetEnumerator(); } /// /// Empty node /// internal class EmptyNode : Node { public static readonly EmptyNode Default = new EmptyNode(); public Tag Type => Tag.Empty; public (bool Found, K Key) Read(K key, uint hash, Sec section) => default; public (int CountDelta, Node Node) Remove(K key, uint hash, Sec section) => (0, this); public (int CountDelta, Node Node) Update((UpdateType Type, bool Mutate) env, K change, uint hash, Sec section) { if (env.Type == UpdateType.SetItem) { // Key must already exist to set it throw new ArgumentException($"Key doesn't exist in map: {change}"); } else if (env.Type == UpdateType.TrySetItem) { // Key doesn't exist, so there's nothing to set return (0, this); } var dataMap = Mask(Bit.Get(hash, section)); return (1, new Entries(dataMap, 0, [change], System.Array.Empty())); } public IEnumerator GetEnumerator() { yield break; } IEnumerator IEnumerable.GetEnumerator() { yield break; } } /// /// Merges two keys into a single Node /// static Node Merge(K key1, K key2, uint pair1Hash, uint pair2Hash, Sec section) { if (section.Offset >= 25) { return new Collision([key1, key2], pair1Hash); } else { var nextLevel = section.Next(); var pair1Index = Bit.Get(pair1Hash, nextLevel); var pair2Index = Bit.Get(pair2Hash, nextLevel); if (pair1Index == pair2Index) { var node = Merge(key1, key2, pair1Hash, pair2Hash, nextLevel); var nodeMap = Mask(pair1Index); return new Entries(0, nodeMap, System.Array.Empty(), new[] { node }); } else { var dataMap = Mask(pair1Index); dataMap = Bit.Set(dataMap, Mask(pair2Index), true); return new Entries(dataMap, 0, pair1Index < pair2Index ? new[] { key1, key2 } : new[] { key2, key1 }, System.Array.Empty()); } } } public Iterable AsEnumerable() => Root.AsIterable(); public IEnumerator GetEnumerator() => Root.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => Root.GetEnumerator(); /// /// Counts the number of 1-bits in bitmap /// [MethodImpl(MethodImplOptions.AggressiveInlining)] static int BitCount(int bits) { var c2 = bits - ((bits >> 1) & 0x55555555); var c4 = (c2 & 0x33333333) + ((c2 >> 2) & 0x33333333); var c8 = (c4 + (c4 >> 4)) & 0x0f0f0f0f; return (c8 * 0x01010101) >> 24; } /// /// Finds the number of set bits below the bit at `location` /// This function is used to find where in the array of entries or nodes /// the item should be inserted /// [MethodImpl(MethodImplOptions.AggressiveInlining)] static int Index(uint bits, int location) => BitCount((int)bits & (location - 1)); /// /// Finds the number of 1-bits below the bit at `location` /// This function is used to find where in the array of entries or nodes /// the item should be inserted /// [MethodImpl(MethodImplOptions.AggressiveInlining)] static int Index(uint bitmap, uint location) => BitCount((int)bitmap & ((int)location - 1)); /// /// Returns the value used to index into the bit vector /// [MethodImpl(MethodImplOptions.AggressiveInlining)] static uint Mask(int index) => (uint)(1 << index); /// /// Sets the item at index. If mutate is true it sets the /// value without copying the array, otherwise the operation is pure /// [MethodImpl(MethodImplOptions.AggressiveInlining)] static A[] SetItem(A[] items, int index, A value, bool mutate) { if (mutate) { items[index] = value; return items; } else { var nitems = new A[items.Length]; System.Array.Copy(items, nitems, items.Length); nitems[index] = value; return nitems; } } /// /// Clones part of an existing array /// [MethodImpl(MethodImplOptions.AggressiveInlining)] static A[] Clone(A[] items, int count) { var nitems = new A[count]; System.Array.Copy(items, nitems, count); return nitems; } /// /// Clones an existing array /// [MethodImpl(MethodImplOptions.AggressiveInlining)] static A[] Clone(A[] items) { var len = items.Length; var nitems = new A[len]; System.Array.Copy(items, nitems, len); return nitems; } /// /// Inserts a new item in the array (immutably) /// [MethodImpl(MethodImplOptions.AggressiveInlining)] static A[] Insert(A[] array, int index, A value) { var narray = new A[array.Length + 1]; System.Array.Copy(array, 0, narray, 0, index); System.Array.Copy(array, index, narray, index + 1, array.Length - index); narray[index] = value; return narray; } /// /// Returns a new array with the item at index removed /// [MethodImpl(MethodImplOptions.AggressiveInlining)] static A[] RemoveAt(A[] array, int index) { if (array.Length == 0) { return array; } var narray = new A[array.Length - 1]; if (index > 0) { System.Array.Copy(array, 0, narray, 0, index); } if (index + 1 < array.Length) { System.Array.Copy(array, index + 1, narray, index, array.Length - index - 1); } return narray; } public override string ToString() => count < 50 ? $"[{ string.Join(", ", AsEnumerable()) }]" : $"[{ string.Join(", ", AsEnumerable().Take(50)) } ... ]"; public bool TryGetValue(K key, out K value) { var ov = Find(key); if (ov.IsSome) { value = (K)ov; return true; } else { value = default!; return false; } } IEnumerator IEnumerable.GetEnumerator() => AsEnumerable().GetEnumerator(); } ================================================ FILE: LanguageExt.Core/LanguageExt.Core.csproj ================================================ TRACE;DEBUG 1701;1702;1705;IDE1006;CS1591;CS1573;CS1712;CS1711;CS1572;CS1587;CA2260 CONTRACTS_FULL net10.0 enable 5.0.0-beta-77 LanguageExt.Core LanguageExt.Core Paul Louth Functional language extensions for C# README.nuget.md Copyright (c) Paul Louth. All rights reserved. This framework uses and abuses the features of C# to provide a functional 'Base class library', that, if you squint, can look like extensions to the language itself. C#, Functional, Language Extension, Monad, Option, Either, Reader, Writer, State, List, Set, Map, Queue, Memo, Memoization, Immutable, Lambda, Pattern Matching, Tuple lang-ext-small.png https://github.com/louthy/language-ext MIT false bin\$(Configuration)\$(TargetFramework)\$(AssemblyName).xml library 5.0.0.0 5.0.0.0 default ================================================ FILE: LanguageExt.Core/Lens/Lens.Operators.cs ================================================ namespace LanguageExt; public static class LensExtensions { extension(Lens _) { public static Lens operator |(Lens lhs, Lens rhs) => Lens.New( Get: a => rhs.Get(lhs.Get(a)), Set: v => lhs.Update(rhs.SetF(v))); } } ================================================ FILE: LanguageExt.Core/Lens/Lens.cs ================================================ using System; using System.Collections.Generic; using System.Linq; namespace LanguageExt; public static class Lens { /// /// Pair two lenses /// public static Lens<(A, B), (C, D)> tuple(Lens First, Lens Second) => Lens<(A, B), (C, D)>.New( Get: a => (First.Get(a.Item1), Second.Get(a.Item2)), Set: v => a => (First.Set(v.Item1, a.Item1), Second.Set(v.Item2, a.Item2))); /// /// Triple three lenses /// public static Lens<(A, B, C), (D, E, F)> tuple(Lens First, Lens Second, Lens Third) => Lens<(A, B, C), (D, E, F)>.New( Get: a => (First.Get(a.Item1), Second.Get(a.Item2), Third.Get(a.Item3)), Set: v => a => (First.Set(v.Item1, a.Item1), Second.Set(v.Item2, a.Item2), Third.Set(v.Item3, a.Item3))); /// /// is applied to source. /// If true, is selected. /// If false, is selected. /// public static Lens cond(Func pred, Lens Then, Lens Else) { Lens choose(A a) => pred(a) ? Then : Else; return Lens.New( Get: a => choose(a).Get(a), Set: v => a => choose(a).Set(v, a)); } /// /// Gets/sets the fst element in a pair /// public static Lens<(A, B), A> fst() => Lens<(A, B), A>.New( Get: ab => ab.Item1, Set: a => ab => (a, ab.Item2)); /// /// Gets/sets the fst element in a triple /// public static Lens<(A, B, C), A> fst() => Lens<(A, B, C), A>.New( Get: ab => ab.Item1, Set: a => ab => (a, ab.Item2, ab.Item3)); /// /// Gets/sets the fst element in a quad /// public static Lens<(A, B, C, D), A> fst() => Lens<(A, B, C, D), A>.New( Get: ab => ab.Item1, Set: a => ab => (a, ab.Item2, ab.Item3, ab.Item4)); /// /// Gets/sets the snd element in a pair /// public static Lens<(A, B), B> snd() => Lens<(A, B), B>.New( Get: ab => ab.Item2, Set: b => ab => (ab.Item1, b)); /// /// Gets/sets the snd element in a pair /// public static Lens<(A, B, C), B> snd() => Lens<(A, B, C), B>.New( Get: ab => ab.Item2, Set: b => ab => (ab.Item1, b, ab.Item3)); /// /// Gets/sets the snd element in a pair /// public static Lens<(A, B, C, D), B> snd() => Lens<(A, B, C, D), B>.New( Get: ab => ab.Item2, Set: b => ab => (ab.Item1, b, ab.Item3, ab.Item4)); /// /// Gets/sets the thrd element in a pair /// public static Lens<(A, B, C), C> thrd() => Lens<(A, B, C), C>.New( Get: abc => abc.Item3, Set: c => abc => (abc.Item1, abc.Item2, c)); /// /// Gets/sets the thrd element in a pair /// public static Lens<(A, B, C, D), C> thrd() => Lens<(A, B, C, D), C>.New( Get: abc => abc.Item3, Set: c => abc => (abc.Item1, abc.Item2, c, abc.Item4)); /// /// Identity lens /// public static Lens identity() => Lens.New( Get: a => a, Set: a => _ => a); /// /// Creates a lens that maps the given lens in an enumerable /// public static Lens, IEnumerable> enumMap(Lens la) => Lens, IEnumerable>.New( Get: lst => lst.Select(la.Get), Set: val => lst => lst.Zip(val).Select(ab => la.Set(ab.Item2, ab.Item1))); /// /// Convert a Lens〈A, B〉 to a Prism〈A, B〉 /// public static Prism ToPrism(this Lens la) => Prism.New(la); /// /// Convert a Lens〈A, Option〈B〉〉 to a Prism〈A, B〉 /// public static Prism ToPrism(this Lens> la) => Prism.New(la); } ================================================ FILE: LanguageExt.Core/Lens/LensAB.cs ================================================ using System; namespace LanguageExt; /// /// Primitive lens type for creating well-behaved bidirectional transformations /// public readonly struct Lens { public readonly Func Get; public readonly Func> SetF; Lens(Func get, Func> set) { Get = get; SetF = set; } public A Set(B value, A cont) => SetF(value)(cont); public static Lens New(Func Get, Func> Set) => new (Get, Set); public Func Update(Func f) { var self = this; return a => self.Set(f(self.Get(a)), a); } public A Update(Func f, A value) => Set(f(Get(value)), value); } ================================================ FILE: LanguageExt.Core/Lens/Prelude.Lens.cs ================================================  namespace LanguageExt; public static partial class Prelude { /// /// Sequentially composes two lenses /// public static Lens lens(Lens la, Lens lb) => Lens.New( Get: a => lb.Get(la.Get(a)), Set: v => la.Update(lb.SetF(v))); /// /// Sequentially composes three lenses /// public static Lens lens(Lens la, Lens lb, Lens lc) => Lens.New( Get: a => lc.Get(lb.Get(la.Get(a))), Set: v => la.Update(lb.Update(lc.SetF(v)))); /// /// Sequentially composes four lenses /// public static Lens lens(Lens la, Lens lb, Lens lc, Lens ld) => Lens.New( Get: a => ld.Get(lc.Get(lb.Get(la.Get(a)))), Set: v => la.Update(lb.Update(lc.Update(ld.SetF(v))))); /// /// Sequentially composes five lenses /// public static Lens lens(Lens la, Lens lb, Lens lc, Lens ld, Lens le) => Lens.New( Get: a => le.Get(ld.Get(lc.Get(lb.Get(la.Get(a))))), Set: v => la.Update(lb.Update(lc.Update(ld.Update(le.SetF(v)))))); /// /// Sequentially composes six lenses /// public static Lens lens(Lens la, Lens lb, Lens lc, Lens ld, Lens le, Lens lf) => Lens.New( Get: a => lf.Get(le.Get(ld.Get(lc.Get(lb.Get(la.Get(a)))))), Set: v => la.Update(lb.Update(lc.Update(ld.Update(le.Update(lf.SetF(v))))))); /// /// Sequentially composes seven lenses /// public static Lens lens(Lens la, Lens lb, Lens lc, Lens ld, Lens le, Lens lf, Lens lg) => Lens.New( Get: a => lg.Get(lf.Get(le.Get(ld.Get(lc.Get(lb.Get(la.Get(a))))))), Set: v => la.Update(lb.Update(lc.Update(ld.Update(le.Update(lf.Update(lg.SetF(v)))))))); /// /// Sequentially composes eight lenses /// public static Lens lens(Lens la, Lens lb, Lens lc, Lens ld, Lens le, Lens lf, Lens lg, Lens lh) => Lens.New( Get: a => lh.Get(lg.Get(lf.Get(le.Get(ld.Get(lc.Get(lb.Get(la.Get(a)))))))), Set: v => la.Update(lb.Update(lc.Update(ld.Update(le.Update(lf.Update(lg.Update(lh.SetF(v))))))))); /// /// Sequentially composes nine lenses /// public static Lens lens(Lens la, Lens lb, Lens lc, Lens ld, Lens le, Lens lf, Lens lg, Lens lh, Lens li) => Lens.New( Get: a => li.Get(lh.Get(lg.Get(lf.Get(le.Get(ld.Get(lc.Get(lb.Get(la.Get(a))))))))), Set: v => la.Update(lb.Update(lc.Update(ld.Update(le.Update(lf.Update(lg.Update(lh.Update(li.SetF(v)))))))))); /// /// Sequentially composes ten lenses /// public static Lens lens(Lens la, Lens lb, Lens lc, Lens ld, Lens le, Lens lf, Lens lg, Lens lh, Lens li, Lens lj) => Lens.New( Get: a => lj.Get(li.Get(lh.Get(lg.Get(lf.Get(le.Get(ld.Get(lc.Get(lb.Get(la.Get(a)))))))))), Set: v => la.Update(lb.Update(lc.Update(ld.Update(le.Update(lf.Update(lg.Update(lh.Update(li.Update(lj.SetF(v))))))))))); } ================================================ FILE: LanguageExt.Core/Lens/README.md ================================================ Lenses allow you to look inside a structure to access its value and then subsequently update that value. But because we don't want to do mutation, the updaters create new versions of the underlying structure. ================================================ FILE: LanguageExt.Core/Memo/Extensions/Memo.Extensions.cs ================================================ using System; using System.Collections.Generic; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; public static partial class MemoExtensions { extension(Memo> self) { public Memo Lift() => (self.acquire, self.state) switch { (null, 2) => new Memo(self.acquire, self.value), (null, _) => new Memo(self.value!), _ => new Memo(self.acquire) }; } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Functor extensions // extension(Memo self) { public Memo MapM(Func, K> f) => new (() => f(self.Value)); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Functor extensions // extension(Memo self) where F : Natural { public Memo Transform() => new (() => F.Transform(self.Value)); } /// /// Returns a Func〈T〉 that wraps func. The first /// call to the resulting Func〈T〉 will cache the result. /// Subsequent calls return the cached item. /// public static Memo Memo(this Func func) => memo(func); /// /// Returns a Func〈T, R〉 that wraps func. Each time the resulting /// Func〈T, R〉 is called with a new value, its result is memoized (cached). /// Subsequent calls use the memoized value. /// /// Remarks: /// Thread-safe and memory-leak safe. /// public static Func Memo(this Func func) where T : notnull => memo(func); /// /// Returns a Func〈T, R〉 that wraps func. Each time the resulting /// Func〈T, R〉 is called with a new value, its result is memoized (cached). /// Subsequent calls use the memoized value. /// /// Remarks: /// No mechanism for freeing cached values and therefore can cause a /// memory leak when holding onto the Func〈T, R〉 reference. /// Uses a ConcurrentDictionary for the cache and is thread-safe /// public static Func MemoUnsafe(this Func func) where T : notnull => Prelude.memoUnsafe(func); /// /// Enumerable memoization. As an enumerable is enumerated each item is retained /// in an internal list, so that future evaluation of the enumerable isn't done. /// Only items not seen before are evaluated. /// /// This minimises one of the major problems with the IEnumerable / yield return /// pattern by causing at-most-once evaluation of each item. /// /// Use the IEnumerable extension method Memo for convenience. /// /// /// Although this allows efficient lazy evaluation, it does come at a memory cost. /// Each item is cached internally, so this method doesn't allow for evaluation of /// infinite sequences. /// /// Enumerable to memoize /// IEnumerable with caching properties public static IEnumerable Memo(this IEnumerable seq) => Prelude.memo(seq); } ================================================ FILE: LanguageExt.Core/Memo/Memo.F.cs ================================================ using System; using System.Threading; using LanguageExt.Traits; namespace LanguageExt; /// /// Makes `K〈F, A〉` lazy /// /// /// This is more of a utility type right now, so it hasn't been fleshed out like other Applicatives /// public class Memo { /// /// Acquired value or null if not yet acquired /// K? value; /// /// Function to acquire the value /// Func>? acquire; /// /// 0 = unloaded, 1 = loading, 2 = loaded /// int state; /// /// Construct a memo /// /// Value acquisition function internal Memo(Func> f) { value = null; acquire = f; state = 0; } /// /// Construct a memo /// /// Already acquired value internal Memo(Func>? f, K? fa) { value = fa; acquire = f; state = 2; } /// /// Construct a memo /// /// Already acquired value internal Memo(K fa) { value = fa; acquire = null; state = 2; } public Memo> Lower() => new (() => Value); /// /// Acquired value /// /// /// If the value is not yet acquired, it will be acquired within `Value` and cached. /// public K Value => state == 2 ? value! : GetValue(); /// /// Create a clone of the memo (without any cached results). This allows for reacquiring the value. /// /// A newly constructed memo structure public Memo Clone() => acquire is null ? new(value!) : new(acquire); /// /// Reset the memo. This clears the cached value. /// /// Unit public Unit Reset() { if(acquire is null) return default; SpinWait sw = default; while (true) { switch (Interlocked.CompareExchange(ref state, 1, 2)) { case 0: // unloaded return default; case 1: // currently unloading, so wait, then loop around and try again sw.SpinOnce(); break; case 2: value = null; state = 0; return default; default: throw new InvalidOperationException("Invalid state"); } } } /// /// Acquisition function. Called only if the value is not yet acquired. /// K GetValue() { SpinWait sw = default; while (true) { switch (Interlocked.CompareExchange(ref state, 1, 0)) { case 0: // value is not set yet, so try to set it try { value = acquire!(); state = 2; return value; } catch { // if we fail, reset the state to 'not set yet' state = 0; throw; } case 1: // currently loading, so wait, then loop around and try again sw.SpinOnce(); break; case 2: // value already set return value!; default: throw new InvalidOperationException("Invalid state"); } } } } ================================================ FILE: LanguageExt.Core/Memo/Memo.Module.cs ================================================ using LanguageExt.Traits; namespace LanguageExt; public static class Memo { /// /// Natural transformation for memoised higher-kinded structures /// /// Structure to transform /// Original structure /// New structure /// Bound value type /// Naturally transformed structure public static Memo transform(Memo ma) where F : Natural => new(() => F.Transform(ma.Value)); /// /// Natural transformation for memoised higher-kinded structures /// /// Structure to transform /// New structure /// Original structure /// Bound value type /// Naturally transformed structure public static Memo cotransform(Memo ma) where F : CoNatural => new(() => F.CoTransform(ma.Value)); } ================================================ FILE: LanguageExt.Core/Memo/Memo.cs ================================================ using System; using System.Threading; using LanguageExt.Traits; namespace LanguageExt; /// /// Lazily acquires a value and caches it for repeated use. /// public class Memo { /// /// Acquired value or null if not yet acquired /// internal A? value; /// /// Function to acquire the value /// internal Func? acquire; /// /// 0 = unloaded, 1 = loading, 2 = loaded /// internal int state; /// /// Construct a memo /// /// Value acquisition function internal Memo(A value) { this.value = value; acquire = null; state = 2; } /// /// Construct a memo /// /// Value acquisition function internal Memo(Func? acq, A? value) { this.value = value; this.acquire = acq; state = 2; } /// /// Construct a memo /// /// Value acquisition function internal Memo(Func acq) { value = default; acquire = acq; state = 0; } /// /// Functor map /// public Memo Map(Func f) => new (() => f(Value)); /// /// Acquired value /// /// /// If the value is not yet acquired, it will be acquired within `Value` and cached. /// public A Value => state == 2 ? value! : GetValue(); /// /// Create a clone of the memo (without any cached results). This allows for reacquiring the value. /// /// A newly constructed memo structure public Memo Clone() => acquire is null ? new Memo(value!) : new (acquire); /// /// Reset the memo. This clears the cached value. /// /// Unit public Unit Reset() { if(acquire == null) return default; SpinWait sw = default; while (true) { switch (Interlocked.CompareExchange(ref state, 1, 2)) { case 0: // unloaded return default; case 1: // currently unloading, so wait, then loop around and try again sw.SpinOnce(); break; case 2: value = default; state = 0; return default; default: throw new InvalidOperationException("Invalid state"); } } } /// /// Acquisition function. Called only if the value is not yet acquired. /// A GetValue() { SpinWait sw = default; while (true) { switch (Interlocked.CompareExchange(ref state, 1, 0)) { case 0: // value is not set yet, so try to set it try { value = acquire!(); state = 2; return value; } catch { // if we fail, reset the state to 'not set yet' state = 0; throw; } case 1: // currently loading, so wait, then loop around and try again sw.SpinOnce(); break; case 2: // value already set return value!; default: throw new InvalidOperationException("Invalid state"); } } } } ================================================ FILE: LanguageExt.Core/Memo/Operators/Memo.Operators.cs ================================================ using LanguageExt.Traits; namespace LanguageExt; public static partial class MemoExtensions { extension(Memo self) { /// /// Extract the cached value /// /// Memoisation structure /// Cached value public static K operator~(Memo ma) => ma.Value; } extension(Memo self) { /// /// Extract the cached value /// /// Memoisation structure /// Cached value public static A operator~(Memo ma) => ma.Value; } } ================================================ FILE: LanguageExt.Core/Memo/Prelude.Memoize.cs ================================================ using System; using System.Collections.Concurrent; using System.Collections.Generic; using LanguageExt.Traits; namespace LanguageExt; public static partial class Prelude { /// /// Returns a `Memo〈A〉` that wraps a function. The first /// call to the resulting `Memo〈A〉` will cache the result. /// Subsequent calls return the cached item. /// public static Memo memo(Func f) => new (f); /// /// Create a preloaded memo structure with a pure value. /// public static Memo memo(A value) => new (value); /// /// Returns a `Memo〈F, A〉` that wraps a function that yields a higher-kind `K〈F, A〉`. /// The first call to the resulting `Memo〈F, A〉` will cache the resulting `K〈F, A〉`. /// Subsequent calls return the cached item. /// /// /// NOTE: This does not invoke the `K〈F, A〉` computation, it merely caches the construction /// of the `K〈F, A〉`. If `K〈F, A〉` itself is lazy, then it can be invoked multiple times; /// this memoisation won't affect that. /// public static Memo memoK(Func> f) => new(f); /// /// Create a preloaded memo structure with a pure value /// public static Memo memoK(A value) where F : Applicative => new (F.Pure(value)); /// /// Create a preloaded memo structure. /// public static Memo memoK(K fa) where F : Applicative => new (fa); /// /// Returns a `Func〈A, B〉` that wraps func. Each time the resulting /// `Func〈A, B〉` is called with a new value, its result is memoized (cached). /// Subsequent calls use the memoized value. /// /// Remarks: /// Thread-safe and memory-leak safe. /// public static Func memo(Func func) where A : notnull { var cache = new WeakDict (); var syncMap = new ConcurrentDictionary(); return inp => { if(cache.TryGetValue(inp, out var x)) { return x; } else { B res; var sync = syncMap.GetOrAdd(inp, new object()); lock (sync) { res = cache.GetOrAdd(inp, func); } syncMap.TryRemove(inp, out sync); return res; } }; } /// /// Returns a Func〈T, R〉 that wraps func. Each time the resulting /// Func〈T, R〉 is called with a new value, its result is memoized (cached). /// Subsequent calls use the memoized value. /// /// Remarks: /// No mechanism for freeing cached values and therefore can cause a /// memory leak when holding onto the Func〈T, R〉 reference. /// Uses a ConcurrentDictionary for the cache and is thread-safe /// public static Func memoUnsafe(Func func) where T : notnull { var cache = new ConcurrentDictionary(); var syncMap = new ConcurrentDictionary(); return inp => { if (!cache.TryGetValue(inp, out var res)) { var sync = syncMap.GetOrAdd(inp, new object()); lock (sync) { res = cache.GetOrAdd(inp, func); } syncMap.TryRemove(inp, out sync); } return res; }; } /// /// Enumerable memoization. As an enumerable is enumerated each item is retained /// in an internal list, so that future evalation of the enumerable isn't done. /// Only items not seen before are evaluated. /// /// This minimises one of the major problems with the IEnumerable / yield return /// pattern by causing at-most-once evaluation of each item. /// /// Use the IEnumerable extension method Memo for convenience. /// /// /// Although this allows efficient lazy evaluation, it does come at a memory cost. /// Each item is cached internally, so this method doesn't allow for evaluation of /// infinite sequences. /// /// Enumerable to memoize /// IEnumerable with caching properties public static Seq memo(IEnumerable seq) => toSeq(seq); /// /// Used internally by the memo function. It wraps a concurrent dictionary that has /// its value objects wrapped in a WeakReference〈OnFinalise〈...〉〉 /// The OnFinalise type is a private class within WeakDict and does nothing but hold /// the value and an Action to call when its finalised. So when the WeakReference is /// collected by the GC, it forces the finaliser to be called on the OnFinalise object, /// which in turn executes the action which removes it from the ConcurrentDictionary. /// That means that both the key and value are collected when the GC fires rather than /// just the value. Mitigates memory leak of keys. /// private class WeakDict where T : notnull { private class OnFinalise(Action onFinalise, V value) { public readonly V Value = value; ~OnFinalise() => onFinalise.Invoke(); } ConcurrentDictionary>> dict = new ConcurrentDictionary>>(); private WeakReference> NewRef(T key, Func addFunc) => new (new OnFinalise(() => dict.TryRemove(key, out _), addFunc(key))); public bool TryGetValue(T key, out R value) { if(dict.TryGetValue(key, out var res) && res.TryGetTarget(out var target)) { value = target.Value; return true; } else { value = default!; return false; } } public R GetOrAdd(T key, Func addFunc) { var res = dict.GetOrAdd(key, _ => NewRef(key, addFunc)); if (res.TryGetTarget(out var target)) { return target.Value; } else { var upd = NewRef(key, addFunc); res = dict.AddOrUpdate(key, upd, (_, _) => upd); if (res.TryGetTarget(out target)) { return target.Value; } else { // This is a best guess of why the target can't be got. // It might not be the best approach, perhaps a retry, or a // better/more-descriptive exception. throw new OutOfMemoryException(); } } } } } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/ChronicleT/ChronicleT.Module.2.cs ================================================ using System; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; public partial class ChronicleT { /// /// `dictate` is an action that records the output `value`. /// Equivalent to `tell` for the `Writable` traits. /// /// Value to construct with /// Chronicle structure public static ChronicleT dictate(A value) => lift(That(value)); /// /// `confess` is an action that ends with a final output `value`. /// Equivalent to `fail` for the 'Fallible' trait. /// /// Value to construct with /// Chronicle structure public static ChronicleT confess(Ch value) => lift(This(value)); /// /// Construct a new chronicle with `this` and `that`. /// /// Value to construct with /// Value to construct with /// Chronicle structure public static ChronicleT chronicle(Ch @this, A that) => lift(Both(@this, that)); /// /// Construct a new chronicle with `these`. /// /// What to chronicle /// Chronicle structure public static ChronicleT lift(These these) => new(_ => M.Pure(these)); /// /// Lift a monad `M` into the monad-transformer /// /// Monad to lift /// Chronicle structure public static ChronicleT lift(K ma) => new(_ =>ma.Map(That)); /// /// Lift an `IO` monad into the monad-transformer /// /// Monad to lift /// Chronicle structure public static ChronicleT liftIO(K ma) => lift(M.LiftIOMaybe(ma)); /// /// `Memento` is an action that executes the action within this structure, returning either /// its record, if it ended with `Confess`, or its final value otherwise, with any record /// added to the current record. /// /// Similar to 'Catch' in the 'Fallible' trait, but with a notion of non-fatal errors (which /// are accumulated) vs. fatal errors (which are caught without accumulating). /// public static ChronicleT> memento(K, A> ma) => ma.As().Memento(); /// /// `absolve` is an action that executes this structure and discards any record it had. /// The `defaultValue` will be used if the action ended via `Confess`. /// /// public static ChronicleT absolve(A defaultValue, K, A> ma) => ma.As().Absolve(defaultValue); /// /// `Condemn` is an action that executes the structure and keeps its value /// only if it had no record. Otherwise, the value (if any) will be discarded /// and only the record kept. /// /// This can be seen as converting non-fatal errors into fatal ones. /// public static ChronicleT condemn(K, A> ma) => ma.As().Condemn(); /// /// An action that executes the structure and applies the function `f` to its output, leaving /// the return value unchanged.- /// /// /// Equivalent to `censor` for the 'Writable` trait. /// /// Censoring function public static ChronicleT censor(Func f, K, A> ma) => ma.As().Censor(f); /// /// Access to the internal semigroup instance. /// internal static readonly ChronicleT> semigroup = new (semi => M.Pure(That>(semi))); } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/ChronicleT/ChronicleT.Module.cs ================================================ using System; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; public static class ChronicleT { /// /// Monoid empty confession /// /// Chronicle type (a monoid) /// Lifted monad type /// Bound value type /// public static ChronicleT empty() where Ch : Monoid where M : Monad => confess(Ch.Empty); /// /// `dictate` is an action that records the output `value`. /// Equivalent to `tell` for the `Writable` traits. /// /// Value to construct with /// Chronicle structure public static ChronicleT dictate(A value) where M : Monad => new(_ => M.Pure(That(value))); /// /// `confess` is an action that ends with a final output `value`. /// Equivalent to `fail` for the 'Fallible' trait. /// /// Value to construct with /// Chronicle structure public static ChronicleT confess(Ch value) where M : Monad => new(_ => M.Pure(This(value))); /// /// Construct a new chronicle with `this` and `that`. /// /// Value to construct with /// Value to construct with /// Chronicle structure public static ChronicleT chronicle(Ch @this, A that) where M : Monad => new(_ => M.Pure(Both(@this, that))); /// /// Construct a new chronicle with `these`. /// /// What to chronicle /// Chronicle structure public static ChronicleT chronicle(These these) where M : Monad => new(_ => M.Pure(these)); /// /// Lift a monad `M` into the monad-transformer /// /// Monad to lift /// Chronicle structure public static ChronicleT lift(K ma) where M : Monad => new(_ =>ma.Map(That)); /// /// Lift an `IO` monad into the monad-transformer /// /// Monad to lift /// Chronicle structure public static ChronicleT liftIO(K ma) where M : Monad => lift(M.LiftIOMaybe(ma)); /// /// `Memento` is an action that executes the action within this structure, returning either /// its record, if it ended with `Confess`, or its final value otherwise, with any record /// added to the current record. /// /// Similar to 'Catch' in the 'Fallible' trait, but with a notion of non-fatal errors (which /// are accumulated) vs. fatal errors (which are caught without accumulating). /// public static ChronicleT> memento(K, A> ma) where M : Monad => ma.As().Memento(); /// /// `absolve` is an action that executes this structure and discards any record it had. /// The `defaultValue` will be used if the action ended via `Confess`. /// /// public static ChronicleT absolve(A defaultValue, K, A> ma) where M : Monad => ma.As().Absolve(defaultValue); /// /// `Condemn` is an action that executes the structure and keeps its value /// only if it had no record. Otherwise, the value (if any) will be discarded /// and only the record kept. /// /// This can be seen as converting non-fatal errors into fatal ones. /// public static ChronicleT condemn(K, A> ma) where M : Monad => ma.As().Condemn(); /// /// An action that executes the structure and applies the function `f` to its output, leaving /// the return value unchanged.- /// /// /// Equivalent to `censor` for the 'Writable` trait. /// /// Censoring function public static ChronicleT censor(Func f, K, A> ma) where M : Monad => ma.As().Censor(f); /// /// Access to the internal semigroup instance. /// internal static ChronicleT> semigroup() where M : Monad => ChronicleT.semigroup; } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/ChronicleT/ChronicleT.cs ================================================ using System; using LanguageExt.Traits; using static LanguageExt.Prelude; using NSE = System.NotSupportedException; namespace LanguageExt; /// /// The `ChronicleT` monad transformer. /// /// /// Hybrid error/writer monad class that allows both accumulating outputs and aborting computation with a final output. /// /// The expected use case is for computations with a notion of fatal vs. non-fatal errors. /// /// /// The 'pure' function produces a computation with no output, and `Bind` combines multiple /// outputs with semigroup combine. /// /// Composed monadic type /// Chronicle type /// Monad type /// Bound value type public record ChronicleT(Func, K>> runChronicleT) : K, A>, K, Ch, A> where M : Monad { /// /// Run the chronicle to yield its inner monad /// /// Semigroup combine operation public K> Run(SemigroupInstance trait) => runChronicleT(trait); /// /// Functor map operation /// /// Mapping function /// Resulting bound value type /// Mapped structure public ChronicleT Map(Func f) => new(c => runChronicleT(c).Map(these => these.Map(f))); /// /// Bifunctor map operation /// /// Chronicle mapping function /// Dictation mapping function /// Chronicle type to map to /// public ChronicleT BiMap(Func This, Func That) where Ch1 : Semigroup => Bifunctor.bimap(This, That, this).As2(); /// /// Functor map operation /// /// Mapping function /// Resulting bound value type /// Mapped structure public ChronicleT Select(Func f) => new(c => runChronicleT(c).Map(these => these.Map(f))); /// /// Monad bind operation /// /// Chaining function /// Chained structure public ChronicleT Bind(Func, B>> f) => new(t => Run(t) .Bind(cx => cx switch { These.This (var c) => M.Pure(This(c)), These.That (var x) => f(x).As().Run(t), These.Both (var c1, var x) => f(x).As() .Run(t) .Map(cy => cy switch { These.This (var c2) => This(t.Combine(c1, c2)), These.That (var b) => Both(c1, b), These.Both (var c2, var b) => Both(t.Combine(c1, c2), b), _ => throw new NSE() }), _ => throw new NSE() })); /// /// Monad bind operation /// /// Chaining function /// Chained structure public ChronicleT BindFirst(Func, Ch1, A>> f) where Ch1 : Semigroup => Bimonad.bindFirst(this, f).As2(); /// /// Monad bind operation /// /// Chaining function /// Chained structure public ChronicleT BindSecond(Func, Ch, B>> f) => Bimonad.bindSecond(this, f).As2(); /// /// Monad bind operation /// /// Chaining function /// Resulting bound value type /// Chained structure public ChronicleT SelectMany(Func, B>> bind, Func project) => Bind(x => bind(x).Map(y => project(x, y))); /// /// Monad bind operation /// /// Chaining function /// Resulting bound value type /// Chained structure public ChronicleT SelectMany(Func> bind, Func project) => Bind(x => ChronicleT.liftIO(bind(x)).Map(y => project(x, y))); /// /// Monad bind operation /// /// Chaining function /// Resulting bound value type /// Chained structure public ChronicleT SelectMany(Func> bind, Func project) => Bind(x => bind(x).ToChronicleT().Map(y => project(x, y))); /// /// `Memento` is an action that executes the action within this structure, returning either /// its record, if it ended with `Confess`, or its final value otherwise, with any record /// added to the current record. /// /// Similar to 'Catch' in the 'Fallible' trait, but with a notion of non-fatal errors (which /// are accumulated) vs. fatal errors (which are caught without accumulating). /// public ChronicleT> Memento() => new(combine => Run(combine).Map(these => these switch { These.This (var c) => That>(Either.Left(c)), These.That (var x) => That>(Either.Right(x)), These.Both (var c, var x) => Both(c, Either.Right(x)), _ => throw new NSE() })); /// /// `Absolve` is an action that executes this structure and discards any record it had. /// The `defaultValue` will be used if the action ended via `Confess`. /// /// public ChronicleT Absolve(A defaultValue) => new(combine => Run(combine).Map(these => these switch { These.This => That(defaultValue), These.That (var x) => That(x), These.Both (_, var x) => That(x), _ => throw new NSE() })); /// /// This can be seen as converting non-fatal errors into fatal ones. /// /// `Condemn` is an action that executes the structure and keeps its value /// only if it had no record. Otherwise, the value (if any) will be discarded /// and only the record kept. /// public ChronicleT Condemn() => new(combine => Run(combine).Map(these => these switch { These.This (var c) => This(c), These.That (var x) => That(x), These.Both (var c, _) => This(c), _ => throw new NSE() })); /// /// An action that executes the structure and applies the function `f` to its output, leaving /// the return value unchanged.- /// /// /// Equivalent to `censor` for the 'Writable` trait. /// /// Censoring function public ChronicleT Censor(Func f) => new(combine => Run(combine).Map(these => these switch { These.This (var c) => This(f(c)), These.That (var x) => That(x), These.Both (var c, var x) => Both(f(c), x), _ => throw new NSE() })); /// /// Coalescing operation /// public ChronicleT Choose(K, A> rhs) => Memento() .Bind(x => x switch { Either.Left => rhs, Either.Right(var r) => dictate(r), _ => throw new NSE() }); /// /// Coalescing operation /// public ChronicleT Choose(Memo, A> rhs) => Memento() .Bind(x => x switch { Either.Left => rhs.Value, Either.Right(var r) => dictate(r), _ => throw new NSE() }); /// /// Fallible error catching operation /// /// /// /// public ChronicleT Catch(Func Predicate, Func, A>> Fail) => Memento() .Bind(x => x switch { Either.Left (var e) => Predicate(e) ? Fail(e) : confess(e), Either.Right(var r) => dictate(r), _ => throw new NSE() }); } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/ChronicleT/Extensions/ChronicleT.Extensions.cs ================================================ using System; using System.Diagnostics.Contracts; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; public static partial class ChronicleTExtensions { /// /// Downcast operator /// [Pure] public static ChronicleT As(this K, A> ma) where M : Monad => (ChronicleT)ma; /// /// Downcast operator /// [Pure] public static ChronicleT As2(this K, Ch, A> ma) where M : Monad => (ChronicleT)ma; /// /// Run the chronicle to yield its inner monad /// [Pure] public static K> Run(this K, A> ma) where Ch : Semigroup where M : Monad => ma.As().Run(Ch.Instance); /// /// Monadic join /// [Pure] public static ChronicleT Flatten(this ChronicleT> mma) where M : Monad => mma.Bind(identity); /// /// Functor map operation /// /// Dictation mapping function /// Dictation type to map to /// [Pure] public static ChronicleT BiMap( this ChronicleT ma, Func f) where Ch : Semigroup where M : Monad => ma.As().Map(f); /// /// Bifunctor map operation /// /// Chronicle mapping function /// Dictation mapping function /// Chronicle type to map to /// Dictation type to map to /// [Pure] public static ChronicleT BiMap( this ChronicleT ma, Func This, Func That) where Ch : Semigroup where Ch1 : Semigroup where M : Monad => Bifunctor.bimap(This, That, ma).As2(); /// /// Filtering based on predicate. /// /// > /// If the predicate returns false, then `ChronicleT.empty()` is yielded and therefore `Ch` must be a monoid. /// [Pure] public static ChronicleT Where(this K, A> ma, Func pred) where Ch : Monoid where M : Monad => ma.Filter(pred); /// /// Filtering based on predicate. /// /// > /// If the predicate returns false, then `ChronicleT.empty()` is yielded and therefore `Ch` must be a monoid. /// [Pure] public static ChronicleT Filter(this K, A> ma, Func pred) where Ch : Monoid where M : Monad => ma.As().Bind(x => pred(x) ? ChronicleT.dictate(x) : ChronicleT.empty()); /// /// Monad bind operation /// /// Monadic bind function /// Projection function /// Source bound value type /// Intermediate bound value type /// Target bound value type /// `EitherT` [Pure] public static ChronicleT SelectMany( this K ma, Func, B>> bind, Func project) where Ch : Semigroup where M : Monad => ChronicleT.lift(ma).SelectMany(bind, project); /// /// Monad bind operation /// /// Monadic bind function /// Projection function /// Source bound value type /// Intermediate bound value type /// Target bound value type /// `EitherT` [Pure] public static ChronicleT SelectMany( this K ma, Func> bind, Func project) where Ch : Semigroup where M : Monad => ChronicleT.lift(ma).SelectMany(bind, project); } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/ChronicleT/Extensions/ChronicleT.Guard.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public static class ChronicleTGuardExtensions { /// /// Natural transformation to `ChronicleT` /// public static ChronicleT ToChronicleT(this Guard guard) where M : Monad => guard.Flag ? ChronicleT.dictate(default) : ChronicleT.confess(guard.OnFalse()); /// /// Monadic binding support for `ChronicleT` /// public static ChronicleT Bind( this Guard guard, Func> f) where M : Monad => guard.Flag ? f(default).As() : ChronicleT.confess(guard.OnFalse()); /// /// Monadic binding support for `ChronicleT` /// public static ChronicleT SelectMany( this Guard guard, Func> bind, Func project) where M : Monad => guard.Flag ? bind(default).Map(b => project(default, b)).As() : ChronicleT.confess(guard.OnFalse()); } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/ChronicleT/Operators/ChronicleT.Operators.Applicative.cs ================================================ using System; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; public static partial class ChronicleTExtensions { extension(K, A> self) where M : Monad { /// /// Applicative sequence operator /// public static ChronicleT operator >>> (K, A> ma, K, B> mb) => +ma.Action(mb); /// /// Applicative apply operator /// public static ChronicleT operator * (K, Func> mf, K, A> ma) => +mf.Apply(ma); /// /// Applicative apply operator /// public static ChronicleT operator * (K, A> ma, K, Func> mf) => +mf.Apply(ma); } extension(K, A> self) where M : Monad { /// /// Applicative apply operator /// public static ChronicleT> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static ChronicleT> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) where M : Monad { /// /// Applicative apply operator /// public static ChronicleT>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static ChronicleT>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) where M : Monad { /// /// Applicative apply operator /// public static ChronicleT>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static ChronicleT>>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) where M : Monad { /// /// Applicative apply operator /// public static ChronicleT>>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static ChronicleT>>>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) where M : Monad { /// /// Applicative apply operator /// public static ChronicleT>>>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static ChronicleT>>>>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) where M : Monad { /// /// Applicative apply operator /// public static ChronicleT>>>>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static ChronicleT>>>>>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) where M : Monad { /// /// Applicative apply operator /// public static ChronicleT>>>>>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static ChronicleT>>>>>>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) where M : Monad { /// /// Applicative apply operator /// public static ChronicleT>>>>>>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static ChronicleT>>>>>>>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) where M : Monad { /// /// Applicative apply operator /// public static ChronicleT>>>>>>>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static ChronicleT>>>>>>>>> operator *( K, A> ma, K, Func> mf) => curry * mf * ma; } } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/ChronicleT/Operators/ChronicleT.Operators.Choice.cs ================================================ using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt; public static partial class ChronicleTExtensions { extension(K, A> self) where M : Monad { public static ChronicleT operator |(K, A> lhs, K, A> rhs) => +lhs.Choose(rhs); public static ChronicleT operator |(K, A> lhs, Pure rhs) => +lhs.Choose(ChronicleT.chronicle(rhs.ToThese())); } } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/ChronicleT/Operators/ChronicleT.Operators.Fallible.cs ================================================ using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt; public static partial class ChronicleTExtensions { extension(K, A> self) where M : Monad { public static ChronicleT operator |(K, A> lhs, CatchM, A> rhs) => +lhs.Catch(rhs); public static ChronicleT operator |(K, A> lhs, Fail rhs) => +lhs.Catch(rhs); } } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/ChronicleT/Operators/ChronicleT.Operators.Final.cs ================================================ namespace LanguageExt.Traits; public static class ChronicleTExtensions { extension(K, A> _) where M : Monad, Final { /// /// Run a `finally` operation after the main operation regardless of whether it succeeds or not. /// /// Primary operation /// Finally operation /// Result of primary operation public static ChronicleT operator |(K, A> lhs, Finally rhs) => new (semi => lhs.As().runChronicleT(semi).Finally(rhs.Operation)); } } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/ChronicleT/Operators/ChronicleT.Operators.Functor.cs ================================================ using System; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; public static partial class ChronicleTExtensions { extension(K, A> _) where M : Monad { /// /// Functor map operator /// public static ChronicleT operator *(Func f, K, A> ma) => ma.Map(f).As(); /// /// Functor map operator /// public static ChronicleT operator *(K, A> ma, Func f) => ma.Map(f).As(); } extension(K, A> _) where M : Monad { /// /// Functor map operator /// public static ChronicleT> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static ChronicleT> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) where M : Monad { /// /// Functor map operator /// public static ChronicleT>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static ChronicleT>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) where M : Monad { /// /// Functor map operator /// public static ChronicleT>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static ChronicleT>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) where M : Monad { /// /// Functor map operator /// public static ChronicleT>>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static ChronicleT>>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) where M : Monad { /// /// Functor map operator /// public static ChronicleT>>>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static ChronicleT>>>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) where M : Monad { /// /// Functor map operator /// public static ChronicleT>>>>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static ChronicleT>>>>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) where M : Monad { /// /// Functor map operator /// public static ChronicleT>>>>>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static ChronicleT>>>>>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) where M : Monad { /// /// Functor map operator /// public static ChronicleT>>>>>>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static ChronicleT>>>>>>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) where M : Monad { /// /// Functor map operator /// public static ChronicleT>>>>>>>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static ChronicleT>>>>>>>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/ChronicleT/Operators/ChronicleT.Operators.Monad.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public static partial class ChronicleTExtensions { extension(K, A> self) where M : Monad { /// /// Monad bind operator /// /// Monad to bind /// Binding function /// Mapped monad public static ChronicleT operator >> (K, A> ma, Func, B>> f) => ma.Bind(f).As(); /// /// Sequentially compose two actions, discarding any value produced by the first, like sequencing operators (such /// as the semicolon) in C#. /// /// First action to run /// Second action to run /// Result of the second action public static ChronicleT operator >> (K, A> lhs, K, B> rhs) => lhs >> (_ => rhs); } extension(K, A> self) where M : MonadIO { /// /// Monad bind operator /// /// Monad to bind /// Binding function /// Mapped monad public static ChronicleT operator >> (K, A> ma, Func> f) => +ma.Bind(x => +f(x)); /// /// Sequentially compose two actions, discarding any value produced by the first, like sequencing operators (such /// as the semicolon) in C#. /// /// First action to run /// Second action to run /// Result of the second action public static ChronicleT operator >> (K, A> lhs, K rhs) => lhs >> (_ => rhs); } extension(K, A> self) where M : Monad { /// /// Sequentially compose two actions. The second action is a unit-returning action, so the result of the /// first action is propagated. /// /// First action to run /// Second action to run /// Result of the first action public static ChronicleT operator >> (K, A> lhs, K, Unit> rhs) => lhs >> (x => rhs * (_ => x)); } extension(K, A> self) where M : MonadIO { /// /// Sequentially compose two actions. The second action is a unit-returning action, so the result of the /// first action is propagated. /// /// First action to run /// Second action to run /// Result of the first action public static ChronicleT operator >> (K, A> lhs, K rhs) => lhs >> (x => rhs * (_ => x)); } } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/ChronicleT/Operators/ChronicleT.Operators.SemigroupK.cs ================================================ using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt; public static partial class ChronicleTExtensions { extension(K, A> self) where M : Monad, SemigroupK { /// /// Semigroup combine operator: an associative binary operation. /// /// Left-hand side operand /// Right-hand side operand /// public static ChronicleT operator +(K, A> lhs, K, A> rhs) => new (semi => lhs.As().runChronicleT(semi) + rhs.As().runChronicleT(semi)); } extension(K, A> self) where M : Monad, SemigroupK, Applicative { /// /// Semigroup combine operator: an associative binary operation. /// /// Left-hand side operand /// Right-hand side operand /// public static ChronicleT operator +(K, A> lhs, Pure rhs) => new(semi => lhs.As().runChronicleT(semi) + M.Pure(These.That(rhs.Value))); } extension(K, A> self) where M : Monad, SemigroupK, Fallible { /// /// Semigroup combine operator: an associative binary operation. /// /// Left-hand side operand /// Right-hand side operand /// public static ChronicleT operator +(K, A> lhs, Fail rhs) => new (semi => lhs.As().runChronicleT(semi) + M.Fail>(rhs.Value)); } } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/ChronicleT/Operators/ChronicleT.Operators.cs ================================================ using LanguageExt.Traits; namespace LanguageExt; public static partial class ChronicleTExtensions { extension(K, A> _) where M : Monad { /// /// Downcast operator /// public static ChronicleT operator +(K, A> ma) => (ChronicleT)ma; /// /// Downcast operator /// public static ChronicleT operator >> (K, A> ma, Lower lower) => +ma; } } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/ChronicleT/Prelude/ChronicleT.Prelude.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public partial class Prelude { /// /// `dictate` is an action that records the output `value`. /// Equivalent to `tell` for the `Writable` traits. /// /// /// This is 'Applicative.Pure'. /// /// Value to construct with /// Chronicle structure public static ChronicleT dictate(A value) where M : Monad => ChronicleT.dictate(value); /// /// `confess` is an action that ends with a final output `value`. /// Equivalent to `fail` for the 'Fallible' trait. /// /// /// This is akin to yielding an error. /// /// Value to construct with /// Chronicle structure public static ChronicleT confess(Ch value) where M : Monad => ChronicleT.confess(value); /// /// Construct a new chronicle with `this` and `that`. /// /// Value to construct with /// Value to construct with /// Chronicle structure public static ChronicleT chronicle(Ch @this, A that) where M : Monad => ChronicleT.chronicle(@this, that); /// /// Construct a new chronicle with `these`. /// /// What to chronicle /// Chronicle structure public static ChronicleT chronicle(These these) where M : Monad => ChronicleT.chronicle(these); /// /// `Memento` is an action that executes the action within this structure, returning either /// its record, if it ended with `Confess`, or its final value otherwise, with any record /// added to the current record. /// /// Similar to 'Catch' in the 'Fallible' trait, but with a notion of non-fatal errors (which /// are accumulated) vs. fatal errors (which are caught without accumulating). /// public static ChronicleT> memento(K, A> ma) where M : MonadIO => ma.As().Memento(); /// /// `absolve` is an action that executes this structure and discards any record it had. /// The `defaultValue` will be used if the action ended via `Confess`. /// /// public static ChronicleT absolve(A defaultValue, K, A> ma) where M : MonadIO => ma.As().Absolve(defaultValue); /// /// `Condemn` is an action that executes the structure and keeps its value /// only if it had no record. Otherwise, the value (if any) will be discarded /// and only the record kept. /// /// This can be seen as converting non-fatal errors into fatal ones. /// public static ChronicleT condemn(K, A> ma) where M : MonadIO => ma.As().Condemn(); /// /// An action that executes the structure and applies the function `f` to its output, leaving /// the return value unchanged.- /// /// /// Equivalent to `censor` for the 'Writable` trait. /// /// Censoring function public static ChronicleT censor(Func f, K, A> ma) where M : MonadIO => ma.As().Censor(f); } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/ChronicleT/Trait/ChronicleT.TraitImpl.2.cs ================================================ using System; using LanguageExt.Traits; using static LanguageExt.Prelude; using NSE = System.NotSupportedException; namespace LanguageExt; /// /// `ChronicleT` trait implementations /// /// Lifted monad type public class ChronicleT : CoproductK>, Bimonad> where M : Monad { static K, A, B> CoproductCons>.Left(A value) => ChronicleT.confess(value); static K, A, B> CoproductCons>.Right(B value) => ChronicleT.dictate(value); static K, A, C> CoproductK>.Match( Func Left, Func Right, K, A, B> fab) => fab.As2().Memento().Map(ea => ea.Match(Left, Right)); static K, Y, B> Bifunctor>.BiMap( Func first, Func second, K, X, A> fab) => new ChronicleT( _ => SemigroupInstance.Instance switch { { IsSome: true, Value: { } t } => fab.As2() .Run(t) .Map(ch => ch switch { These.This (var x) => This( first(x)), These.That (var a) => That( second(a)), These.Both (var x, var a) => Both( first(x), second(a)), _ => throw new NSE() }), _ => throw new NSE($"Type {typeof(X).Name} is not a valid semigroup") }); static K, Y, A> Bimonad>.BindFirst( K, X, A> ma, Func, Y, A>> f) => new ChronicleT(ty => SemigroupInstance.Instance switch { { IsSome: true, Value: { } tx } => ma.As2() .Run(tx) .Bind(ch => ch switch { These.This (var x) => f(x).As2().Run(ty), These.That (var a) => M.Pure( That(a)), These.Both (var x, var a) => f(x).As2().Run(ty).Map(ch1 => ch1 switch { These.This (var y1) => Both(y1, a), These.That (var a1) => That(a1), These.Both (var y1, var a1) => Both(y1, a1), _ => throw new NSE() }), _ => throw new NSE() }), _ => throw new NSE($"Type {typeof(X).Name} is not a valid semigroup") }); static K, X, B> Bimonad>.BindSecond( K, X, A> ma, Func, X, B>> f) => new ChronicleT( tx => ma.As2() .Run(tx) .Bind(ch => ch switch { These.This (var x) => M.Pure(This(x)), These.That (var a) => f(a).As2().Run(tx), These.Both (var x, var a) => f(a).As2().Run(tx).Map(ch1 => ch1 switch { These.This (var x1) => This(tx.Combine(x, x1)), These.That (var b1) => Both(x, b1), These.Both (var x1, var b1) => Both(tx.Combine(x, x1), b1), _ => throw new NSE() }), _ => throw new NSE() })); } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/ChronicleT/Trait/ChronicleT.TraitImpl.cs ================================================ using System; using System.Buffers; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; /// /// `ChronicleT` trait implementations /// /// Chronicle type (a semigroup) /// Lifted monad type public partial class ChronicleT : MonadT, M>, MonadIO>, Fallible>, Choice>, Chronicaler, Ch> where M : Monad { static K, B> Functor>.Map( Func f, K, A> ma) => ma.As().Map(f); static K, A> Applicative>.Pure(A value) => ChronicleT.dictate(value); static K, B> Applicative>.Apply( K, Func> mf, K, A> ma) { return new ChronicleT(t => Applicative.lift(apply(t.Combine), mf.As().Run(t), ma.As().Run(t))); static Func>, These, These> apply(Func combine) => (mf, mx) => mf.Apply(mx, combine); } static K, B> Applicative>.Apply( K, Func> mf, Memo, A> mma) { return new ChronicleT( semi => Applicative.lift( apply(semi.Combine), memoK(mf.As().Run(semi)), mma.Lower().Map(ma => ma.As().Run(semi)).Lift())); static Func>, These, These> apply(Func combine) => (mf, mx) => mf.Apply(mx, combine); } static K, B> Monad>.Bind( K, A> ma, Func, B>> f) => ma.As().Bind(f); static K, B> Monad>.Recur( A value, Func, Next>> f) => new ChronicleT(semi => M.Recur<(Ch? Chronicle, A Value), These>( (default, value), pair => f(pair.Value) .As() .runChronicleT(semi) .Map(e => e switch { These>.This(var ch) => Pure(These.This(ch)), These>.That({ IsDone: true } n) => Pure(These.That(n.Done)), These>.That({ IsLoop: true } n) => Loop((pair.Chronicle, n.Loop)), These>.Both(var ch, { IsDone: true } n) => Pure(These.Both(Combine(pair.Chronicle, ch, semi), n.Done)), These>.Both(var ch, { IsLoop: true } n) => Next.Loop<(Ch? Chronicle, A Value), These>((Combine(pair.Chronicle, ch, semi), n.Loop)), _ => throw new NotSupportedException() }))); static Ch Combine(Ch? fst, Ch snd, SemigroupInstance semi) => fst is null ? snd : semi.Combine(fst, snd); static K, A> MonadT, M>.Lift(K ma) => ChronicleT.lift(ma); static K, A> MonadIO>.LiftIO(IO ma) => ChronicleT.liftIO(ma); static K, A> Fallible>.Fail(Ch error) => ChronicleT.confess(error); static K, A> Fallible>.Catch( K, A> fa, Func Predicate, Func, A>> Fail) => fa.As().Catch(Predicate, Fail); static K, A> Choice>.Choose(K, A> lhs, K, A> rhs) => lhs.As().Choose(rhs); static K, A> Choice>.Choose(K, A> lhs, Memo, A> rhs) => lhs.As().Choose(rhs); static K, A> Chronicaler, Ch>.Dictate(A value) => ChronicleT.dictate(value); static K, A> Chronicaler, Ch>.Confess(Ch c) => ChronicleT.confess(c); static K, Either> Chronicaler, Ch>.Memento( K, A> ma) => ma.As().Memento(); static K, A> Chronicaler, Ch>.Absolve( A defaultValue, K, A> ma) => ma.As().Absolve(defaultValue); static K, A> Chronicaler, Ch>.Condemn(K, A> ma) => ma.As().Condemn(); static K, A> Chronicaler, Ch>.Censor( Func f, K, A> ma) => ma.As().Censor(f); static K, A> Chronicaler, Ch>.Chronicle(These ma) => ChronicleT.chronicle(ma); } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/Either/Either.Left.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics.Contracts; using LanguageExt.ClassInstances; namespace LanguageExt; public abstract partial record Either { /// /// Left case type /// /// Left value public sealed record Left(L Value) : Either { /// /// Is the Either in a Right state? /// [Pure] public override bool IsRight => false; /// /// Is the Either in a Left state? /// [Pure] public override bool IsLeft => true; /// /// Invokes the Right or Left function depending on the state of the Either /// /// Return type /// Function to invoke if in a Left state /// Function to invoke if in a Right state /// The return value of the invoked function [Pure] public override B Match(Func Left, Func Right) => Left(Value); /// /// Show the structure as a string /// [Pure] public override string ToString() => Value is null ? "Left(null)" : $"Left({Value})"; /// /// Get a hash code for the structure /// [Pure] public override int GetHashCode() => Value is null ? 0 : HashableDefault.GetHashCode(Value); /// /// Span of left value /// [Pure] public override ReadOnlySpan LeftSpan() => new([Value]); /// /// Empty span /// [Pure] public override ReadOnlySpan RightSpan() => ReadOnlySpan.Empty; /// /// Singleton enumerable if in a right state, otherwise empty enumerable /// [Pure] public override IEnumerable AsEnumerable() { yield break; } /// /// Compare this structure to another to find its relative ordering /// [Pure] public override int CompareTo(Either other) => other switch { Left l => OrdL.Compare(Value, l.Value), _ => -1 }; /// /// Equality override /// [Pure] public override bool Equals(Either other) => other switch { Left l => EqL.Equals(Value, l.Value), _ => false }; /// /// Unsafe access to the right-value /// /// internal override R RightValue => throw new EitherIsNotRightException(); /// /// Unsafe access to the left-value /// /// internal override L LeftValue => Value; /// /// Maps the value in the Either if it's in a Right state /// /// Left /// Right /// Mapped Either type /// Map function /// Mapped Either [Pure] public override Either Map(Func f) => new Either.Left(Value); /// /// Bi-maps the value in the Either if it's in a Right state /// /// Left /// Right /// Left return /// Right return /// Right map function /// Left map function /// Mapped Either [Pure] public override Either BiMap(Func Left, Func Right) => new Either.Left(Left(Value)); /// /// Monadic bind /// /// Left /// Right /// Resulting bound value /// Bind function /// Bound Either [Pure] public override Either Bind(Func> f) => new Either.Left(Value); /// /// Bi-bind. Allows mapping of both monad states /// [Pure] public override Either BiBind( Func> Left, Func> Right) => Left(Value); } } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/Either/Either.Module.cs ================================================ using System.Diagnostics.Contracts; namespace LanguageExt; public partial class Either { /// /// Construct an `Either` value in a `Right` state. /// /// /// `Right` is often synonymous with 'correct', or 'success', although that isn't a requirement for any reason other /// than the default monad bind behaviour. `Left` shortcuts and 'fails', whereas `Right` means we can successfully /// continue. /// /// Value to construct the `Right` state with /// Left value type /// Right value type /// Constructed `Either` value [Pure] public static Either Right(R value) => new Either.Right(value); /// /// Construct an `Either` value in a `Left` state. /// /// /// `Left` is often synonymous with 'failure', or 'error', although that isn't a requirement for any reason other /// than the default monad bind behaviour. `Left` shortcuts and 'fails', whereas `Right` means we can successfully /// continue. /// /// Value to construct the `Right` state with /// Left value type /// Right value type /// Constructed `Either` value [Pure] public static Either Left(L value) => new Either.Left(value); } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/Either/Either.Right.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics.Contracts; using LanguageExt.ClassInstances; namespace LanguageExt; public abstract partial record Either { /// /// Right case-type /// /// Right value public sealed record Right(R Value) : Either { /// /// Is the Either in a Right state? /// [Pure] public override bool IsRight => true; /// /// Is the Either in a Left state? /// [Pure] public override bool IsLeft => false; /// /// Invokes the Right or Left function depending on the state of the Either /// /// Return type /// Function to invoke if in a Left state /// Function to invoke if in a Right state /// The return value of the invoked function [Pure] public override B Match(Func Left, Func Right) => Right(Value); /// /// Show the structure as a string /// [Pure] public override string ToString() => Value is null ? "Right(null)" : $"Right({Value})"; /// /// Get a hash code for the structure /// [Pure] public override int GetHashCode() => Value is null ? 0 : HashableDefault.GetHashCode(Value); /// /// Empty span /// [Pure] public override ReadOnlySpan LeftSpan() => ReadOnlySpan.Empty; /// /// Span of right value /// [Pure] public override ReadOnlySpan RightSpan() => new([Value]); /// /// Singleton enumerable if in a right state, otherwise empty enumerable /// [Pure] public override IEnumerable AsEnumerable() { yield return Value; } /// /// Compare this structure to another to find its relative ordering /// [Pure] public override int CompareTo(Either other) => other switch { Right r => OrdR.Compare(Value, r.Value), _ => 1 }; /// /// Equality override /// [Pure] public override bool Equals(Either other) => other switch { Right r => EqR.Equals(Value, r.Value), _ => false }; /// /// Unsafe access to the right-value /// /// internal override R RightValue => Value; /// /// Unsafe access to the left-value /// /// internal override L LeftValue => throw new EitherIsNotLeftException(); /// /// Maps the value in the Either if it's in a Right state /// /// Left /// Right /// Mapped Either type /// Map function /// Mapped Either [Pure] public override Either Map(Func f) => new Either.Right(f(Value)); /// /// Bi-maps the value in the Either if it's in a Right state /// /// Left /// Right /// Left return /// Right return /// Right map function /// Left map function /// Mapped Either [Pure] public override Either BiMap(Func Left, Func Right) => new Either.Right(Right(Value)); /// /// Monadic bind /// /// Left /// Right /// Resulting bound value /// Bind function /// Bound Either [Pure] public override Either Bind(Func> f) => f(Value); /// /// Bi-bind. Allows mapping of both monad states /// [Pure] public override Either BiBind( Func> Left, Func> Right) => Right(Value); } } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/Either/Either.cs ================================================ using System; using System.Collections; using System.Collections.Generic; using static LanguageExt.Prelude; using System.Diagnostics.Contracts; using LanguageExt.ClassInstances; using System.Runtime.CompilerServices; using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt; /// /// Holds one of two values `Left` or `Right`. Usually, `Left` is considered _wrong_ or _in-error_, and /// `Right` is, well, right - as in correct. When the `Either` is in a `Left` state, it cancels /// computations like `bind` or `map`, etc. So, you can see `Left` as an _'early out, with a message'_. /// Unlike `Option` that has `None` as its alternative value (i.e. it has an _'early out, but no message'_). /// /// /// NOTE: If you use `Filter` or `Where` (or `where` in a LINQ expression) with `Either`, then the `Either` /// will be put into a `Bottom` state [if the predicate returns false]. When it's in this state it is /// neither `Right` nor `Left`. And any usage could trigger a `BottomException`. So be aware of the issue /// of filtering `Either`. /// /// Also note, when the `Either` is in a `Bottom` state, some operations on it will continue to give valid /// results or return another `Either` in the `Bottom` state and not throw. This is so a filtered `Either` /// doesn't needlessly break expressions and could be rescued into something useful via `Match`. /// /// Left /// Right public abstract partial record Either : IEither, IComparable, IComparable, IComparable>, IEquatable>, IEquatable, K, R>, K { /// /// Stop other types deriving from Either /// private Either() {} /// /// Is the Either in a Right state? /// public abstract bool IsRight { get; } /// /// Is the Either in a Left state? /// public abstract bool IsLeft { get; } /// /// Must exist here to make `operator true` work /// public static Either operator |(Either lhs, Either rhs) => lhs.Choose(rhs).As(); /// /// Explicit conversion operator from `Either` to `R` /// /// Value /// Value is not in a Right state [Pure] public static explicit operator R(Either ma) => ma.RightValue; /// /// Explicit conversion operator from `Either` to `L` /// /// Value /// Value is not in a Left state [Pure] public static explicit operator L(Either ma) => ma.LeftValue; /// /// Implicit conversion operator from R to Either R L /// /// Value [Pure] public static implicit operator Either(R value) => new Right(value); /// /// Implicit conversion operator from L to Either R L /// /// Value [Pure] public static implicit operator Either(L value) => new Left(value); /// /// Invokes the Right or Left function depending on the state of the Either /// /// Return type /// Function to invoke if in a Left state /// Function to invoke if in a Right state /// The return value of the invoked function [Pure] public abstract B Match(Func Left, Func Right); /// /// Invokes the Right or Left action depending on the state of the Either /// /// Action to invoke if in a Right state /// Action to invoke if in a Left state /// Unit public Unit Match(Action Left, Action Right) => Match(Left: l => fun(Left)(l), Right: r => fun(Right)(r)); /// /// Executes the Left function if the Either is in a Left state. /// Returns the Right value if the Either is in a Right state. /// /// Function to generate a Right value if in the Left state /// Returns an unwrapped Right value [Pure] public R IfLeft(Func Left) => IfLeft(_ => Left()); /// /// Executes the leftMap function if the Either is in a Left state. /// Returns the Right value if the Either is in a Right state. /// /// Function to generate a Right value if in the Left state /// Returns an unwrapped Right value [Pure] public R IfLeft(Func leftMap) => Match(Left: leftMap, Right: identity); /// /// Returns the rightValue if the Either is in a Left state. /// Returns the Right value if the Either is in a Right state. /// /// Value to return if in the Left state /// Returns an unwrapped Right value [Pure] public R IfLeft(R rightValue) => IfLeft(_ => rightValue); /// /// Executes the Left action if the Either is in a Left state. /// /// Function to generate a Right value if in the Left state /// Returns an unwrapped Right value public Unit IfLeft(Action Left) => Match(Left: Left, Right: _ => {}); /// /// Invokes the Right action if the Either is in a Right state, otherwise does nothing /// /// Action to invoke /// Unit public Unit IfRight(Action Right) => Match(Left: _ => { }, Right: Right); /// /// Returns the leftValue if the Either is in a Right state. /// Returns the Left value if the Either is in a Left state. /// /// Value to return if in the Left state /// Returns an unwrapped Left value [Pure] public L IfRight(L leftValue) => Match(Left: identity, Right: _ => leftValue); /// /// Returns the result of Right() if the Either is in a Right state. /// Returns the Left value if the Either is in a Left state. /// /// Function to generate a Left value if in the Right state /// Returns an unwrapped Left value [Pure] public L IfRight(Func Right) => Match(Left: identity, Right: _ => Right()); /// /// Returns the result of rightMap if the Either is in a Right state. /// Returns the Left value if the Either is in a Left state. /// /// Function to generate a Left value if in the Right state /// Returns an unwrapped Left value [Pure] public L IfRight(Func rightMap) => Match(Left: identity, Right: rightMap); [Pure] public int CompareTo(object? obj) => obj is Either t ? CompareTo(t) : 1; /// /// Span of left value /// [Pure] public abstract ReadOnlySpan LeftSpan(); /// /// Span of right value /// [Pure] public abstract ReadOnlySpan RightSpan(); /// /// Singleton enumerable if in a right state, otherwise empty. /// [Pure] public abstract IEnumerable AsEnumerable(); /// /// Singleton `Iterable` if in a right state, otherwise empty. /// [Pure] public Iterable ToIterable() => [..RightSpan()]; /// /// Project the Either into a Lst R /// /// If the Either is in a Right state, a Lst of R with one item. A zero length Lst R otherwise public Lst ToList() => new(RightSpan()); /// /// Project the Either into an ImmutableArray R /// /// If the Either is in a Right state, an immutable-array of R with one item. A zero-length array of R otherwise public Arr ToArray() => new(RightSpan()); /// /// Convert either to sequence of 0 or 1 right values /// [Pure] public Seq ToSeq() => new(RightSpan()); /// /// Convert the Either to an Option /// /// Some(Right) or None [Pure] public Option ToOption() => Match(Left: _ => None, Right: Some); /// /// Convert to an `Eff` /// /// Map the `Left` value to the`Fail` state of the `Eff` /// `Eff` monad [Pure] public Eff ToEff(Func Left) => Match(Left: e => Eff.Fail(Left(e)), Right: Eff.Pure); /// /// Convert to an Either transformer with embedded IO /// /// [Pure] public EitherT ToIO() => EitherT.lift(this); /// /// Comparison operator /// /// The left hand side of the operation /// The right hand side of the operation /// True if lhs〈 rhs [Pure] public static bool operator <(Either lhs, Fail rhs) => lhs.CompareTo(rhs) < 0; /// /// Comparison operator /// /// The left hand side of the operation /// The right hand side of the operation /// True if lhs〈 rhs [Pure] public static bool operator <=(Either lhs, Fail rhs) => lhs.CompareTo(rhs) <= 0; /// /// Comparison operator /// /// The left hand side of the operation /// The right hand side of the operation /// True if lhs 〉rhs [Pure] public static bool operator >(Either lhs, Fail rhs) => lhs.CompareTo(rhs) > 0; /// /// Comparison operator /// /// The left hand side of the operation /// The right hand side of the operation /// True if lhs >= rhs [Pure] public static bool operator >=(Either lhs, Fail rhs) => lhs.CompareTo(rhs) >= 0; /// /// Comparison operator /// /// The left hand side of the operation /// The right hand side of the operation /// True if lhs〈 rhs [Pure] public static bool operator <(Either lhs, Pure rhs) => lhs.CompareTo(rhs) < 0; /// /// Comparison operator /// /// The left hand side of the operation /// The right hand side of the operation /// True if lhs〈= rhs [Pure] public static bool operator <=(Either lhs, Pure rhs) => lhs.CompareTo(rhs) <= 0; /// /// Comparison operator /// /// The left hand side of the operation /// The right hand side of the operation /// True if lhs 〉rhs [Pure] public static bool operator >(Either lhs, Pure rhs) => lhs.CompareTo(rhs) > 0; /// /// Comparison operator /// /// The left hand side of the operation /// The right hand side of the operation /// True if lhs 〉= rhs [Pure] public static bool operator >=(Either lhs, Pure rhs) => lhs.CompareTo(rhs) >= 0; /// /// Comparison operator /// /// The left hand side of the operation /// The right hand side of the operation /// True if lhs〈 rhs [Pure] public static bool operator <(Fail lhs, Either rhs) => ((Either)lhs).CompareTo(rhs) < 0; /// /// Comparison operator /// /// The left hand side of the operation /// The right hand side of the operation /// True if lhs〈= rhs [Pure] public static bool operator <=(Fail lhs, Either rhs) => ((Either)lhs).CompareTo(rhs) <= 0; /// /// Comparison operator /// /// The left hand side of the operation /// The right hand side of the operation /// True if lhs 〉rhs [Pure] public static bool operator >(Fail lhs, Eitherrhs) => ((Either)lhs).CompareTo(rhs) > 0; /// /// Comparison operator /// /// The left hand side of the operation /// The right hand side of the operation /// True if lhs 〉= rhs [Pure] public static bool operator >=(Fail lhs, Either rhs) => ((Either)lhs).CompareTo(rhs) >= 0; /// /// Comparison operator /// /// The left hand side of the operation /// The right hand side of the operation /// True if lhs〈 rhs [Pure] public static bool operator <(Pure lhs, Either rhs) => ((Either)lhs).CompareTo(rhs) < 0; /// /// Comparison operator /// /// The left hand side of the operation /// The right hand side of the operation /// True if lhs〈= rhs [Pure] public static bool operator <=(Pure lhs, Either rhs) => ((Either)lhs).CompareTo(rhs) <= 0; /// /// Comparison operator /// /// The left hand side of the operation /// The right hand side of the operation /// True if lhs 〉rhs [Pure] public static bool operator >(Pure lhs, Either rhs) => ((Either)lhs).CompareTo(rhs) > 0; /// /// Comparison operator /// /// The left hand side of the operation /// The right hand side of the operation /// True if lhs 〉= rhs [Pure] public static bool operator >=(Pure lhs, Either rhs) => ((Either)lhs).CompareTo(rhs) >= 0; /// /// Comparison operator /// /// The left hand side of the operation /// The right hand side of the operation /// True if lhs〈 rhs [Pure] public static bool operator <(Either lhs, Either rhs) => lhs.CompareTo(rhs) < 0; /// /// Comparison operator /// /// The left hand side of the operation /// The right hand side of the operation /// True if lhs〈= rhs [Pure] public static bool operator <=(Either lhs, Either rhs) => lhs.CompareTo(rhs) <= 0; /// /// Comparison operator /// /// The left hand side of the operation /// The right hand side of the operation /// True if lhs 〉rhs [Pure] public static bool operator >(Either lhs, Either rhs) => lhs.CompareTo(rhs) > 0; /// /// Comparison operator /// /// The left hand side of the operation /// The right hand side of the operation /// True if lhs 〉= rhs [Pure] public static bool operator >=(Either lhs, Either rhs) => lhs.CompareTo(rhs) >= 0; /// /// Equality operator override /// [Pure] public static bool operator ==(Either lhs, Fail rhs) => lhs.Equals(rhs); /// /// Equality operator override /// [Pure] public static bool operator ==(Either lhs, Pure rhs) => lhs.Equals(rhs); /// /// Equality operator override /// [Pure] public static bool operator ==(Fail lhs, Either rhs) => ((Either)lhs).Equals(rhs); /// /// Equality operator override /// [Pure] public static bool operator ==(Pure lhs, Either rhs) => ((Either)lhs).Equals(rhs); /// /// Non-equality operator override /// [Pure] public static bool operator !=(Either lhs, Fail rhs) => !(lhs == rhs); /// /// Non-equality operator override /// [Pure] public static bool operator !=(Either lhs, Pure rhs) => !(lhs == rhs); /// /// Non-equality operator override /// [Pure] public static bool operator !=(Fail lhs, Either rhs) => !(lhs == rhs); /// /// Non-equality operator override /// [Pure] public static bool operator !=(Pure lhs, Either rhs) => !(lhs == rhs); /// /// Override of the True operator to return True if the Either is Right /// [Pure] public static bool operator true(Either value) => value is Right; /// /// Override of the False operator to return True if the Either is Left /// [Pure] public static bool operator false(Either value) => value is Left; /// /// CompareTo override /// [Pure] public int CompareTo(Either other) => CompareTo, OrdDefault>(other); /// /// CompareTo /// [Pure] public int CompareTo(Either other) where OrdR : Ord => CompareTo, OrdR>(other); /// /// CompareTo /// [Pure] public abstract int CompareTo(Either other) where OrdL : Ord where OrdR : Ord; /// /// CompareTo override /// [Pure] public int CompareTo(Fail other) => CompareTo((Either)other); /// /// CompareTo override /// [Pure] public int CompareTo(Pure other) => CompareTo((Either)other); /// /// CompareTo override /// [Pure] public int CompareTo(R? other) => other switch { null => 1, _ => CompareTo(Right(other)) }; /// /// CompareTo override /// [Pure] public int CompareTo(L? other) => other switch { null => 1, _ => CompareTo(Left(other)) }; /// /// Equality override /// [Pure] public bool Equals(R? other) => other is not null && Equals(Right(other)); /// /// Equality override /// [Pure] public bool Equals(L? other) => other is not null && Equals(Left(other)); /// /// Equality override /// [Pure] public virtual bool Equals(Either? other) => other is not null && Equals, EqDefault>(other); /// /// Equality override /// [Pure] public virtual bool Equals(Either other) where EqR : Eq => Equals, EqR>(other); /// /// Equality override /// [Pure] public abstract bool Equals(Either other) where EqL : Eq where EqR : Eq; [Pure] public abstract override int GetHashCode(); /// /// Equality override /// [Pure] public bool Equals(Fail other) => Equals((Either)other); /// /// Equality override /// [Pure] public bool Equals(Pure other) => Equals((Either)other); /// /// Match the Right and Left values but as objects. This can be useful to avoid reflection. /// [Pure] public Res MatchUntyped(Func Left, Func Right) => Match(Left: l => Left(l), Right: r => Right(r)); /// /// Find out the underlying Right type /// [Pure] public Type GetUnderlyingRightType() => typeof(R); /// /// Find out the underlying Left type /// [Pure] public Type GetUnderlyingLeftType() => typeof(L); /// /// Unsafe access to the right-value /// /// internal abstract R RightValue { get; } /// /// Unsafe access to the left-value /// /// internal abstract L LeftValue { get; } [Pure] public Type GetUnderlyingType() => typeof(R); /// /// Flips the left and right tagged values /// /// Either with the types swapped [Pure] public Either Swap() => Match(Left: Either.Right, Right: Either.Left); /// /// Invokes a predicate on the value of the Either if it's in the Right state /// /// Left /// Right /// Either to forall /// Predicate /// True if the Either is in a Left state. /// True if the Either is in a Right state and the predicate returns True. /// False otherwise. [Pure] public bool ForAll(Func Right) => Match(Left: _ => true, Right: Right); /// /// Invokes a predicate on the value of the Either if it's in the Right state /// /// Left /// Right /// Either to forall /// Predicate /// Predicate /// True if either Predicate returns true [Pure] public bool BiForAll(Func Left, Func Right) => Match(Left: Left, Right: Right); /// /// /// Either types are like lists of 0 or 1 items, and therefore follow the /// same rules when folding. /// /// In the case of lists, 'Fold', when applied to a binary /// operator, a starting value(typically the left-identity of the operator), /// and a list, reduces the list using the binary operator, from left to /// right: /// /// /// Aggregate state type /// Initial state /// Folder function, applied if structure is in a Right state /// The aggregate state [Pure] public S Fold(S state, Func Right) => Match(Left: _ => state, Right: curry(Right)(state)); /// /// /// Either types are like lists of 0 or 1 items, and therefore follow the /// same rules when folding. /// /// In the case of lists, 'Fold', when applied to a binary /// operator, a starting value(typically the left-identity of the operator), /// and a list, reduces the list using the binary operator, from left to /// right: /// /// /// Aggregate state type /// Initial state /// Folder function, applied if Either is in a Right state /// Folder function, applied if Either is in a Left state /// The aggregate state [Pure] public S BiFold(S state, Func Left, Func Right) => Match(Left: curry(Left)(state), Right: curry(Right)(state)); /// /// Invokes a predicate on the value of the Either if it's in the Right state /// /// Left /// Right /// Either to check existence of /// Predicate /// True if the Either is in a Right state and the predicate returns True. False otherwise. [Pure] public bool Exists(Func pred) => Match(Left: _ => false, Right: pred); /// /// Impure iteration of the bound values in the structure /// /// /// Returns the original unmodified structure /// public Either Do(Action f) => Map(r => { f(r); return r; }); /// /// Map each element of a structure to an action, evaluate these actions from /// left to right, and collect the results. /// /// /// Traversable structure /// Applicative functor trait /// Bound value (output) [Pure] public K> Traverse(Func> f) where F : Applicative => F.Map(x => x.As(), Traversable.traverse(f, this)); /// /// Maps the value in the Either if it's in a Right state /// /// Left /// Right /// Mapped Either type /// Map function /// Mapped Either [Pure] public abstract Either Map(Func f); /// /// Maps the value in the Either if it's in a Left state /// /// Left /// Right /// Mapped Either type /// Map function /// Mapped Either [Pure] public Either MapLeft(Func f) => BiMap(Left: f, Right: identity); /// /// Bi-maps the value in the Either if it's in a Right state /// /// Left /// Right /// Left return /// Right return /// Right map function /// Left map function /// Mapped Either [Pure] public abstract Either BiMap(Func Left, Func Right); /// /// Monadic bind /// /// Left /// Right /// Resulting bound value /// Bind function /// Bound Either [Pure] public abstract Either Bind(Func> f); /// /// Monadic bind /// /// Left /// Right /// /// /// Bound Either [Pure] public Either Bind(Func, B>> f) => Bind(x => (Either)f(x)); /// /// Bi-bind. Allows mapping of both monad states /// [Pure] public abstract Either BiBind(Func> Left, Func> Right); /// /// Bind left. Binds the left path of the monad only /// [Pure] public Either BindLeft(Func> f) => BiBind(f, Right); /// /// Maps the value in the Either if it's in a Right state /// /// Left /// Right /// Mapped Either type /// Map function /// Mapped Either [Pure] public Either Select(Func f) => Map(f); /// /// Monadic bind function /// [Pure] public Either SelectMany(Func> bind, Func project) => Bind(x => bind(x).Map(y => project(x, y))); /// /// Monadic bind function /// public Either SelectMany(Func> f) => Bind(a => f(a).ToEither()); /// /// Monadic bind function /// public Either SelectMany(Func> bind, Func project) => Bind(a => bind(a).ToEither().Map(_ => project(a, default))); //////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // `Pure` and `Fail` support // /// /// Monadic bind /// /// Bind function [Pure] public Either Bind(Func> f) => Bind(x => (Either)f(x)); /// /// Monadic bind /// /// Bind function [Pure] public Either Bind(Func> f) => Bind(x => (Either)f(x)); /// /// Monadic bind and project /// /// Bind function /// Project function [Pure] public Either SelectMany(Func> bind, Func project) => Bind(x => bind(x).Map(y => project(x, y))); /// /// Monadic bind and project /// /// Bind function /// Project function [Pure] public Either SelectMany(Func> bind, Func _) => Bind(x => bind(x).ToEither()); [Pure] public static implicit operator Either(Pure mr) => new Right(mr.Value); [Pure] public static implicit operator Either(Fail mr) => new Left(mr.Value); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Obsolete // /// /// Project the Either into a Lst R /// /// If the Either is in a Right state, a Lst of R with one item. A zero length Lst R otherwise [Pure] [Obsolete(Change.UseToListInstead)] public Lst RightToList() => ToList(); /// /// Project the Either into an ImmutableArray R /// /// If the Either is in a Right state, a ImmutableArray of R with one item. A zero length ImmutableArray of R otherwise [Pure] [Obsolete(Change.UseToArrayInstead)] public Arr RightToArray() => ToArray(); /// /// Convert either to sequence of 0 or 1 right values /// [Pure] [Obsolete(Change.UseToSeqInstead)] public Seq RightToSeq() => ToSeq(); } /// /// Context for the fluent Either matching /// public readonly struct EitherContext { readonly Either either; readonly Func rightHandler; internal EitherContext(Either either, Func rightHandler) { this.either = either; this.rightHandler = rightHandler; } /// /// Left match /// /// /// Result of the match [Pure] public Ret Left(Func left) => either.Match(left, rightHandler); } /// /// Context for the fluent Either matching /// public readonly struct EitherUnitContext { readonly Either either; readonly Action rightHandler; internal EitherUnitContext(Either either, Action rightHandler) { this.either = either; this.rightHandler = rightHandler; } public Unit Left(Action leftHandler) => either.Match(leftHandler, rightHandler); } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/Either/Extensions/Either.Extensions.Apply.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public static partial class EitherExtensions { /// /// Applicative action: runs the first applicative, ignores the result, and returns the second applicative /// public static Either Action(this Either ma, K, B> mb) => Applicative.action(ma, mb).As(); /// /// Applicative action: runs the first applicative, ignores the result, and returns the second applicative /// public static Either Action(this K, A> ma, K, B> mb) => Applicative.action(ma, mb).As(); /// /// Applicative functor apply operation /// /// /// Unwraps the value within the `ma` applicative-functor, passes it to the unwrapped function(s) within `mf`, and /// then takes the resulting value and wraps it back up into a new applicative-functor. /// /// Value(s) applicative functor /// Mapping function(s) /// Mapped applicative functor public static Either Apply(this Either> mf, K, A> ma) => Applicative.apply(mf, ma).As(); /// /// Applicative functor apply operation /// /// /// Unwraps the value within the `ma` applicative-functor, passes it to the unwrapped function(s) within `mf`, and /// then takes the resulting value and wraps it back up into a new applicative-functor. /// /// Value(s) applicative functor /// Mapping function(s) /// Mapped applicative functor public static Either Apply(this K, Func> mf, K, A> ma) => Applicative.apply(mf, ma).As(); } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/Either/Extensions/Either.Extensions.Map.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public static partial class EitherExtensions { /// /// Functor map operation /// /// /// Unwraps the value within the functor, passes it to the map function `f` provided, and /// then takes the mapped value and wraps it back up into a new functor. /// /// Functor to map /// Mapping function /// Mapped functor public static Either Map(this Func f, K, A> ma) => Functor.map(f, ma).As(); /// /// Functor map operation /// /// /// Unwraps the value within the functor, passes it to the map function `f` provided, and /// then takes the mapped value and wraps it back up into a new functor. /// /// Functor to map /// Mapping function /// Mapped functor public static Either Map(this Func f, Either ma) => Functor.map(f, ma).As(); } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/Either/Extensions/Either.Extensions.cs ================================================ using System; using static LanguageExt.Prelude; using System.Diagnostics.Contracts; using NSE = System.NotSupportedException; using LanguageExt.Traits; using LanguageExt.Common; namespace LanguageExt; /// /// Extension methods for Either /// public static partial class EitherExtensions { public static Either As(this K, R> ma) => (Either)ma; public static Either As2(this K ma) => (Either)ma; /// /// Match Right and return a context. You must follow this with .Left(...) to complete the match /// /// Action to invoke if the Either is in a Right state /// Context that must have Left() called upon it. [Pure] public static EitherUnitContext Right(this K, R> ma, Action right) => new (ma.As(), right); /// /// Match Right and return a context. You must follow this with .Left(...) to complete the match /// /// Action to invoke if the Either is in a Right state /// Context that must have Left() called upon it. [Pure] public static EitherContext Right(this K, R> ma, Func right) => new (ma.As(), right); /// /// Monadic join /// [Pure] public static Either Flatten(this K, Either> ma) => ma.As().Bind(x => x); /// /// Monadic join /// [Pure] public static Either Flatten(this K, K, R>> ma) => ma.As().Bind(x => x); /// /// Filtering based on predicate. /// /// > /// If the predicate returns false, then `Left(L.Empty)` is yielded and therefore `L` must be a monoid. /// [Pure] public static Either Where(this K, A> ma, Func pred) where L : Monoid => ma.Filter(pred); /// /// Filtering based on predicate. /// /// > /// If the predicate returns false, then `Left(L.Empty)` is yielded and therefore `L` must be a monoid. /// [Pure] public static Either Filter(this K, A> ma, Func pred) where L : Monoid => ma.As().Bind(x => pred(x) ? Either.Right(x) : Either.Left(L.Empty)); /* /// /// Partitions a foldable of `Either` into two sequences. /// /// All the `Left` elements are extracted, in order, to the first component of the output. /// Similarly, the `Right` elements are extracted to the second component of the output. /// /// A pair containing the sequences of partitioned values [Pure] public static (Seq Lefts, Seq Rights) Partition(this K> self) where F : Foldable => self.Fold((Left: Seq.Empty, Right: Seq.Empty), (s, ma) => ma switch { Either.Right (var r) => (s.Left, s.Right.Add(r)), Either.Left (var l) => (s.Left.Add(l), s.Right), _ => throw new NSE() }); /// /// Partitions a foldable of `Either` into two lists and returns the `Left` items only. /// /// A sequence of partitioned items [Pure] public static Seq Lefts(this K> self) where F : Foldable => self.Fold(Seq.Empty, (s, ma) => ma switch { Either.Left (var l) => s.Add(l), _ => throw new NSE() }); /// /// Partitions a foldable of `Either` into two lists and returns the `Right` items only. /// /// A sequence of partitioned items [Pure] public static Seq Rights(this K> self) where F : Foldable => self.Fold(Seq.Empty, (s, ma) => ma switch { Either.Right (var r) => s.Add(r), _ => throw new NSE() }); */ [Pure] public static Validation ToValidation(this Either ma) where L : Monoid => ma switch { Either.Right => Pure(ma.RightValue), Either.Left => Fail(ma.LeftValue), _ => throw new BottomException() }; /// /// Convert to an Eff /// /// Eff monad [Pure] public static Eff ToEff(this Either ma) => ma switch { Either.Right => Pure(ma.RightValue), Either.Left => Fail(ma.LeftValue), _ => throw new BottomException() }; /// /// Convert to an Eff /// /// Eff monad [Pure] public static Eff ToEff(this Either ma) => ma switch { Either.Right => Pure(ma.RightValue), Either.Left => Fail(ma.LeftValue), _ => throw new BottomException() }; /// /// Convert to an Eff /// /// Eff monad [Pure] public static Eff ToEff(this Either ma) => ma switch { Either.Right => Pure(ma.RightValue), Either.Left => Fail(Error.New(ma.LeftValue)), _ => throw new BottomException() }; } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/Either/Extensions/Either.Guard.cs ================================================ using System; namespace LanguageExt; public static class EitherGuardExtensions { /// /// Natural transformation to `Either` /// public static Either ToEither(this Guard guard) => guard.Flag ? new Either.Right(default) : new Either.Left(guard.OnFalse()); /// /// Monadic binding support for `Either` /// public static Either Bind( this Guard guard, Func> f) => guard.Flag ? f(default).As() : new Either.Left(guard.OnFalse()); /// /// Monadic binding support for `Either` /// public static Either SelectMany( this Guard guard, Func> bind, Func project) => guard.Flag ? bind(default).As().Map(b => project(default, b)) : new Either.Left(guard.OnFalse()); } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/Either/IEither.cs ================================================ using System; namespace LanguageExt; public interface IEither { bool IsRight { get; } bool IsLeft { get; } R MatchUntyped(Func Left, Func Right); Type GetUnderlyingRightType(); Type GetUnderlyingLeftType(); } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/Either/Operators/Either.Operators.Applicative.cs ================================================ using System; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; public static partial class EitherExtensions { extension(K, A> self) { /// /// Applicative sequence operator /// public static Either operator >>> (K, A> ma, K, B> mb) => ma.Action(mb).As(); /// /// Applicative apply operator /// public static Either operator * (K, Func> mf, K, A> ma) => mf.Apply(ma); /// /// Applicative apply operator /// public static Either operator * (K, A> ma, K, Func> mf) => mf.Apply(ma); } extension(K, A> self) { /// /// Applicative apply operator /// public static Either> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static Either> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) { /// /// Applicative apply operator /// public static Either>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static Either>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) { /// /// Applicative apply operator /// public static Either>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static Either>>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) { /// /// Applicative apply operator /// public static Either>>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static Either>>>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) { /// /// Applicative apply operator /// public static Either>>>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static Either>>>>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) { /// /// Applicative apply operator /// public static Either>>>>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static Either>>>>>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) { /// /// Applicative apply operator /// public static Either>>>>>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static Either>>>>>>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) { /// /// Applicative apply operator /// public static Either>>>>>>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static Either>>>>>>>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) { /// /// Applicative apply operator /// public static Either>>>>>>>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static Either>>>>>>>>> operator *( K, A> ma, K, Func> mf) => curry * mf * ma; } } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/Either/Operators/Either.Operators.Choice.cs ================================================ using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt; public static partial class EitherExtensions { extension(Either self) { public static Either operator |(Either lhs, Either rhs) => lhs.Choose(rhs).As(); public static Either operator |(Either lhs, Pure rhs) => lhs.Choose(rhs.ToEither()).As(); } extension(K, A> self) { public static Either operator |(K, A> lhs, K, A> rhs) => lhs.Choose(rhs).As(); public static Either operator |(K, A> lhs, Pure rhs) => lhs.Choose(rhs.ToEither()).As(); } } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/Either/Operators/Either.Operators.Fallible.cs ================================================ using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt; public static partial class EitherExtensions { extension(Either self) { public static Either operator |(Either lhs, CatchM, A> rhs) => +lhs.Catch(rhs); public static Either operator |(Either lhs, Fail rhs) => +lhs.Catch(rhs); } extension(K, A> self) { public static Either operator |(K, A> lhs, CatchM, A> rhs) => +lhs.Catch(rhs); public static Either operator |(K, A> lhs, Fail rhs) => +lhs.Catch(rhs); } } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/Either/Operators/Either.Operators.Functor.cs ================================================ using System; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; public static partial class EitherExtensions { extension(K, A> _) { /// /// Functor map operator /// public static Either operator *(Func f, K, A> ma) => ma.Map(f).As(); /// /// Functor map operator /// public static Either operator *(K, A> ma, Func f) => ma.Map(f).As(); } extension(K, A> _) { /// /// Functor map operator /// public static Either> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static Either> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) { /// /// Functor map operator /// public static Either>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static Either>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) { /// /// Functor map operator /// public static Either>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static Either>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) { /// /// Functor map operator /// public static Either>>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static Either>>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) { /// /// Functor map operator /// public static Either>>>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static Either>>>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) { /// /// Functor map operator /// public static Either>>>>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static Either>>>>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) { /// /// Functor map operator /// public static Either>>>>>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static Either>>>>>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) { /// /// Functor map operator /// public static Either>>>>>>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static Either>>>>>>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) { /// /// Functor map operator /// public static Either>>>>>>>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static Either>>>>>>>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/Either/Operators/Either.Operators.Monad.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public static partial class EitherExtensions { extension(K, A> self) { /// /// Monad bind operator /// /// Monad to bind /// Binding function /// Mapped monad public static Either operator >> (K, A> ma, Func, B>> f) => ma.Bind(f).As(); /// /// Sequentially compose two actions, discarding any value produced by the first, like sequencing operators (such /// as the semicolon) in C#. /// /// First action to run /// Second action to run /// Result of the second action public static Either operator >> (K, A> lhs, K, B> rhs) => lhs >> (_ => rhs); } extension(K, A> self) { /// /// Sequentially compose two actions. The second action is a unit-returning action, so the result of the /// first action is propagated. /// /// First action to run /// Second action to run /// Result of the first action public static Either operator >> (K, A> lhs, K, Unit> rhs) => lhs >> (x => (_ => x) * rhs); } } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/Either/Operators/Either.Operators.cs ================================================ using LanguageExt.Traits; namespace LanguageExt; public static partial class EitherExtensions { extension(K, A> _) { /// /// Downcast operator /// public static Either operator +(K, A> ma) => (Either)ma; /// /// Downcast operator /// public static Either operator >> (K, A> ma, Lower lower) => +ma; } } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/Either/Prelude/Either.Prelude.cs ================================================ using System.Diagnostics.Contracts; namespace LanguageExt; public static partial class Prelude { /// /// Either constructor /// Constructs an Either in a Right state /// /// Left /// Right /// Right value /// A new Either instance [Pure] public static Either Right(R value) => new Either.Right(value); /// /// Constructs an `Pure` which can be implicitly cast to an /// Either〈_, R〉 /// /// Right /// Right value /// A new EitherRight instance [Pure] public static Pure Right(R value) => new (value); /// /// Either constructor /// Constructs an Either in a Left state /// /// Left /// Right /// Left value /// A new Either instance [Pure] public static Either Left(L value) => new Either.Left(value); /// /// Constructs a `Fail` which can be implicitly cast to an /// Either〈L, _〉 /// /// Left /// Right value /// A new EitherLeft instance [Pure] public static Fail Left(L value) => new (value); } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/Either/Prelude/Either.Prelude.mapapply.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public static partial class Prelude { /// /// Functor map operation /// /// /// Unwraps the value within the functor, passes it to the map function `f` provided, and /// then takes the mapped value and wraps it back up into a new functor. /// /// Functor to map /// Mapping function /// Mapped functor public static Either map(Func f, K, A> ma) => Functor.map(f, ma).As(); /// /// Applicative action: runs the first applicative, ignores the result, and returns the second applicative /// public static Either action(K, A> ma, K, B> mb) => Applicative.action(ma, mb).As(); /// /// Applicative functor apply operation /// /// /// Unwraps the value within the `ma` applicative-functor, passes it to the unwrapped function(s) within `mf`, and /// then takes the resulting value and wraps it back up into a new applicative-functor. /// /// Value(s) applicative functor /// Mapping function(s) /// Mapped applicative functor public static Either apply(K, Func> mf, K, A> ma) => Applicative.apply(mf, ma).As(); } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/Either/README.md ================================================ `Either` monads support either a `L` (left) or a `R` (right) value. `L` is traditionally used as an error carrier, and any `Either` monad carrying an `L` will short-cut any bind operations and return the `L` (kinda like an `Exception`). However, they're not only for that, and can be used to carry an alternative value which could be mapped using `BiMap`, or `MapLeft`. You can construct an `Either` using the constructor functions in the `Prelude`: Either ma = Left("this is an error"); Either mb = Right(123); There are also convenience types called `Left` and `Right`, that don't need both generics providing. They can often make it a little easier to work with `Either`: Either ma = Left("this is an error"); Either mb = Right(123); It uses implicit casts to work out what the type should be. Note, if you're having trouble getting the types resolved, just specify the type parameters. ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/Either/Trait/Either.TraitImpl.2.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; /// /// Coproduct trait implementation for `Either〈L, R〉` /// public partial class Either : Coproduct, Bimonad { static K Bifunctor.BiMap( Func first, Func second, K fab) => fab switch { Either.Right(var value) => new Either.Right(second(value)), Either.Left(var value) => new Either.Left(first(value)), _ => throw new NotSupportedException() }; static K CoproductCons.Left(A value) => new Either.Left(value); static K CoproductCons.Right(B value) => new Either.Right(value); public static C Match( Func Left, Func Right, K fab) => fab switch { Either.Right(var value) => Right(value), Either.Left(var value) => Left(value), _ => throw new NotSupportedException() }; public static K BindFirst(K ma, Func> f) => ma switch { Either.Right (var a) => new Either.Right(a), Either.Left (var l) => f(l), _ => throw new NotSupportedException() }; public static K BindSecond(K ma, Func> f) => ma switch { Either.Right (var a) => f(a), Either.Left (var l) => new Either.Left(l), _ => throw new NotSupportedException() }; } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/Either/Trait/Either.TraitImpl.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; /// /// Monad trait implementation for `Either〈L, R〉` /// /// Left type parameter public class Either : Either, Monad>, Fallible>, Traversable>, Natural, Option>, Choice> { static K, B> Applicative>.Apply( K, Func> mf, K, A> ma) => (mf, ma) switch { (Either>.Right (var f), Either.Right (var a)) => Right(f(a)), (Either>.Left (var e1), _) => Left(e1), (_, Either.Left (var e2)) => Left(e2), _ => throw new NotSupportedException() }; static K, B> Applicative>.Apply( K, Func> mf, Memo, A> ma) => mf switch { Either>.Right => mf.Apply(ma.Value), Either>.Left (var e1) => Left(e1), _ => throw new NotSupportedException() }; static K, B> Monad>.Bind(K, A> ma, Func, B>> f) => ma switch { Either.Right (var r) => f(r), Either.Left (var l) => Left(l), _ => throw new NotSupportedException() }; static K, B> Monad>.Recur(A value, Func, Next>> f) { while (true) { var mr = +f(value); if (mr.IsLeft) return Either.Left(mr.LeftValue); var next = (Next)mr; if(next.IsDone) return Either.Right(next.Done); value = next.Loop; } } static K, A> Applicative>.Pure(A value) => new Either.Right(value); static K, B>> Traversable>.Traverse(Func> f, K, A> ta) => ta switch { Either.Right (var r) => F.Map(Right, f(r)), Either.Left (var l) => F.Pure(Left(l)), _ => throw new NotSupportedException() }; static S Foldable>.FoldWhile( Func> f, Func<(S State, A Value), bool> predicate, S state, K, A> ta) => ta switch { Either.Right (var r) => predicate((state, r)) ? f(r)(state) : state, _ => state }; static S Foldable>.FoldBackWhile( Func> f, Func<(S State, A Value), bool> predicate, S state, K, A> ta) => ta switch { Either.Right (var r) => predicate((state, r)) ? f(state)(r) : state, _ => state }; static K, B> Functor>.Map(Func f, K, A> ma) => ma switch { Either.Right (var r) => Right(f(r)), Either.Left (var l) => Left(l), _ => throw new NotSupportedException() }; static K, A> Right(A value) => new Either.Right(value); static K, A> Left(L value) => new Either.Left(value); static K, A> Choice>.Choose(K, A> ma, K, A> mb) => ma is Either.Right ? ma : mb; static K, A> Choice>.Choose(K, A> ma, Memo, A> mb) => ma is Either.Right ? ma : mb.Value; static K, A> Fallible>.Fail(L error) => new Either.Left(error); static K, A> Fallible>.Catch( K, A> fa, Func Predicate, Func, A>> Fail) => fa.As().BindLeft(l => Predicate(l) ? Fail(l).As() : Either.Left(l)); static K Natural, Option>.Transform(K, A> fa) => fa switch { Either.Right (var r) => Option.Some(r), _ => Option.None }; static Fold Foldable>.FoldStep(K, A> ta, S initialState) { var ma = ta.As(); return ma.IsRight ? Fold.Loop(initialState, ma.RightValue, Fold.Done) : Fold.Done(initialState); } static Fold Foldable>.FoldStepBack(K, A> ta, S initialState) => ta.FoldStep(initialState); } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/EitherT/EitherT.Module.cs ================================================ using LanguageExt.Traits; namespace LanguageExt; public class EitherT { /// /// Lift a pure value into the monad-transformer /// /// Value to lift /// `EitherT` public static EitherT Right(A value) where M : Monad => lift(M.Pure(value)); /// /// Lift a fail value into the monad-transformer /// /// Value to lift /// `EitherT` public static EitherT Left(L value) where M : Monad => lift(Either.Left(value)); /// /// Lifts a given `Either` value into the transformer /// /// Value to lift /// `EitherT` public static EitherT lift(Either value) where M : Monad => new(M.Pure(value)); /// /// Lifts a given monad into the transformer /// /// Monad to lift /// `EitherT` public static EitherT lift(K ma) where M : Monad => new(M.Map(Either.Right, ma)); /// /// Lifts a given monad into the transformer /// /// Monad to lift /// `EitherT` public static EitherT lift(K> ma) where M : Monad => new(ma); /// /// Lifts a given value into the transformer /// /// Value to lift /// `EitherT` public static EitherT lift(Pure ma) where M : Monad => Right(ma.Value); /// /// Lifts a given value into the transformer /// /// Value to lift /// `EitherT` public static EitherT lift(Fail ma) where M : Monad => lift(Either.Left(ma.Value)); /// /// Lifts a given monad into the transformer /// /// Monad to lift /// `EitherT` public static EitherT liftIO(K ma) where M : MonadIO => lift(M.LiftIO(ma)); /// /// Lifts a given monad into the transformer /// /// Monad to lift /// `EitherT` public static EitherT liftIO(IO> ma) where M : MonadIO => lift(M.LiftIO(ma)); /// /// Lifts a given monad into the transformer /// /// Monad to lift /// `EitherT` internal static EitherT liftIOMaybe(K ma) where M : Monad => lift(M.LiftIOMaybe(ma)); /// /// Lifts a given monad into the transformer /// /// Monad to lift /// `EitherT` internal static EitherT liftIOMaybe(IO> ma) where M : Monad => lift(M.LiftIOMaybe(ma)); } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/EitherT/EitherT.cs ================================================ using System; using System.Diagnostics.Contracts; using System.Runtime.CompilerServices; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; /// /// `EitherT` monad transformer, which allows for either an `L` or `R` result value to be carried. /// /// Given monad trait /// Left value type /// Bound value type public record EitherT(K> runEither) : K, R>, K, L, R> where M : Monad { /// /// Is the `EitherT` in a Right state? /// public K IsRight => Match(Left: _ => false, Right: _ => true); /// /// Is the `EitherT` in a Left state? /// public K IsLeft => Match(Left: _ => true, Right: _ => false); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Match // /// /// Invokes the Right or Left function depending on the state of the Either /// /// Return type /// Function to invoke if in a Left state /// Function to invoke if in a Right state /// The return value of the invoked function [Pure] public K Match(Func Left, Func Right) => M.Map(mx => mx.Match(Left: Left, Right: Right), runEither); /// /// Invokes the Right or Left action depending on the state of the Either /// /// Action to invoke if in a Right state /// Action to invoke if in a Left state /// Unit [Pure] public K Match(Action Left, Action Right) => M.Map(mx => mx.Match(Left: Left, Right: Right), runEither); /// /// Executes the Left function if the Either is in a Left state. /// Returns the Right value if the Either is in a Right state. /// /// Function to generate a Right value if in the Left state /// Returns an unwrapped Right value [Pure] public K IfLeft(Func Left) => IfLeft(_ => Left()); /// /// Executes the leftMap function if the Either is in a Left state. /// Returns the Right value if the Either is in a Right state. /// /// Function to generate a Right value if in the Left state /// Returns an unwrapped Right value [Pure] public K IfLeft(Func leftMap) => Match(Left: leftMap, Right: identity); /// /// Returns the rightValue if the Either is in a Left state. /// Returns the Right value if the Either is in a Right state. /// /// Value to return if in the Left state /// Returns an unwrapped Right value [Pure] public K IfLeft(R rightValue) => IfLeft(_ => rightValue); /// /// Executes the Left action if the Either is in a Left state. /// /// Function to generate a Right value if in the Left state /// Returns an unwrapped Right value public K IfLeft(Action Left) => Match(Left: Left, Right: _ => {}); /// /// Invokes the Right action if the Either is in a Right state, otherwise does nothing /// /// Action to invoke /// Unit public K IfRight(Action Right) => Match(Left: _ => { }, Right: Right); /// /// Returns the leftValue if the Either is in a Right state. /// Returns the Left value if the Either is in a Left state. /// /// Value to return if in the Left state /// Returns an unwrapped Left value [Pure] public K IfRight(L leftValue) => Match(Left: identity, Right: _ => leftValue); /// /// Returns the result of Right() if the Either is in a Right state. /// Returns the Left value if the Either is in a Left state. /// /// Function to generate a Left value if in the Right state /// Returns an unwrapped Left value [Pure] public K IfRight(Func Right) => Match(Left: identity, Right: _ => Right()); /// /// Returns the result of rightMap if the Either is in a Right state. /// Returns the Left value if the Either is in a Left state. /// /// Function to generate a Left value if in the Right state /// Returns an unwrapped Left value [Pure] public K IfRight(Func rightMap) => Match(Left: identity, Right: rightMap); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Map // /// /// Maps the bound monad /// /// Mapping function /// Target monad type /// Target bound value type /// Mapped monad public EitherT MapT(Func>, K>> f) where M1 : Monad => new (f(runEither)); /// /// Maps the given monad /// /// Mapping function public EitherT MapM(Func, K> f) => new(runEither .Bind(fv => fv switch { Either.Right (var v) => f(M.Pure(v)).Map(Either.Right), Either.Left (var e) => M.Pure>(e), _ => throw new NotSupportedException() })); /// /// Maps the bound value /// /// Mapping function /// Target bound value type /// Mapped structure public EitherT Map(Func f) => new(M.Map(mx => mx.Map(f), runEither)); /// /// Maps the bound value /// /// Mapping transducer /// Target bound value type /// Mapped structure public EitherT Select(Func f) => new(M.Map(mx => mx.Map(f), runEither)); /// /// Maps the left value /// /// Mapping function /// Target bound value type /// Mapped structure public EitherT MapLeft(Func f) => new(M.Map(mx => mx.MapLeft(f), runEither)); /// /// Bifunctor map operation /// /// Left map function /// Right map function /// Target left type /// Target right type /// Mapped structure public EitherT BiMap( Func Left, Func Right) => Bifunctor.bimap(Left, Right, this).As2(); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Bind // /// /// Monad bind operation /// /// Mapping function /// Target bound value type /// `EitherT` public EitherT Bind(Func, B>> f) => Bind(x => f(x).As()); /// /// Monad bind operation /// /// Mapping function /// Target bound value type /// `EitherT` public EitherT Bind(Func> f) => Bind(x => EitherT.lift(f(x))); /// /// Monad bind operation /// /// Mapping function /// Target bound value type /// `EitherT` public EitherT Bind(Func> f) => new(M.Bind(runEither, ex => ex.Match( Right: x => f(x).runEither, Left: e => M.Pure(Either.Left(e))))); /// /// Monad bind operation /// /// Mapping function /// Target bound value type /// `EitherT` public EitherT Bind(Func> f) => Map(a => f(a).Value); /// /// Monad bind operation /// /// Mapping function /// Target bound value type /// `EitherT` public EitherT Bind(Func> f) => Bind(a => EitherT.lift(f(a).Value)); /// /// Bimonad bind left /// /// /// /// public EitherT BindLeft(Func, L1, R>> f) => Bimonad.bindFirst(this, f).As2(); /// /// Bimonad bind right /// /// /// /// public EitherT BindRight(Func, L, R1>> f) => Bimonad.bindSecond(this, f).As2(); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // SelectMany // /// /// Monad bind operation /// /// Monadic bind function /// Projection function /// Intermediate bound value type /// Target bound value type /// `EitherT` public EitherT SelectMany(Func, B>> bind, Func project) => SelectMany(x => bind(x).As(), project); /// /// Monad bind operation /// /// Monadic bind function /// Projection function /// Intermediate bound value type /// Target bound value type /// `EitherT` public EitherT SelectMany(Func> bind, Func project) => Bind(x => bind(x).Map(y => project(x, y))); /// /// Monad bind operation /// /// Monadic bind function /// Projection function /// Intermediate bound value type /// Target bound value type /// `EitherT` public EitherT SelectMany(Func> bind, Func project) => SelectMany(x => EitherT.lift(bind(x)), project); /// /// Monad bind operation /// /// Monadic bind function /// Projection function /// Intermediate bound value type /// Target bound value type /// `EitherT` public EitherT SelectMany(Func> bind, Func project) => SelectMany(x => EitherT.lift(bind(x)), project); /// /// Monad bind operation /// /// Monadic bind function /// Projection function /// Intermediate bound value type /// Target bound value type /// `EitherT` public EitherT SelectMany(Func> bind, Func project) => Map(x => project(x, bind(x).Value)); /// /// Monad bind operation /// /// Monadic bind function /// Projection function /// Intermediate bound value type /// Target bound value type /// `EitherT` public EitherT SelectMany(Func> bind, Func project) => SelectMany(x => M.LiftIOMaybe(bind(x)), project); /// /// Monad bind operation /// /// Monadic bind function /// Projection function /// Intermediate bound value type /// Target bound value type /// `EitherT` public EitherT SelectMany(Func> bind, Func project) => SelectMany(x => bind(x).ToEither(), project); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Operators // public static implicit operator EitherT(Either ma) => EitherT.lift(ma); public static implicit operator EitherT(Pure ma) => EitherT.Right(ma.Value); public static implicit operator EitherT(Fail ma) => EitherT.lift(ma); public static implicit operator EitherT(L fail) => EitherT.Left(fail); public static implicit operator EitherT(IO ma) => EitherT.liftIOMaybe(ma); public static implicit operator EitherT(Lift ma) => EitherT.liftIOMaybe(ma.ToIO()); public static implicit operator EitherT(Lift ma) => EitherT.liftIOMaybe(ma.ToIO()); public static implicit operator EitherT(IO> ma) => EitherT.liftIOMaybe(ma); public OptionT ToOption() => new(runEither.Map(ma => ma.ToOption())); } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/EitherT/Extensions/EitherT.Extensions.Apply.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public static partial class EitherTExtensions { /// /// Applicative action: runs the first applicative, ignores the result, and returns the second applicative /// public static EitherT Action(this EitherT ma, K, B> mb) where M : Monad => Applicative.action(ma, mb).As(); /// /// Applicative action: runs the first applicative, ignores the result, and returns the second applicative /// public static EitherT Action(this K, A> ma, K, B> mb) where M : Monad => Applicative.action(ma, mb).As(); /// /// Applicative functor apply operation /// /// /// Unwraps the value within the `ma` applicative-functor, passes it to the unwrapped function(s) within `mf`, and /// then takes the resulting value and wraps it back up into a new applicative-functor. /// /// Value(s) applicative functor /// Mapping function(s) /// Mapped applicative functor public static EitherT Apply(this EitherT> mf, K, A> ma) where M : Monad => Applicative.apply(mf, ma).As(); /// /// Applicative functor apply operation /// /// /// Unwraps the value within the `ma` applicative-functor, passes it to the unwrapped function(s) within `mf`, and /// then takes the resulting value and wraps it back up into a new applicative-functor. /// /// Value(s) applicative functor /// Mapping function(s) /// Mapped applicative functor public static EitherT Apply(this K, Func> mf, K, A> ma) where M : Monad => Applicative.apply(mf, ma).As(); } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/EitherT/Extensions/EitherT.Extensions.Map.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public static partial class EitherTExtensions { /// /// Functor map operation /// /// /// Unwraps the value within the functor, passes it to the map function `f` provided, and /// then takes the mapped value and wraps it back up into a new functor. /// /// Functor to map /// Mapping function /// Mapped functor public static EitherT Map(this Func f, K, A> ma) where M : Monad => Functor.map(f, ma).As(); /// /// Functor map operation /// /// /// Unwraps the value within the functor, passes it to the map function `f` provided, and /// then takes the mapped value and wraps it back up into a new functor. /// /// Functor to map /// Mapping function /// Mapped functor public static EitherT Map(this Func f, EitherT ma) where M : Monad => Functor.map(f, ma).As(); } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/EitherT/Extensions/EitherT.Extensions.cs ================================================ using System; using static LanguageExt.Prelude; using System.Diagnostics.Contracts; using System.Threading.Tasks; using LanguageExt.Common; using LanguageExt.Traits; using NSE = System.NotSupportedException; namespace LanguageExt; /// /// EitherT monad-transformer extensions /// public static partial class EitherTExtensions { public static EitherT As(this K, R> ma) where M : Monad => (EitherT)ma; public static EitherT As2(this K, L, R> ma) where M : Monad => (EitherT)ma; public static FinT ToFin(this K, R> ma) where M : Monad => new(ma.As().runEither.Map(ma => ma.ToFin())); /// /// Runs the EitherT exposing the outer monad with an inner wrapped `Either` /// public static K> Run(this K, A> ma) where M : Monad => ma.As().runEither; /// /// Monad bind operation /// /// Mapping function /// Target bound value type /// `EitherT` public static EitherT Bind(this K, A> ma, Func> f) where M : MonadIO => ma.As().Bind(a => EitherT.liftIO(f(a))); /// /// Get the outer task and wrap it up in a new IO within the EitherT IO /// public static EitherT Flatten(this Task> tma) => EitherT .lift>(IO.liftAsync(async () => await tma.ConfigureAwait(false))) .Flatten(); /// /// Lift the task /// public static EitherT ToIO(this Task> ma) => liftIO(ma); /// /// Monadic join /// [Pure] public static EitherT Flatten(this EitherT> mma) where M : Monad => mma.Bind(identity); /// /// Filtering based on predicate. /// /// > /// If the predicate returns false, then `Left(L.Empty)` is yielded and therefore `L` must be a monoid. /// [Pure] public static EitherT Where(this K, A> ma, Func pred) where L : Monoid where M : Monad => ma.Filter(pred); /// /// Filtering based on predicate. /// /// > /// If the predicate returns false, then `Left(L.Empty)` is yielded and therefore `L` must be a monoid. /// [Pure] public static EitherT Filter(this K, A> ma, Func pred) where L : Monoid where M : Monad => ma.As().Bind(x => pred(x) ? EitherT.Right(x) : EitherT.Left(L.Empty)); /// /// Monad bind operation /// /// Monadic bind function /// Projection function /// Intermediate bound value type /// Target bound value type /// `EitherT` [Pure] public static EitherT SelectMany( this K ma, Func, B>> bind, Func project) where M : Monad => EitherT.lift(ma).SelectMany(bind, project); /// /// Monad bind operation /// /// Monadic bind function /// Projection function /// Intermediate bound value type /// Target bound value type /// `EitherT` [Pure] public static EitherT SelectMany( this K ma, Func> bind, Func project) where M : Monad => EitherT.lift(ma).SelectMany(bind, project); /// /// Partitions a foldable of `EitherT` into two sequences. /// /// All the `Left` elements are extracted, in order, to the first component of the output. /// Similarly, the `Right` elements are extracted to the second component of the output. /// /// A pair containing the sequences of partitioned values [Pure] public static K Lefts, Seq Rights)> Partition(this K> self) where F : Foldable where M : Monad => self.Fold(M.Pure((Left: Seq.Empty, Right: Seq.Empty)), (ms, ma) => ms.Bind(s => ma.Run().Map(a => a switch { Either.Right (var r) => (s.Left, s.Right.Add(r)), Either.Left (var l) => (s.Left.Add(l), s.Right), _ => throw new NSE() }))); /// /// Partitions a foldable of `EitherT` into two lists and returns the `Left` items only. /// /// A sequence of partitioned items [Pure] public static K> Lefts(this K> self) where F : Foldable where M : Monad => self.Fold(M.Pure(Seq.Empty), (ms, ma) => ms.Bind(s => ma.Run().Map(a => a switch { Either.Left (var l) => s.Add(l), _ => throw new NSE() }))); /// /// Partitions a foldable of `EitherT` into two lists and returns the `Right` items only. /// /// A sequence of partitioned items [Pure] public static K> Rights(this K> self) where F : Foldable where M : Monad => self.Fold(M.Pure(Seq.Empty), (ms, ma) => ms.Bind(s => ma.Run().Map(a => a switch { Either.Right (var r) => s.Add(r), _ => throw new NSE() }))); } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/EitherT/Extensions/EitherT.Guard.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public static class EitherTGuardExtensions { /// /// Natural transformation to `EitherT` /// public static EitherT ToEitherT(this Guard guard) where M : Monad => guard.Flag ? EitherT.Right(default) : EitherT.Left(guard.OnFalse()); /// /// Monadic binding support for `EitherT` /// public static EitherT Bind( this Guard guard, Func> f) where M : Monad => guard.Flag ? f(default).As() : EitherT.Left(guard.OnFalse()); /// /// Monadic binding support for `EitherT` /// public static EitherT SelectMany( this Guard guard, Func> bind, Func project) where M : Monad => guard.Flag ? bind(default).As().Map(b => project(default, b)) : EitherT.Left(guard.OnFalse()); } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/EitherT/Operators/EitherT.Operators.Applicative.cs ================================================ using System; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; public static partial class EitherTExtensions { extension(K, A> self) where M : Monad { /// /// Applicative sequence operator /// public static EitherT operator >>> (K, A> ma, K, B> mb) => ma.Action(mb); /// /// Applicative apply operator /// public static EitherT operator * (K, Func> mf, K, A> ma) => mf.Apply(ma); /// /// Applicative apply operator /// public static EitherT operator * (K, A> ma, K, Func> mf) => mf.Apply(ma); } extension(K, A> self) where M : Monad { /// /// Applicative apply operator /// public static EitherT> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static EitherT> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) where M : Monad { /// /// Applicative apply operator /// public static EitherT>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static EitherT>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) where M : Monad { /// /// Applicative apply operator /// public static EitherT>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static EitherT>>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) where M : Monad { /// /// Applicative apply operator /// public static EitherT>>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static EitherT>>>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) where M : Monad { /// /// Applicative apply operator /// public static EitherT>>>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static EitherT>>>>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) where M : Monad { /// /// Applicative apply operator /// public static EitherT>>>>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static EitherT>>>>>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) where M : Monad { /// /// Applicative apply operator /// public static EitherT>>>>>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static EitherT>>>>>>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) where M : Monad { /// /// Applicative apply operator /// public static EitherT>>>>>>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static EitherT>>>>>>>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) where M : Monad { /// /// Applicative apply operator /// public static EitherT>>>>>>>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static EitherT>>>>>>>>> operator *( K, A> ma, K, Func> mf) => curry * mf * ma; } } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/EitherT/Operators/EitherT.Operators.Choice.cs ================================================ using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt; public static partial class EitherTExtensions { extension(K, A> self) where M : Monad { public static EitherT operator |(K, A> lhs, K, A> rhs) => +lhs.Choose(rhs); public static EitherT operator |(K, A> lhs, Pure rhs) => +lhs.Choose(EitherT.lift(rhs.ToEither())); } } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/EitherT/Operators/EitherT.Operators.Fallible.cs ================================================ using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt; public static partial class EitherTExtensions { extension(K, A> self) where M : Monad { public static EitherT operator |(K, A> lhs, CatchM, A> rhs) => +lhs.Catch(rhs); public static EitherT operator |(K, A> lhs, Fail rhs) => +lhs.Catch(rhs); } } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/EitherT/Operators/EitherT.Operators.Final.cs ================================================ namespace LanguageExt.Traits; public static class EitherTExtensions { extension(K, A> _) where M : Monad, Final { /// /// Run a `finally` operation after the main operation regardless of whether it succeeds or not. /// /// Primary operation /// Finally operation /// Result of primary operation public static EitherT operator |(K, A> lhs, Finally rhs) => new (lhs.As().runEither.Finally(rhs.Operation)); } } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/EitherT/Operators/EitherT.Operators.Functor.cs ================================================ using System; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; public static partial class EitherTExtensions { extension(K, A> _) where M : Monad { /// /// Functor map operator /// public static EitherT operator *(Func f, K, A> ma) => +ma.Map(f); /// /// Functor map operator /// public static EitherT operator *(K, A> ma, Func f) => +ma.Map(f); } extension(K, A> _) where M : Monad { /// /// Functor map operator /// public static EitherT> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static EitherT> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) where M : Monad { /// /// Functor map operator /// public static EitherT>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static EitherT>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) where M : Monad { /// /// Functor map operator /// public static EitherT>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static EitherT>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) where M : Monad { /// /// Functor map operator /// public static EitherT>>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static EitherT>>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) where M : Monad { /// /// Functor map operator /// public static EitherT>>>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static EitherT>>>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) where M : Monad { /// /// Functor map operator /// public static EitherT>>>>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static EitherT>>>>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) where M : Monad { /// /// Functor map operator /// public static EitherT>>>>>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static EitherT>>>>>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) where M : Monad { /// /// Functor map operator /// public static EitherT>>>>>>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static EitherT>>>>>>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) where M : Monad { /// /// Functor map operator /// public static EitherT>>>>>>>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static EitherT>>>>>>>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/EitherT/Operators/EitherT.Operators.Monad.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public static partial class EitherTExtensions { extension(K, A> self) where M : Monad { /// /// Monad bind operator /// /// Monad to bind /// Binding function /// Mapped monad public static EitherT operator >> (K, A> ma, Func, B>> f) => +ma.Bind(f); /// /// Sequentially compose two actions, discarding any value produced by the first, like sequencing operators (such /// as the semicolon) in C#. /// /// First action to run /// Second action to run /// Result of the second action public static EitherT operator >> (K, A> lhs, K, B> rhs) => lhs >> (_ => rhs); } extension(K, A> self) where M : MonadIO { /// /// Monad bind operator /// /// Monad to bind /// Binding function /// Mapped monad public static EitherT operator >> (K, A> ma, Func> f) => +ma.Bind(x => +f(x)); /// /// Sequentially compose two actions, discarding any value produced by the first, like sequencing operators (such /// as the semicolon) in C#. /// /// First action to run /// Second action to run /// Result of the second action public static EitherT operator >> (K, A> lhs, K rhs) => lhs >> (_ => rhs); } extension(K, A> self) where M : Monad { /// /// Sequentially compose two actions. The second action is a unit-returning action, so the result of the /// first action is propagated. /// /// First action to run /// Second action to run /// Result of the first action public static EitherT operator >> (K, A> lhs, K, Unit> rhs) => lhs >> (x => (_ => x) * rhs); } extension(K, A> self) where M : MonadIO { /// /// Sequentially compose two actions. The second action is a unit-returning action, so the result of the /// first action is propagated. /// /// First action to run /// Second action to run /// Result of the first action public static EitherT operator >> (K, A> lhs, K rhs) => lhs >> (x => (_ => x) * rhs); } } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/EitherT/Operators/EitherT.Operators.SemigroupK.cs ================================================ using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt; public static partial class EitherTExtensions { extension(K, A> self) where M : Monad, SemigroupK { public static EitherT operator +(K, A> lhs, K, A> rhs) => new(lhs.As().runEither.Combine(rhs.As().runEither)); public static EitherT operator +(K, A> lhs, Pure rhs) => new(lhs.As().runEither.Combine(M.Pure(Either.Right(rhs.Value)))); } extension(K, A> self) where M : Monad, SemigroupK, Fallible { public static EitherT operator +(K, A> lhs, Fail rhs) => new(lhs.As().runEither.Combine(M.Fail>(rhs.Value))); } } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/EitherT/Operators/EitherT.Operators.cs ================================================ using LanguageExt.Traits; namespace LanguageExt; public static partial class EitherTExtensions { extension(K, A> _) where M : Monad { /// /// Downcast operator /// public static EitherT operator +(K, A> ma) => (EitherT)ma; /// /// Downcast operator /// public static EitherT operator >> (K, A> ma, Lower lower) => +ma; } } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/EitherT/Trait/EitherT.TraitImpl.2.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public class EitherT : CoproductK>, Bimonad> where M : Monad { static K, A, B> CoproductCons>.Left(A value) => EitherT.Left(value); static K, A, B> CoproductCons>.Right(B value) => EitherT.Right(value); static K, A, C> CoproductK>.Match( Func Left, Func Right, K, A, B> fab) => EitherT.lift(fab.As2().Match(Left, Right)); static K, L1, B> Bifunctor>.BiMap( Func first, Func second, K, L, A> fab) => new EitherT(fab.As2().runEither.Map(e => e.BiMap(first, second))); static K, L1, A> Bimonad>.BindFirst( K, L, A> ma, Func, L1, A>> f) => new EitherT(ma.As2() .runEither .Bind(e => e switch { Either.Right(var r) => M.Pure(Either.Right(r)), Either.Left(var l) => f(l).As2().runEither, _ => throw new NotSupportedException() })); static K, L, B> Bimonad>.BindSecond( K, L, A> ma, Func, L, B>> f) => new EitherT(ma.As2() .runEither .Bind(e => e switch { Either.Right(var r) => f(r).As2().runEither, Either.Left(var l) => M.Pure(Either.Left(l)), _ => throw new NotSupportedException() })); } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/EitherT/Trait/EitherT.TraitImpl.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; /// /// Trait implementation for `EitherT` /// /// Given monad trait public partial class EitherT : MonadT, M>, Fallible>, Choice>, Natural, OptionT>, MonadIO> where M : Monad { static K, B> Monad>.Bind(K, A> ma, Func, B>> f) => ma.As().Bind(f); static K, B> Monad>.Recur(A value, Func, Next>> f) => new EitherT( M.Recur>( value, a => f(a).As() .runEither .Map(e => e switch { Either>.Left(var l) => Next.Done>(l), Either>.Right({ IsDone: true } n) => Next.Done>(n.Done), Either>.Right({ IsLoop: true } n) => Next.Loop>(n.Loop), _ => throw new NotSupportedException() }))); static K, B> Functor>.Map(Func f, K, A> ma) => ma.As().Map(f); static K, A> Applicative>.Pure(A value) => EitherT.Right(value); static K, B> Applicative>.Apply(K, Func> mf, K, A> ma) => mf.As().Bind(x => ma.As().Map(x)); static K, B> Applicative>.Apply(K, Func> mf, Memo, A> ma) => mf.As().Bind(x => ma.Value.As().Map(x)); static K, A> MonadT, M>.Lift(K ma) => EitherT.lift(ma); static K, A> MonadIO>.LiftIO(IO ma) => EitherT.lift(M.LiftIOMaybe(ma)); static K, A> Choice>.Choose(K, A> ma, K, A> mb) => new EitherT( M.Bind(ma.As().runEither, ea => ea switch { Either.Right => M.Pure(ea), Either.Left => mb.As().runEither, _ => M.Pure(ea) })); static K, A> Choice>.Choose(K, A> ma, Memo, A> mb) => new EitherT( M.Bind(ma.As().runEither, ea => ea switch { Either.Right => M.Pure(ea), Either.Left => mb.Value.As().runEither, _ => M.Pure(ea) })); static K, A> Fallible>.Fail(L error) => EitherT.Left(error); static K, A> Fallible>.Catch( K, A> fa, Func Predicate, Func, A>> Fail) => fa.As().BindLeft(l => Predicate(l) ? Fail(l).As() : EitherT.Left(l)); static K, A> Natural, OptionT>.Transform(K, A> fa) => new OptionT(fa.As().runEither.Map(Natural.transform, Option, A>).Map(ma => ma.As())); } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/Fin/Extensions/Fin.Extensions.Apply.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public static partial class FinExtensions { /// /// Applicative action: runs the first applicative, ignores the result, and returns the second applicative /// public static Fin Action(this Fin ma, K mb) => Applicative.action(ma, mb).As(); /// /// Applicative action: runs the first applicative, ignores the result, and returns the second applicative /// public static Fin Action(this K ma, K mb) => Applicative.action(ma, mb).As(); /// /// Applicative functor apply operation /// /// /// Unwraps the value within the `ma` applicative-functor, passes it to the unwrapped function(s) within `mf`, and /// then takes the resulting value and wraps it back up into a new applicative-functor. /// /// Value(s) applicative functor /// Mapping function(s) /// Mapped applicative functor public static Fin Apply(this Fin> mf, K ma) => Applicative.apply(mf, ma).As(); /// /// Applicative functor apply operation /// /// /// Unwraps the value within the `ma` applicative-functor, passes it to the unwrapped function(s) within `mf`, and /// then takes the resulting value and wraps it back up into a new applicative-functor. /// /// Value(s) applicative functor /// Mapping function(s) /// Mapped applicative functor public static Fin Apply(this K> mf, K ma) => Applicative.apply(mf, ma).As(); } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/Fin/Extensions/Fin.Extensions.Map.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public static partial class FinExtensions { /// /// Functor map operation /// /// /// Unwraps the value within the functor, passes it to the map function `f` provided, and /// then takes the mapped value and wraps it back up into a new functor. /// /// Functor to map /// Mapping function /// Mapped functor public static Fin Map(this Func f, K ma) => Functor.map(f, ma).As(); /// /// Functor map operation /// /// /// Unwraps the value within the functor, passes it to the map function `f` provided, and /// then takes the mapped value and wraps it back up into a new functor. /// /// Functor to map /// Mapping function /// Mapped functor public static Fin Map(this Func f, Fin ma) => Functor.map(f, ma).As(); } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/Fin/Extensions/Fin.Extensions.cs ================================================ using static LanguageExt.Prelude; using System.Diagnostics.Contracts; using LanguageExt.Traits; using LanguageExt.Common; using NSE = System.NotSupportedException; namespace LanguageExt; /// /// Extension methods for Fin /// public static partial class FinExtensions { public static Fin As(this K ma) => (Fin)ma; /// /// Natural transformation from `Either` to `Fin` /// public static Fin ToFin(this Either ma) => ma.Match(Right: Fin.Succ, Left: Fin.Fail); /// /// Monadic join /// [Pure] public static Fin Flatten(this Fin> ma) => ma.Bind(identity); /// /// Add the bound values of x and y, uses an Add trait to provide the add /// operation for type A. For example x.Add〈TInteger, int〉(y) /// /// Num of A /// Bound value type /// Left hand side of the operation /// Right hand side of the operation /// Fin with y added to x [Pure] public static Fin Plus(this Fin x, Fin y) where NUM : Arithmetic => from a in x from b in y select NUM.Add(a, b); /// /// Find the difference between the two bound values of x and y, uses a Subtract trait /// to provide the subtract operation for type A. For example x.Subtract〈TInteger, int〉(y) /// /// Num of A /// Bound value type /// Left hand side of the operation /// Right hand side of the operation /// Fin with the difference between x and y [Pure] public static Fin Subtract(this Fin x, Fin y) where NUM : Arithmetic => from a in x from b in y select NUM.Subtract(a, b); /// /// Find the product between the two bound values of x and y, uses a Product trait /// to provide the product operation for type A. For example x.Product〈TInteger, int〉(y) /// /// Num of A /// Bound value type /// Left hand side of the operation /// Right hand side of the operation /// Fin with the product of x and y [Pure] public static Fin Product(this Fin x, Fin y) where NUM : Arithmetic => from a in x from b in y select NUM.Multiply(a, b); /// /// Divide the two bound values of x and y, uses a Divide trait to provide the divide /// operation for type A. For example x.Divide〈TDouble, double〉(y) /// /// Num of A /// Bound value type /// Left hand side of the operation /// Right hand side of the operation /// Fin x / y [Pure] public static Fin Divide(this Fin x, Fin y) where NUM : Num => from a in x from b in y select NUM.Divide(a, b); extension(K> self) where F : Foldable { /// /// Partitions a foldable of `Fin` into two sequences. /// /// All the `Fail` elements are extracted, in order, to the first component of the output. /// Similarly, the `Succ` elements are extracted to the second component of the output. /// /// A pair containing the sequences of partitioned values [Pure] public (Seq Fails, Seq Succs) Partition() => self.Fold((Fail: Seq.Empty, Succ: Seq.Empty), (s, ma) => ma switch { Fin.Succ (var r) => (s.Fail, s.Succ.Add(r)), Fin.Fail (var l) => (s.Fail.Add(l), s.Succ), _ => throw new NSE() }); /// /// Partitions a foldable of `Fin` into two lists and returns the `Fail` items only. /// /// A sequence of partitioned items [Pure] public Seq Fails() => self.Fold(Seq.Empty, (s, ma) => ma switch { Fin.Fail (var l) => s.Add(l), _ => throw new NSE() }); /// /// Partitions a foldable of `Fin` into two lists and returns the `Succ` items only. /// /// A sequence of partitioned items [Pure] public Seq Succs() => self.Fold(Seq.Empty, (s, ma) => ma switch { Fin.Succ (var r) => s.Add(r), _ => throw new NSE() }); } } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/Fin/Extensions/Fin.Guard.cs ================================================ using System; using LanguageExt.Common; using static LanguageExt.Prelude; namespace LanguageExt; public static class FinGuardExtensions { /// /// Natural transformation to `Fin` /// public static Fin ToFin(this Guard ma) => ma.Flag ? Fin.Succ(unit) : Fin.Fail(ma.OnFalse()); /// /// Monadic binding support for `Fin` /// public static Fin Bind( this Guard guard, Func> f) => guard.Flag ? f(default).As() : Fin.Fail(guard.OnFalse()); /// /// Monadic binding support for `Fin` /// public static Fin SelectMany(this Guard ma, Func> f) => ma.Flag ? f(default) : Fin.Fail(ma.OnFalse()); /// /// Monadic binding support for `Fin` /// public static Fin SelectMany( this Guard ma, Func> bind, Func project) => ma.Flag ? bind(default).Map(b => project(default, b)) : Fin.Fail(ma.OnFalse()); } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/Fin/Fin.Fail.cs ================================================ using System; using System.Diagnostics.Contracts; using LanguageExt.Common; namespace LanguageExt; public partial class Fin { /// /// Fail case for the `Fin` union-type /// /// Error value public sealed class Fail(Error Error) : Fin { /// /// Value accessor /// public Error Error { get; } = Error; /// /// Is the structure in a Success state? /// [Pure] public override bool IsSucc => false; /// /// Is the structure in a Fail state? /// [Pure] public override bool IsFail => true; /// /// Invokes the Succ or Fail function depending on the state of the structure /// /// Return type /// Function to invoke if in a Succ state /// Function to invoke if in a Fail state /// The return value of the invoked function [Pure] public override B Match(Func Succ, Func Fail) => Fail(Error); /// /// Show the structure as a string /// [Pure] public override string ToString() => $"Fail({Error})"; [Pure] public override int GetHashCode() => -1; /// /// Empty span /// [Pure] public override ReadOnlySpan FailSpan() => new([Error]); /// /// Span of right value /// [Pure] public override ReadOnlySpan SuccSpan() => ReadOnlySpan.Empty; /// /// Compare this structure to another to find its relative ordering /// [Pure] public override int CompareTo(Fin other) => other is Fail ? 0 : -1; /// /// Equality override /// [Pure] public override bool Equals(Fin other) => other is Fail; /// /// Unsafe access to the success value /// internal override A SuccValue => throw new InvalidCastException(); /// /// Unsafe access to the fail value /// /// internal override Error FailValue => Error; /// /// Maps the value in the structure /// /// Map function /// Mapped structure [Pure] public override Fin Map(Func f) => new Fin.Fail(Error); /// /// Maps the value in the structure /// /// Map function /// Mapped structure [Pure] public override Fin MapFail(Func f) => new Fail(f(Error)); /// /// Bi-maps the structure /// /// Mapped Either [Pure] public override Fin BiMap(Func Succ, Func Fail) => new Fin.Fail(Fail(Error)); /// /// Monadic bind /// /// Resulting bound value /// Bind function /// Bound structure [Pure] public override Fin Bind(Func> f) => new Fin.Fail(Error); /// /// Bi-bind. Allows mapping of both monad states /// [Pure] public override Fin BiBind( Func> Succ, Func> Fail) => Fail(Error); public void Deconstruct(out Error value) => value = Error; } } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/Fin/Fin.Module.cs ================================================ using LanguageExt.Common; using System.Diagnostics.Contracts; using System.Runtime.CompilerServices; namespace LanguageExt; public partial class Fin { /// /// Construct a `Fin` value in a `Succ` state (success) /// /// Value to construct the `Succ` state with /// Value type /// Constructed `Fin` value [Pure, MethodImpl(Opt.Default)] public static Fin Succ(A value) => new Fin.Succ(value); /// /// Construct a `Fin` value in a `Fail` state /// /// Value to construct the `Fail` state with /// Value type /// Constructed `Fin` value [Pure, MethodImpl(Opt.Default)] public static Fin Fail(Error error) => new Fin.Fail(error); /// /// Construct a `Fin` value in a `Fail` state /// /// Value to construct the `Fail` state with /// Value type /// Constructed `Fin` value [Pure, MethodImpl(Opt.Default)] public static Fin Fail(string error) => new Fin.Fail(Error.New(error)); } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/Fin/Fin.Succ.cs ================================================ using System; using System.Diagnostics.Contracts; using LanguageExt.Common; namespace LanguageExt; public partial class Fin { /// /// Success case for the `Fin` union-type /// /// Success value public sealed class Succ(A Value) : Fin { /// /// Value accessor /// public A Value { get; } = Value; /// /// Is the structure in a Success state? /// [Pure] public override bool IsSucc => true; /// /// Is the structure in a Fail state? /// [Pure] public override bool IsFail => false; /// /// Invokes the Succ or Fail function depending on the state of the structure /// /// Return type /// Function to invoke if in a Succ state /// Function to invoke if in a Fail state /// The return value of the invoked function [Pure] public override B Match(Func Succ, Func Fail) => Succ(Value); /// /// Show the structure as a string /// [Pure] public override string ToString() => Value is null ? "Succ(null)" : $"Succ({Value})"; /// /// Get a hash code for the structure /// public override int GetHashCode() => Value is null ? 0 : HashA.GetHashCode(Value); /// /// Empty span /// [Pure] public override ReadOnlySpan FailSpan() => ReadOnlySpan.Empty; /// /// Span of right value /// [Pure] public override ReadOnlySpan SuccSpan() => new([Value]); /// /// Compare this structure to another to find its relative ordering /// [Pure] public override int CompareTo(Fin? other) => other switch { Succ r => OrdA.Compare(Value, r.Value), _ => 1 }; /// /// Equality override /// [Pure] public override bool Equals(Fin other) => other switch { Succ r => EqA.Equals(Value, r.Value), _ => false }; /// /// Unsafe access to the success value /// internal override A SuccValue => Value; /// /// Unsafe access to the fail value /// /// internal override Error FailValue => throw new InvalidCastException(); /// /// Maps the value in the structure /// /// Map function /// Mapped structure [Pure] public override Fin Map(Func Succ) => new Fin.Succ(Succ(Value)); /// /// Maps the value in the structure /// /// Map function /// Mapped structure [Pure] public override Fin MapFail(Func f) => this; /// /// Bi-maps the structure /// /// Mapped Either [Pure] public override Fin BiMap(Func Succ, Func Fail) => new Fin.Succ(Succ(Value)); /// /// Monadic bind /// /// Resulting bound value /// Bind function /// Bound structure [Pure] public override Fin Bind(Func> f) => f(Value); /// /// Bi-bind. Allows mapping of both monad states /// [Pure] public override Fin BiBind( Func> Succ, Func> Fail) => Succ(Value); public void Deconstruct(out A value) => value = Value; } } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/Fin/Fin.cs ================================================ using System; using LanguageExt.Common; using LanguageExt.Traits; using System.Collections; using System.Collections.Generic; using System.Diagnostics.Contracts; using System.Runtime.CompilerServices; using LanguageExt.ClassInstances; using static LanguageExt.Prelude; namespace LanguageExt; /// /// Equivalent of `Either〈Error, A〉` /// Called `Fin` because it is expected to be used as the concrete result of a computation /// public abstract partial class Fin : IComparable>, IEquatable>, IComparable, K { /// /// Stop other types deriving from Fin /// private Fin() {} /// /// Is the structure in a Success state? /// [Pure] public abstract bool IsSucc { get; } /// /// Is the structure in a Fail state? /// [Pure] public abstract bool IsFail { get; } /// /// Invokes the Succ or Fail function depending on the state of the structure /// /// Return type /// Function to invoke if in a Succ state /// Function to invoke if in a Fail state /// The return value of the invoked function [Pure] public abstract B Match(Func Succ, Func Fail); /// /// Empty span /// [Pure] public abstract ReadOnlySpan FailSpan(); /// /// Span of right value /// [Pure] public abstract ReadOnlySpan SuccSpan(); /// /// Compare this structure to another to find its relative ordering /// [Pure] public abstract int CompareTo(Fin other) where OrdA : Ord; /// /// Equality override /// [Pure] public abstract bool Equals(Fin other) where EqA : Eq; /// /// Equality override /// [Pure] public bool Equals(Fin? other) => other is not null && Equals>(other); /// /// Equality override /// [Pure] public override bool Equals(object? other) => other is Fin f && Equals(f); [Pure] public abstract int GetHashCode() where HashA : Hashable; [Pure] public override int GetHashCode() => GetHashCode>(); /// /// Unsafe access to the success value /// internal abstract A SuccValue { get; } /// /// Unsafe access to the fail value /// internal abstract Error FailValue { get; } /// /// Maps the value in the structure /// /// Map function /// Mapped structure [Pure] public abstract Fin Map(Func f); /// /// Maps the value in the structure /// /// Map function /// Mapped structure [Pure] public abstract Fin MapFail(Func f); /// /// Bi-maps the structure /// /// Mapped Either [Pure] public abstract Fin BiMap(Func Succ, Func Fail); /// /// Monadic bind /// /// Resulting bound value /// Bind function /// Bound structure [Pure] public abstract Fin Bind(Func> f); /// /// Bi-bind. Allows mapping of both monad states /// [Pure] public abstract Fin BiBind(Func> Succ, Func> Fail); /// /// Bind if in a fail state /// [Pure] public Fin BindFail(Func> Fail) => BiBind(Fin.Succ, Fail); /// /// Monoid empty /// [Pure] public static Fin Empty { get; } = new Fail(Errors.None); /// /// Must exist here to make `operator true` work /// public static Fin operator |(Fin lhs, Fin rhs) => lhs.Choose(rhs).As(); [Pure, MethodImpl(Opt.Default)] public static implicit operator Fin(A value) => new Succ(value); [Pure, MethodImpl(Opt.Default)] public static implicit operator Fin(Error error) => new Fail(error); [Pure, MethodImpl(Opt.Default)] public static implicit operator Fin(Either either) => either switch { Either.Right (var r) => new Succ(r), Either.Left (var l) => new Fail(l), _ => throw new InvalidCastException() }; [Pure, MethodImpl(Opt.Default)] public static implicit operator Fin(Pure value) => new Succ(value.Value); [Pure, MethodImpl(Opt.Default)] public static implicit operator Fin(Fail value) => new Fail(value.Value); [Pure, MethodImpl(Opt.Default)] public static explicit operator A(Fin ma) => ma.SuccValue; [Pure, MethodImpl(Opt.Default)] public static explicit operator Error(Fin ma) => ma.FailValue; [Pure, MethodImpl(Opt.Default)] public static bool operator true(Fin ma) => ma.IsSucc; [Pure, MethodImpl(Opt.Default)] public static bool operator false(Fin ma) => ma.IsFail; [Pure, MethodImpl(Opt.Default)] public static bool operator ==(Fin ma, Fin mb) => ma.Equals(mb); [Pure, MethodImpl(Opt.Default)] public static bool operator !=(Fin ma, Fin mb) => !ma.Equals(mb); /// /// Comparison operator /// /// The left hand side of the operation /// The right hand side of the operation /// True if lhs〈 rhs [Pure] public static bool operator <(Fin lhs, A rhs) => lhs < (Fin)rhs; /// /// Comparison operator /// /// The left hand side of the operation /// The right hand side of the operation /// True if lhs〈= rhs [Pure] public static bool operator <=(Fin lhs, A rhs) => lhs <= (Fin)rhs; /// /// Comparison operator /// /// The left hand side of the operation /// The right hand side of the operation /// True if lhs 〉rhs [Pure] public static bool operator >(Fin lhs, A rhs) => lhs > (Fin)rhs; /// /// Comparison operator /// /// The left hand side of the operation /// The right hand side of the operation /// True if lhs 〉= rhs [Pure] public static bool operator >=(Fin lhs, A rhs) => lhs >= (Fin)rhs; /// /// Comparison operator /// /// The left hand side of the operation /// The right hand side of the operation /// True if lhs〈 rhs [Pure] public static bool operator <(A lhs, Fin rhs) => (Fin)lhs < rhs; /// /// Comparison operator /// /// The left hand side of the operation /// The right hand side of the operation /// True if lhs〈= rhs [Pure] public static bool operator <=(A lhs, Fin rhs) => (Fin)lhs <= rhs; /// /// Comparison operator /// /// The left hand side of the operation /// The right hand side of the operation /// True if lhs 〉rhs [Pure] public static bool operator >(A lhs, Finrhs) => (Fin)lhs > rhs; /// /// Comparison operator /// /// The left hand side of the operation /// The right hand side of the operation /// True if lhs 〉= rhs [Pure] public static bool operator >=(A lhs, Fin rhs) => (Fin)lhs >= rhs; /// /// Comparison operator /// /// The left hand side of the operation /// The right hand side of the operation /// True if lhs〈 rhs [Pure] public static bool operator <(Fin lhs, Fin rhs) => lhs.CompareTo(rhs) < 0; /// /// Comparison operator /// /// The left hand side of the operation /// The right hand side of the operation /// True if lhs〈= rhs [Pure] public static bool operator <=(Fin lhs, Fin rhs) => lhs.CompareTo(rhs) <= 0; /// /// Comparison operator /// /// The left hand side of the operation /// The right hand side of the operation /// True if lhs > rhs [Pure] public static bool operator >(Fin lhs, Fin rhs) => lhs.CompareTo(rhs) > 0; /// /// Comparison operator /// /// The left hand side of the operation /// The right hand side of the operation /// True if lhs >= rhs [Pure] public static bool operator >=(Fin lhs, Fin rhs) => lhs.CompareTo(rhs) >= 0; /// /// Equality operator override /// [Pure] public static bool operator ==(Fin lhs, Error rhs) => lhs.Equals((Fin)rhs); /// /// Equality operator override /// [Pure] public static bool operator ==(Fin lhs, A rhs) => lhs.Equals((Fin)rhs); /// /// Equality operator override /// [Pure] public static bool operator ==(A lhs, Fin rhs) => ((Fin)lhs).Equals(rhs); /// /// Equality operator override /// [Pure] public static bool operator ==(Error lhs, Fin rhs) => Fin.Fail(lhs).Equals(rhs); /// /// Non-equality operator override /// [Pure] public static bool operator !=(Fin lhs, Error rhs) => !(lhs == rhs); /// /// Non-equality operator override /// [Pure] public static bool operator !=(Error lhs, Fin rhs) => !(lhs == rhs); /// /// Equality operator override /// [Pure] public static bool operator !=(Fin lhs, A rhs) => !(lhs == rhs!); /// /// Equality operator override /// [Pure] public static bool operator !=(A lhs, Fin rhs) => !(lhs! == rhs); [Pure, MethodImpl(Opt.Default)] static Option convert(in object? value) { if (value == null) { return None; } try { return (T)Convert.ChangeType(value, typeof(T)); } catch { return None; } } [Pure, MethodImpl(Opt.Default)] internal Fin Cast() => Bind(x => convert(x) .Map(Fin.Succ) .IfNone(() => Fin.Fail(Error.New($"Can't cast success value of `{nameof(A)}` to `{nameof(B)}` ")))); [Pure, MethodImpl(Opt.Default)] public int CompareTo(Fin? other) => other is null ? 1 : CompareTo>(other); [Pure, MethodImpl(Opt.Default)] public int CompareTo(object? obj) => obj is Fin t ? CompareTo(t) : 1; [MethodImpl(Opt.Default)] public Unit Match(Action Succ, Action Fail) => Match(fun(Succ), fun(Fail)); [Pure, MethodImpl(Opt.Default)] public A IfFail(Func Fail) => Match(identity, Fail); [Pure, MethodImpl(Opt.Default)] public A IfFail(A alternative) => Match(identity, _ => alternative); [MethodImpl(Opt.Default)] public Unit IfFail(Action Fail) => Match(_ => { }, Fail); [MethodImpl(Opt.Default)] public Unit IfSucc(Action Succ) => Match(Succ, _ => { }); [MethodImpl(Opt.Default)] public Unit Iter(Action Succ) => Match(Succ, _ => { }); [Pure, MethodImpl(Opt.Default)] public S Fold(S state, Func f) => Match(curry(f)(state), _ => state); [Pure, MethodImpl(Opt.Default)] public S BiFold(in S state, Func Succ, Func Fail) => Match(curry(Succ)(state), curry(Fail)(state)); [Pure, MethodImpl(Opt.Default)] public bool Exists(Func f) => Match(f, _ => false); [Pure, MethodImpl(Opt.Default)] public bool ForAll(Func f) => Match(f, _ => true); /// /// Map each element of a structure to an action, evaluate these actions from /// left to right, and collect the results. /// /// /// Traversable structure /// Applicative functor trait /// Bound value (output) [Pure] public K> Traverse(Func> f) where F : Applicative => F.Map(x => x.As(), Traversable.traverse(f, this)); /// /// Map each element of a structure to an action, evaluate these actions from /// left to right, and collect the results. /// /// /// Traversable structure /// Monad trait /// Bound value (output) [Pure] public K> TraverseM(Func> f) where M : Monad => M.Map(x => x.As(), Traversable.traverseM(f, this)); [Pure, MethodImpl(Opt.Default)] public Fin Select(Func f) => Map(f); [Pure, MethodImpl(Opt.Default)] public Fin Bind(Func> f) => Bind(x => f(x).As()); [Pure, MethodImpl(Opt.Default)] public Fin Bind(Func> f) => Map(x => f(x).Value); [Pure, MethodImpl(Opt.Default)] public Fin Bind(Func> f) => Bind(x => Fail(f(x).Value)); [Pure, MethodImpl(Opt.Default)] public Fin SelectMany(Func> bind, Func project) => Bind(x => bind(x).Map(y => project(x, y))); [Pure, MethodImpl(Opt.Default)] public Fin SelectMany(Func> bind, Func project) => Bind(x => bind(x).As().Map(y => project(x, y))); [Pure, MethodImpl(Opt.Default)] public Fin SelectMany(Func> bind, Func project) => Bind(x => bind(x).Map(y => project(x, y))); [Pure, MethodImpl(Opt.Default)] public Fin SelectMany(Func> bind, Func project) => Map(x => ignore(bind(x))); [Pure, MethodImpl(Opt.Default)] public Fin SelectMany(Func> f) => Bind(a => f(a).ToFin()); [Pure, MethodImpl(Opt.Default)] public Fin SelectMany(Func> bind, Func project) => Bind(a => bind(a).ToFin().Map(_ => project(a, default))); [Pure, MethodImpl(Opt.Default)] public IEnumerable AsEnumerable() { if(IsSucc) yield return SuccValue; } [Pure, MethodImpl(Opt.Default)] public Iterable ToIterable() => Iterable.createRange(AsEnumerable()); [Pure, MethodImpl(Opt.Default)] public Lst ToList() => new(SuccSpan()); [Pure, MethodImpl(Opt.Default)] public Seq ToSeq() => new(SuccSpan()); [Pure, MethodImpl(Opt.Default)] public Arr ToArray() => new(SuccSpan()); [Pure, MethodImpl(Opt.Default)] public Option ToOption() => IsSucc ? Option.Some(SuccValue) : Option.None; [Pure, MethodImpl(Opt.Default)] public Either ToEither() => IsSucc ? Either.Right(SuccValue) : Either.Left(FailValue); [Pure, MethodImpl(Opt.Default)] public Validation ToValidation() => IsSucc ? Validation.Success(SuccValue) : Validation.Fail(FailValue); [Pure, MethodImpl(Opt.Default)] public Eff ToEff() => IsSucc ? Eff.Pure(SuccValue) : Eff.Fail(FailValue); public A ThrowIfFail() { if (IsFail) { FailValue.Throw(); } return SuccValue; } } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/Fin/Operators/Fin.Operators.Applicative.cs ================================================ using System; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; public static partial class FinExtensions { extension(K self) { /// /// Applicative sequence operator /// public static Fin operator >>> (K ma, K mb) => ma.Action(mb).As(); /// /// Applicative apply operator /// public static Fin operator * (K> mf, K ma) => mf.Apply(ma); /// /// Applicative apply operator /// public static Fin operator * (K ma, K> mf) => mf.Apply(ma); } extension(K self) { /// /// Applicative apply operator /// public static Fin> operator * ( K> mf, K ma) => curry * mf * ma; /// /// Applicative apply operator /// public static Fin> operator * ( K ma, K> mf) => curry * mf * ma; } extension(K self) { /// /// Applicative apply operator /// public static Fin>> operator * ( K> mf, K ma) => curry * mf * ma; /// /// Applicative apply operator /// public static Fin>> operator * ( K ma, K> mf) => curry * mf * ma; } extension(K self) { /// /// Applicative apply operator /// public static Fin>>> operator * ( K> mf, K ma) => curry * mf * ma; /// /// Applicative apply operator /// public static Fin>>> operator * ( K ma, K> mf) => curry * mf * ma; } extension(K self) { /// /// Applicative apply operator /// public static Fin>>>> operator * ( K> mf, K ma) => curry * mf * ma; /// /// Applicative apply operator /// public static Fin>>>> operator * ( K ma, K> mf) => curry * mf * ma; } extension(K self) { /// /// Applicative apply operator /// public static Fin>>>>> operator * ( K> mf, K ma) => curry * mf * ma; /// /// Applicative apply operator /// public static Fin>>>>> operator * ( K ma, K> mf) => curry * mf * ma; } extension(K self) { /// /// Applicative apply operator /// public static Fin>>>>>> operator * ( K> mf, K ma) => curry * mf * ma; /// /// Applicative apply operator /// public static Fin>>>>>> operator * ( K ma, K> mf) => curry * mf * ma; } extension(K self) { /// /// Applicative apply operator /// public static Fin>>>>>>> operator * ( K> mf, K ma) => curry * mf * ma; /// /// Applicative apply operator /// public static Fin>>>>>>> operator * ( K ma, K> mf) => curry * mf * ma; } extension(K self) { /// /// Applicative apply operator /// public static Fin>>>>>>>> operator * ( K> mf, K ma) => curry * mf * ma; /// /// Applicative apply operator /// public static Fin>>>>>>>> operator * ( K ma, K> mf) => curry * mf * ma; } extension(K self) { /// /// Applicative apply operator /// public static Fin>>>>>>>>> operator * ( K> mf, K ma) => curry * mf * ma; /// /// Applicative apply operator /// public static Fin>>>>>>>>> operator *( K ma, K> mf) => curry * mf * ma; } } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/Fin/Operators/Fin.Operators.Choice.cs ================================================ using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt; public static partial class FinExtensions { extension(Fin self) { public static Fin operator |(Fin lhs, Fin rhs) => +lhs.Choose(rhs); public static Fin operator |(Fin lhs, Pure rhs) => +lhs.Choose(rhs.ToFin()); } extension(K self) { public static Fin operator |(K lhs, K rhs) => +lhs.Choose(rhs); public static Fin operator |(K lhs, Pure rhs) => +lhs.Choose(rhs.ToFin()); } } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/Fin/Operators/Fin.Operators.Fallible.cs ================================================ using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt; public static partial class FinExtensions { extension(Fin self) { public static Fin operator |(Fin lhs, CatchM rhs) => +lhs.Catch(rhs); public static Fin operator |(Fin lhs, Fail rhs) => +lhs.Catch(rhs); public static Fin operator |(Fin lhs, Error rhs) => +lhs.Catch(rhs); } extension(K self) { public static Fin operator |(K lhs, CatchM rhs) => +lhs.Catch(rhs); public static Fin operator |(K lhs, Fail rhs) => +lhs.Catch(rhs); public static Fin operator |(K lhs, Error rhs) => +lhs.Catch(rhs); } } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/Fin/Operators/Fin.Operators.Functor.cs ================================================ using System; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; public static partial class FinExtensions { extension(K _) { /// /// Functor map operator /// public static Fin operator *(Func f, K ma) => +ma.Map(f); /// /// Functor map operator /// public static Fin operator *(K ma, Func f) => +ma.Map(f); } extension(K _) { /// /// Functor map operator /// public static Fin> operator * ( Func f, K ma) => curry(f) * ma; /// /// Functor map operator /// public static Fin> operator * ( K ma, Func f) => curry(f) * ma; } extension(K _) { /// /// Functor map operator /// public static Fin>> operator * ( Func f, K ma) => curry(f) * ma; /// /// Functor map operator /// public static Fin>> operator * ( K ma, Func f) => curry(f) * ma; } extension(K _) { /// /// Functor map operator /// public static Fin>>> operator * ( Func f, K ma) => curry(f) * ma; /// /// Functor map operator /// public static Fin>>> operator * ( K ma, Func f) => curry(f) * ma; } extension(K _) { /// /// Functor map operator /// public static Fin>>>> operator * ( Func f, K ma) => curry(f) * ma; /// /// Functor map operator /// public static Fin>>>> operator * ( K ma, Func f) => curry(f) * ma; } extension(K _) { /// /// Functor map operator /// public static Fin>>>>> operator * ( Func f, K ma) => curry(f) * ma; /// /// Functor map operator /// public static Fin>>>>> operator * ( K ma, Func f) => curry(f) * ma; } extension(K _) { /// /// Functor map operator /// public static Fin>>>>>> operator * ( Func f, K ma) => curry(f) * ma; /// /// Functor map operator /// public static Fin>>>>>> operator * ( K ma, Func f) => curry(f) * ma; } extension(K _) { /// /// Functor map operator /// public static Fin>>>>>>> operator * ( Func f, K ma) => curry(f) * ma; /// /// Functor map operator /// public static Fin>>>>>>> operator * ( K ma, Func f) => curry(f) * ma; } extension(K _) { /// /// Functor map operator /// public static Fin>>>>>>>> operator * ( Func f, K ma) => curry(f) * ma; /// /// Functor map operator /// public static Fin>>>>>>>> operator * ( K ma, Func f) => curry(f) * ma; } extension(K _) { /// /// Functor map operator /// public static Fin>>>>>>>>> operator * ( Func f, K ma) => curry(f) * ma; /// /// Functor map operator /// public static Fin>>>>>>>>> operator * ( K ma, Func f) => curry(f) * ma; } } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/Fin/Operators/Fin.Operators.Monad.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public static partial class FinExtensions { extension(K self) { /// /// Monad bind operator /// /// Monad to bind /// Binding function /// Mapped monad public static Fin operator >> (K ma, Func> f) => +ma.Bind(f); /// /// Sequentially compose two actions, discarding any value produced by the first, like sequencing operators (such /// as the semicolon) in C#. /// /// First action to run /// Second action to run /// Result of the second action public static Fin operator >> (K lhs, K rhs) => lhs >> (_ => rhs); } extension(K self) { /// /// Sequentially compose two actions. The second action is a unit-returning action, so the result of the /// first action is propagated. /// /// First action to run /// Second action to run /// Result of the first action public static Fin operator >> (K lhs, K rhs) => lhs >> (x => (_ => x) * rhs); } } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/Fin/Operators/Fin.Operators.SemigroupK.cs ================================================ using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt; public static partial class FinExtensions { extension(K _) { /// /// Semigroup combine operator: an associative binary operation. /// /// Left-hand side operand /// Right-hand side operand /// public static Fin operator +(K lhs, K rhs) => +lhs.Combine(rhs); /// /// Semigroup combine operator: an associative binary operation. /// /// Left-hand side operand /// Right-hand side operand /// public static Fin operator +(K lhs, Pure rhs) => +lhs.Combine(rhs.ToFin()); /// /// Semigroup combine operator: an associative binary operation. /// /// Left-hand side operand /// Right-hand side operand /// public static Fin operator +(K lhs, Fail rhs) => +lhs.Combine(Fin.Fail(rhs.Value)); /// /// Semigroup combine operator: an associative binary operation. /// /// Left-hand side operand /// Right-hand side operand /// public static Fin operator +(K lhs, Error rhs) => +lhs.Combine(Fin.Fail(rhs)); } } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/Fin/Operators/Fin.Operators.cs ================================================ using LanguageExt.Traits; namespace LanguageExt; public static partial class FinExtensions { extension(K _) { /// /// Downcast operator /// public static Fin operator +(K ma) => (Fin)ma; /// /// Downcast operator /// public static Fin operator >> (K ma, Lower lower) => +ma; } } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/Fin/README.md ================================================ The `Fin` monad supports either an `Error` or an `A` (success) value. It is functionally exactly the same as `Either`, it is a convenience type to avoid the generics pain of `Either`. To construct a `Fin`: Fin ma = Pure(123); Fin mb = Fail(Error.New("Error!")); ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/Fin/Trait/Fin.TraitImpl.cs ================================================ using System; using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt; public partial class Fin : Monad, MonoidK, Fallible, Traversable, Alternative, Natural>, Natural, Natural, Natural, Natural { static K Monad.Bind(K ma, Func> f) => ma switch { Fin.Succ (var r) => f(r), Fin.Fail (var l) => Fail(l), _ => throw new NotSupportedException() }; static K Monad.Recur(A value, Func>> f) { while (true) { var mr = +f(value); if (mr.IsFail) return Fail(mr.FailValue); var next = (Next)mr; if(next.IsDone) return Succ(next.Done); value = next.Loop; } } static K Functor.Map(Func f, K ma) => ma switch { Fin.Succ (var r) => Succ(f(r)), Fin.Fail (var l) => Fail(l), _ => throw new NotSupportedException() }; static K Applicative.Pure(A value) => Succ(value); static K Applicative.Apply(K> mf, K ma) => (mf, ma) switch { (Fin>.Succ (var f), Fin.Succ (var a)) => Succ(f(a)), (Fin>.Fail (var e1), Fin.Fail (var e2)) => Fail(e1 + e2), (Fin>.Fail (var e1), _) => Fail(e1), (_, Fin.Fail (var e2)) => Fail(e2), _ => throw new NotSupportedException() }; static K Applicative.Apply(K> mf, Memo ma) => (mf, ma.Value) switch { (Fin>.Succ (var f), Fin.Succ (var a)) => Succ(f(a)), (Fin>.Fail (var e1), Fin.Fail (var e2)) => Fail(e1 + e2), (Fin>.Fail (var e1), _) => Fail(e1), (_, Fin.Fail (var e2)) => Fail(e2), _ => throw new NotSupportedException() }; static S Foldable.FoldWhile( Func> f, Func<(S State, A Value), bool> predicate, S state, K ta) => ta switch { Fin.Succ (var r) => predicate((state, r)) ? f(r)(state) : state, _ => state }; static S Foldable.FoldBackWhile( Func> f, Func<(S State, A Value), bool> predicate, S state, K ta) => ta switch { Fin.Succ (var r) => predicate((state, r)) ? f(state)(r) : state, _ => state }; static K> Traversable.Traverse(Func> f, K ta) => ta switch { Fin.Succ (var r) => F.Map(ConsSucc, f(r)), Fin.Fail (var l) => F.Pure(ConsFail(l)), _ => throw new NotSupportedException() }; static K MonoidK.Empty() => Fail(Errors.None); static K Alternative.Empty() => Fail(Errors.None); static K SemigroupK.Combine(K ma, K mb) => ma switch { Fin.Succ => ma, Fin.Fail (var e1) => mb switch { Fin.Succ => mb, Fin.Fail (var e2) => Fail(e1 + e2), _ => mb }, _ => ma }; static K Choice.Choose(K ma, K mb) => ma switch { Fin.Succ => ma, _ => mb }; static K Choice.Choose(K ma, Memo mb) => ma switch { Fin.Succ => ma, _ => mb.Value }; static K ConsSucc(A value) => Succ(value); static K ConsFail(Error value) => Fail(value); static K Fallible.Fail(Error error) => Fail(error); static K Fallible.Catch( K fa, Func Predicate, Func> Fail) => fa.As().BindFail(e => Predicate(e) ? Fail(e).As() : Fail(e)); static K, A> Natural>.Transform(K fa) => fa switch { Fin.Succ (var x) => Either.Right(x), Fin.Fail (var e) => Either.Left(e), _ => throw new NotSupportedException() }; static K Natural.Transform(K fa) => fa switch { Fin.Succ (var x) => Option.Some(x), Fin.Fail => Option.None, _ => throw new NotSupportedException() }; static K Natural.Transform(K fa) => fa switch { Fin.Succ (var x) => Try.Succ(x), Fin.Fail (var e) => Try.Fail(e), _ => throw new NotSupportedException() }; static K Natural.Transform(K fa) => fa switch { Fin.Succ (var x) => Eff.Pure(x), Fin.Fail (var e) => Eff.Fail(e), _ => throw new NotSupportedException() }; static K Natural.Transform(K fa) => fa switch { Fin.Succ (var x) => IO.pure(x), Fin.Fail (var e) => IO.fail(e), _ => throw new NotSupportedException() }; static Fold Foldable.FoldStep(K ta, S initialState) { var ma = ta.As(); return ma.IsSucc ? Fold.Loop(initialState, ma.SuccValue, Fold.Done) : Fold.Done(initialState); } static Fold Foldable.FoldStepBack(K ta, S initialState) => ta.FoldStep(initialState); } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/FinT/Extensions/FinT.Extensions.Apply.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public static partial class FinTExtensions { /// /// Applicative action: runs the first applicative, ignores the result, and returns the second applicative /// public static FinT Action(this FinT ma, FinT mb) where M : Monad => ma.Kind().Action(mb).As(); /// /// Applicative functor apply operation /// /// /// Unwraps the value within the `ma` applicative-functor, passes it to the unwrapped function(s) within `mf`, and /// then takes the resulting value and wraps it back up into a new applicative-functor. /// /// Value(s) applicative functor /// Mapping function(s) /// Mapped applicative functor public static FinT Apply(this FinT> mf, K, A> ma) where M : Monad => Applicative.apply(mf, ma).As(); /// /// Applicative functor apply operation /// /// /// Unwraps the value within the `ma` applicative-functor, passes it to the unwrapped function(s) within `mf`, and /// then takes the resulting value and wraps it back up into a new applicative-functor. /// /// Value(s) applicative functor /// Mapping function(s) /// Mapped applicative functor public static FinT Apply(this K, Func> mf, K, A> ma) where M : Monad => Applicative.apply(mf, ma).As(); } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/FinT/Extensions/FinT.Extensions.Map.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public static partial class FinTExtensions { /// /// Functor map operation /// /// /// Unwraps the value within the functor, passes it to the map function `f` provided, and /// then takes the mapped value and wraps it back up into a new functor. /// /// Functor to map /// Mapping function /// Mapped functor public static FinT Map(this Func f, K, A> ma) where M : Monad => Functor.map(f, ma).As(); /// /// Functor map operation /// /// /// Unwraps the value within the functor, passes it to the map function `f` provided, and /// then takes the mapped value and wraps it back up into a new functor. /// /// Functor to map /// Mapping function /// Mapped functor public static FinT Map(this Func f, FinT ma) where M : Monad => Functor.map(f, ma).As(); } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/FinT/Extensions/FinT.Extensions.cs ================================================ using System; using static LanguageExt.Prelude; using NSE = System.NotSupportedException; using System.Diagnostics.Contracts; using System.Threading.Tasks; using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt; public static partial class FinTExtensions { public static FinT As(this K, A> ma) where M : Monad => (FinT)ma; /// /// Runs the FinT exposing the outer monad with an inner wrapped `Fin` /// public static K> Run(this K, A> ma) where M : Monad => ma.As().runFin; /// /// Monad bind operation /// /// Mapping function /// Target bound value type /// `FinT` public static FinT Bind(this K, A> ma, Func> f) where M : MonadIO => ma.As().Bind(a => FinT.liftIO(f(a))); /// /// Get the outer task and wrap it up in a new IO within the FinT IO /// public static FinT Flatten(this Task> tma) => FinT.lift(IO.liftAsync(async () => await tma.ConfigureAwait(false))) .Flatten(); /// /// Lift the task /// public static FinT ToIO(this Task> ma) where M : Monad => liftIO(ma); /// /// Monadic join /// [Pure] public static FinT Flatten(this FinT> mma) where M : Monad => mma.Bind(identity); /// /// Monad bind operation /// /// Monadic bind function /// Projection function /// Intermediate bound value type /// Target bound value type /// `FinT` [Pure] public static FinT SelectMany( this K ma, Func, B>> bind, Func project) where M : Monad => FinT.lift(ma).SelectMany(bind, project); /// /// Monad bind operation /// /// Monadic bind function /// Projection function /// Intermediate bound value type /// Target bound value type /// `FinT` [Pure] public static FinT SelectMany( this K ma, Func> bind, Func project) where M : Monad => FinT.lift(ma).SelectMany(bind, project); /// /// Monad bind operation /// /// Monadic bind function /// Projection function /// Intermediate bound value type /// Target bound value type /// `FinT` public static FinT SelectMany(this K, A> ma, Func> bind, Func project) where M : MonadIO => ma.As().SelectMany(x => M.LiftIO(bind(x)), project); /// /// Partitions a foldable of `FinT` into two sequences. /// /// All the `Fail` elements are extracted, in order, to the first component of the output. /// Similarly, the `Succ` elements are extracted to the second component of the output. /// /// A pair containing the sequences of partitioned values [Pure] public static K Fails, Seq Succs)> Partition(this K> self) where F : Foldable where M : Monad => self.Fold(M.Pure((Fail: Seq.Empty, Succ: Seq.Empty)), (ms, ma) => ms.Bind(s => ma.Run().Map(a => a switch { Fin.Succ (var r) => (s.Fail, s.Succ.Add(r)), Fin.Fail (var l) => (s.Fail.Add(l), s.Succ), _ => throw new NSE() }))); /// /// Partitions a foldable of `FinT` into two lists and returns the `Fail` items only. /// /// A sequence of partitioned items [Pure] public static K> Fails(this K> self) where F : Foldable where M : Monad => self.Fold(M.Pure(Seq.Empty), (ms, ma) => ms.Bind(s => ma.Run().Map(a => a switch { Fin.Fail(var l) => s.Add(l), _ => throw new NSE() }))); /// /// Partitions a foldable of `FinT` into two lists and returns the `Succ` items only. /// /// A sequence of partitioned items [Pure] public static K> Succs(this K> self) where F : Foldable where M : Monad => self.Fold(M.Pure(Seq.Empty), (ms, ma) => ms.Bind(s => ma.Run().Map(a => a switch { Fin.Succ (var r) => s.Add(r), _ => throw new NSE() }))); } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/FinT/Extensions/FinT.Guard.cs ================================================ using System; using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt; public static class FinTGuardExtensions { /// /// Natural transformation to `FinT` /// public static FinT ToFinT(this Guard guard) where M : Monad => guard.Flag ? FinT.Succ(default) : FinT.Fail(guard.OnFalse()); /// /// Monadic binding support for `FinT` /// public static FinT Bind( this Guard guard, Func> f) where M : Monad => guard.Flag ? f(default).As() : FinT.Fail(guard.OnFalse()); /// /// Monadic binding support for `FinT` /// public static FinT SelectMany( this Guard guard, Func> bind, Func project) where M : Monad => guard.Flag ? bind(default).As().Map(b => project(default, b)) : FinT.Fail(guard.OnFalse()); /// /// Monadic binding support for `FinT` /// public static FinT SelectMany( this FinT ma, Func> f) where M : Monad => ma.Bind(a => f(a).ToFinT()); /// /// Monadic binding support for `FinT` /// public static FinT SelectMany( this FinT ma, Func> bind, Func project) where M : Monad => ma.Bind(a => bind(a).ToFinT().Map(_ => project(a, default))); } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/FinT/FinT.Module.cs ================================================ using System; using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt; public partial class FinT { /// /// Lift a pure value into the monad-transformer /// /// Value to lift /// `FinT` public static FinT Succ(A value) where M : Monad => lift(M.Pure(value)); /// /// Lift a fail value into the monad-transformer /// /// Value to lift /// `FinT` public static FinT Fail(Error value) where M : Monad => lift(Fin.Fail(value)); /// /// Lifts a given `Fin` value into the transformer /// /// `Fin` value /// `FinT` public static FinT lift(Fin ma) where M : Monad => new(M.Pure(ma)); /// /// Lifts a given pure value into the transformer /// /// Value to lift /// `FinT` public static FinT lift(Pure value) where M : Monad => lift(Fin.Succ(value.Value)); /// /// Lifts a given failure value into the transformer /// /// Value to lift /// `FinT` public static FinT lift(Fail value) where M : Monad => lift(Fin.Fail(value.Value)); /// /// Lifts a given monad into the transformer /// /// Monad to lift /// `FinT` public static FinT lift(K monad) where M : Monad => new(M.Map(Fin.Succ, monad)); /// /// Lifts a given monad into the transformer /// /// Monad to lift /// `FinT` public static FinT lift(K> ma) where M : Monad => new(ma); /// /// Lifts a given `IO` monad into the transformer /// /// Monad to lift /// `FinT` public static FinT liftIO(IO ma) where M : MonadIO => lift(M.LiftIO(ma)); /// /// Lifts a given `IO` monad into the transformer /// /// Monad to lift /// `FinT` public static FinT liftIO(IO> ma) where M : MonadIO => lift(M.LiftIO(ma)); /// /// Lifts a given `IO` monad into the transformer /// /// Monad to lift /// `FinT` internal static FinT liftIOMaybe(IO ma) where M : Monad => lift(M.LiftIOMaybe(ma)); /// /// Lifts a given `IO` monad into the transformer /// /// Monad to lift /// `FinT` internal static FinT liftIOMaybe(IO> ma) where M : Monad => lift(M.LiftIOMaybe(ma)); } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/FinT/FinT.cs ================================================ using System; using System.Diagnostics.Contracts; using LanguageExt.Common; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; /// /// `FinT` monad transformer, which allows for either an `Error` or `R` result value to be carried. /// /// Given monad trait /// Bound value type public record FinT(K> runFin) : K, A> where M : Monad { /// /// Is the `FinT` in a `Succ` state? /// public K IsSucc => runFin.Map(f => f.IsSucc); /// /// Is the `FinT` in a `Fail` state? /// public K IsFail => runFin.Map(f => f.IsFail); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Match // /// /// Invokes the `Succ` or `Fail` function depending on the state of the `FinT` /// /// Return type /// Function to invoke if in a Fail state /// Function to invoke if in a Succ state /// The return value of the invoked function [Pure] public K Match(Func Succ, Func Fail) => M.Map(mx => mx.Match(Fail: Fail, Succ: Succ), runFin); /// /// Invokes the `Succ` or `Fail` function depending on the state of the `FinT` /// /// Action to invoke if in a `Succ` state /// Action to invoke if in a `Fail` state /// Unit [Pure] public K Match(Action Succ, Action Fail) => M.Map(mx => mx.Match(Fail: Fail, Succ: Succ), runFin); /// /// Executes the `Fail` function if the `Fin` is in a `Fail` state. /// Returns the `Succ` value if the Fin is in a `Succ` state. /// /// Function to generate a `Succ` value if in the `Fail` state /// Returns an unwrapped `Succ` value [Pure] public K IfFail(Func Fail) => IfFail(_ => Fail()); /// /// Executes the `f` function if the `Fin` is in a `Fail` state. /// Returns the `Succ` value if the `Fin` is in a `Succ` state. /// /// Function to generate a `Succ` value if in the `Fail` state /// Returns an unwrapped `Succ` value [Pure] public K IfFail(Func f) => Match(Fail: f, Succ: identity); /// /// Returns the `value` if the `Fin` is in a `Fail` state. /// Returns the `Succ` value if the `Fin` is in a `Succ` state. /// /// Value to return if in the Fail state /// Returns an unwrapped `Succ` value [Pure] public K IfFail(A value) => IfFail(_ => value); /// /// Executes the `Fail` action if the `Fin` is in a `Fail` state. /// /// Function to generate a `Succ` value if in the `Fail` state /// Returns an unwrapped Succ value public K IfFail(Action Fail) => Match(Fail: Fail, Succ: _ => {}); /// /// Invokes the `Succ` action if the `Fin` is in a `Succ` state, otherwise does nothing /// /// Action to invoke /// Unit public K IfSucc(Action Succ) => Match(Fail: _ => { }, Succ: Succ); /// /// Returns the `fail` value if the `Fin` is in a `Succ` state. /// Returns the `Fail` value if the `Fin` is in a `Fail` state. /// /// Value to return if in the `Succ` state /// Returns an unwrapped `Fail` value [Pure] public K IfSucc(Error fail) => Match(Fail: identity, Succ: _ => fail); /// /// Returns the result of `Succ()` if the `Fin` is in a `Succ` state. /// Returns the `Fail` value if the `Fin` is in a `Fail` state. /// /// Function to generate a `Fail` value if in the `Succ` state /// Returns an unwrapped Fail value [Pure] public K IfSucc(Func Succ) => Match(Fail: identity, Succ: _ => Succ()); /// /// Returns the result of `f` if the `Fin` is in a `Succ` state. /// Returns the `Fail` value if the `Fin` is in a `Fail` state. /// /// Function to generate a `Fail` value if in the `Succ` state /// Returns an unwrapped Fail value [Pure] public K IfSucc(Func f) => Match(Fail: identity, Succ: f); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Map // /// /// Maps the bound monad /// /// Mapping function /// Target monad type /// Target bound value type /// Mapped monad public FinT MapT(Func>, K>> f) where M1 : Monad => new (f(runFin)); /// /// Maps the given monad /// /// Mapping function public FinT MapM(Func, K> f) => new(runFin .Bind(fv => fv switch { Fin.Succ (var v) => f(M.Pure(v)).Map(Fin.Succ), Fin.Fail (var e) => M.Pure>(e), _ => throw new NotSupportedException() })); /// /// Maps the bound value /// /// Mapping function /// Target bound value type /// `FinT` public FinT Map(Func f) => new(M.Map(mx => mx.Map(f), runFin)); /// /// Maps the `Error` value /// /// Mapping function /// Target bound value type /// `FinT` public FinT MapFail(Func f) => new(M.Map(mx => mx.MapFail(f), runFin)); /// /// Maps the bound value /// /// Mapping transducer /// Target bound value type /// `FinT` public FinT Select(Func f) => new(M.Map(mx => mx.Map(f), runFin)); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Bind // /// /// Monad bind operation /// /// Mapping function /// Target bound value type /// `FinT` public FinT Bind(Func, B>> f) => Bind(x => f(x).As()); /// /// Monad bind operation /// /// Mapping function /// Target bound value type /// `FinT` public FinT Bind(Func> f) => Bind(x => FinT.lift(f(x))); /// /// Monad bind operation /// /// Mapping function /// Target bound value type /// `FinT` public FinT Bind(Func> f) => new(M.Bind(runFin, ex => ex.Match( Succ: x => f(x).runFin, Fail: e => M.Pure(Fin.Fail(e))))); /// /// Monad bind operation /// /// Mapping function /// Target bound value type /// `FinT` public FinT Bind(Func> f) => Map(a => f(a).Value); /// /// Monad bind operation /// /// Mapping function /// Target bound value type /// `FinT` public FinT Bind(Func> f) => Bind(a => FinT.lift(f(a).Value)); /// /// Monad bi-bind operation /// /// Fail state mapping function /// Fail state mapping function /// Target bound value type /// `FinT` public FinT BiBind(Func> Fail, Func> Succ) => new(M.Bind(runFin, ex => ex.Match( Succ: x => Succ(x).runFin, Fail: e => Fail(e).runFin))); /// /// Monad bi-bind operation /// /// Fail state mapping function /// `FinT` public FinT BindFail(Func> Fail) => BiBind(Fail, FinT.Succ); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // SelectMany // /// /// Monad bind operation /// /// Monadic bind function /// Projection function /// Intermediate bound value type /// Target bound value type /// `FinT` public FinT SelectMany(Func, B>> bind, Func project) => SelectMany(x => bind(x).As(), project); /// /// Monad bind operation /// /// Monadic bind function /// Projection function /// Intermediate bound value type /// Target bound value type /// `FinT` public FinT SelectMany(Func> bind, Func project) => Bind(x => bind(x).Map(y => project(x, y))); /// /// Monad bind operation /// /// Monadic bind function /// Projection function /// Intermediate bound value type /// Target bound value type /// `FinT` public FinT SelectMany(Func> bind, Func project) => SelectMany(x => FinT.lift(bind(x)), project); /// /// Monad bind operation /// /// Monadic bind function /// Projection function /// Intermediate bound value type /// Target bound value type /// `FinT` public FinT SelectMany(Func> bind, Func project) => SelectMany(x => FinT.lift(bind(x)), project); /// /// Monad bind operation /// /// Monadic bind function /// Projection function /// Intermediate bound value type /// Target bound value type /// `FinT` public FinT SelectMany(Func> bind, Func project) => Map(x => project(x, bind(x).Value)); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Operators // public static implicit operator FinT(Fin ma) => FinT.lift(ma); public static implicit operator FinT(Pure ma) => FinT.Succ(ma.Value); public static implicit operator FinT(Fail ma) => FinT.lift(new Fin.Fail(ma.Value)); public static implicit operator FinT(Error ma) => FinT.Fail(ma); public static implicit operator FinT(IO ma) => FinT.liftIOMaybe(ma); public static implicit operator FinT(Lift ma) => FinT.liftIOMaybe(ma); public static implicit operator FinT(Lift ma) => FinT.liftIOMaybe(ma); public static implicit operator FinT(IO> ma) => FinT.liftIOMaybe(ma); public OptionT ToOption() => new(runFin.Map(ma => ma.ToOption())); public EitherT ToEither() => new(runFin.Map(ma => ma.ToEither())); public ValidationT ToValidation() => new(_ => runFin.Map(ma => ma.ToValidation())); } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/FinT/Operators/FinT.Operators.Applicative.cs ================================================ using System; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; public static partial class FinTExtensions { extension(K, A> self) where M : Monad { /// /// Applicative sequence operator /// public static FinT operator >>> (K, A> ma, K, B> mb) => +ma.Action(mb); /// /// Applicative apply operator /// public static FinT operator * (K, Func> mf, K, A> ma) => mf.Apply(ma); /// /// Applicative apply operator /// public static FinT operator * (K, A> ma, K, Func> mf) => mf.Apply(ma); } extension(K, A> self) where M : Monad { /// /// Applicative apply operator /// public static FinT> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static FinT> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) where M : Monad { /// /// Applicative apply operator /// public static FinT>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static FinT>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) where M : Monad { /// /// Applicative apply operator /// public static FinT>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static FinT>>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) where M : Monad { /// /// Applicative apply operator /// public static FinT>>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static FinT>>>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) where M : Monad { /// /// Applicative apply operator /// public static FinT>>>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static FinT>>>>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) where M : Monad { /// /// Applicative apply operator /// public static FinT>>>>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static FinT>>>>>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) where M : Monad { /// /// Applicative apply operator /// public static FinT>>>>>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static FinT>>>>>>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) where M : Monad { /// /// Applicative apply operator /// public static FinT>>>>>>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static FinT>>>>>>>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) where M : Monad { /// /// Applicative apply operator /// public static FinT>>>>>>>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static FinT>>>>>>>>> operator *( K, A> ma, K, Func> mf) => curry * mf * ma; } } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/FinT/Operators/FinT.Operators.Choice.cs ================================================ using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt; public static partial class FinTExtensions { extension(K, A> self) where M : Monad { public static FinT operator |(K, A> lhs, K, A> rhs) => +lhs.Choose(rhs); public static FinT operator |(K, A> lhs, Pure rhs) => +lhs.Choose(FinT.lift(rhs.ToFin())); } } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/FinT/Operators/FinT.Operators.Fallible.cs ================================================ using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt; public static partial class FinTExtensions { extension(K, A> self) where M : Monad { public static FinT operator |(K, A> lhs, CatchM, A> rhs) => +lhs.Catch(rhs); public static FinT operator |(K, A> lhs, Fail rhs) => +lhs.Catch(rhs); public static FinT operator |(K, A> lhs, Error rhs) => +lhs.Catch(rhs); } } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/FinT/Operators/FinT.Operators.Final.cs ================================================ namespace LanguageExt.Traits; public static class FinTExtensions { extension(K, A> _) where M : Monad, Final { /// /// Run a `finally` operation after the main operation regardless of whether it succeeds or not. /// /// Primary operation /// Finally operation /// Result of primary operation public static FinT operator |(K, A> lhs, Finally rhs) => new (lhs.As().runFin.Finally(rhs.Operation)); } } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/FinT/Operators/FinT.Operators.Functor.cs ================================================ using System; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; public static partial class FinTExtensions { extension(K, A> _) where M : Monad { /// /// Functor map operator /// public static FinT operator *(Func f, K, A> ma) => +ma.Map(f); /// /// Functor map operator /// public static FinT operator *(K, A> ma, Func f) => +ma.Map(f); } extension(K, A> _) where M : Monad { /// /// Functor map operator /// public static FinT> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static FinT> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) where M : Monad { /// /// Functor map operator /// public static FinT>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static FinT>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) where M : Monad { /// /// Functor map operator /// public static FinT>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static FinT>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) where M : Monad { /// /// Functor map operator /// public static FinT>>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static FinT>>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) where M : Monad { /// /// Functor map operator /// public static FinT>>>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static FinT>>>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) where M : Monad { /// /// Functor map operator /// public static FinT>>>>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static FinT>>>>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) where M : Monad { /// /// Functor map operator /// public static FinT>>>>>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static FinT>>>>>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) where M : Monad { /// /// Functor map operator /// public static FinT>>>>>>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static FinT>>>>>>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) where M : Monad { /// /// Functor map operator /// public static FinT>>>>>>>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static FinT>>>>>>>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/FinT/Operators/FinT.Operators.Monad.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public static partial class FinTExtensions { extension(K, A> self) where M : Monad { /// /// Monad bind operator /// /// Monad to bind /// Binding function /// Mapped monad public static FinT operator >> (K, A> ma, Func, B>> f) => +ma.Bind(f); /// /// Sequentially compose two actions, discarding any value produced by the first, like sequencing operators (such /// as the semicolon) in C#. /// /// First action to run /// Second action to run /// Result of the second action public static FinT operator >> (K, A> lhs, K, B> rhs) => lhs >> (_ => rhs); } extension(K, A> self) where M : MonadIO { /// /// Monad bind operator /// /// Monad to bind /// Binding function /// Mapped monad public static FinT operator >> (K, A> ma, Func> f) => +ma.Bind(x => +f(x)); /// /// Sequentially compose two actions, discarding any value produced by the first, like sequencing operators (such /// as the semicolon) in C#. /// /// First action to run /// Second action to run /// Result of the second action public static FinT operator >> (K, A> lhs, K rhs) => lhs >> (_ => rhs); } extension(K, A> self) where M : Monad { /// /// Sequentially compose two actions. The second action is a unit-returning action, so the result of the /// first action is propagated. /// /// First action to run /// Second action to run /// Result of the first action public static FinT operator >> (K, A> lhs, K, Unit> rhs) => lhs >> (x => (_ => x) * rhs); } extension(K, A> self) where M : MonadIO { /// /// Sequentially compose two actions. The second action is a unit-returning action, so the result of the /// first action is propagated. /// /// First action to run /// Second action to run /// Result of the first action public static FinT operator >> (K, A> lhs, K rhs) => lhs >> (x => (_ => x) * rhs); } } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/FinT/Operators/FinT.Operators.SemigroupK.cs ================================================ using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt; public static partial class FinExtensions { extension(K, A> _) where M : Monad { /// /// Semigroup combine operator: an associative binary operation. /// /// Left-hand side operand /// Right-hand side operand /// public static FinT operator +(K, A> lhs, K, A> rhs) => +lhs.Combine(rhs); /// /// Semigroup combine operator: an associative binary operation. /// /// Left-hand side operand /// Right-hand side operand /// public static FinT operator +(K, A> lhs, Pure rhs) => +lhs.Combine(FinT.lift(rhs.ToFin())); /// /// Semigroup combine operator: an associative binary operation. /// /// Left-hand side operand /// Right-hand side operand /// public static FinT operator +(K, A> lhs, Fail rhs) => +lhs.Combine(FinT.Fail(rhs.Value)); /// /// Semigroup combine operator: an associative binary operation. /// /// Left-hand side operand /// Right-hand side operand /// public static FinT operator +(K, A> lhs, Error rhs) => +lhs.Combine(FinT.Fail(rhs)); } } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/FinT/Operators/FinT.Operators.cs ================================================ using LanguageExt.Traits; namespace LanguageExt; public static partial class FinTExtensions { extension(K, A> _) where M : Monad { /// /// Downcast operator /// public static FinT operator +(K, A> ma) => (FinT)ma; /// /// Downcast operator /// public static FinT operator >> (K, A> ma, Lower lower) => +ma; } } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/FinT/Trait/FinT.TraitImpl.cs ================================================ using System; using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt; /// /// Trait implementation for `FinT` /// /// Given monad trait public partial class FinT : MonadT, M>, Fallible>, MonoidK>, Alternative>, Natural, EitherT>, Natural, OptionT>, Natural, TryT>, MonadIO> where M : Monad { static K, B> Monad>.Bind(K, A> ma, Func, B>> f) => ma.As().Bind(f); static K, B> Monad>.Recur(A value, Func, Next>> f) => new FinT( M.Recur>( value, a => f(a).As() .runFin .Map(e => e switch { Fin>.Fail(var err) => Next.Done>(err), Fin>.Succ({ IsDone: true } n) => Next.Done>(n.Done), Fin>.Succ({ IsLoop: true } n) => Next.Loop>(n.Loop), _ => throw new NotSupportedException() }))); static K, B> Functor>.Map(Func f, K, A> ma) => ma.As().Map(f); static K, A> Applicative>.Pure(A value) => FinT.Succ(value); static K, B> Applicative>.Apply(K, Func> mf, K, A> ma) => new FinT(((Fin> ff, Fin fa) => ff.Apply(fa)) * mf.As().runFin * ma.As().runFin); static K, B> Applicative>.Apply(K, Func> mf, Memo, A> ma) => new FinT(((Fin> ff, Fin fa) => ff.Apply(fa)) * mf.As().runFin * ma.Value.As().runFin); static K, A> MonadT, M>.Lift(K ma) => FinT.lift(ma); static K, A> MonadIO>.LiftIO(IO ma) => FinT.lift(M.LiftIOMaybe(ma)); static K, A> MonoidK>.Empty() => FinT.Fail(Error.Empty); static K, A> Alternative>.Empty() => FinT.Fail(Error.Empty); static K, A> SemigroupK>.Combine(K, A> ma, K, A> mb) => new FinT( M.Bind(ma.As().runFin, ea => ea switch { Fin.Succ => M.Pure(ea), Fin.Fail => mb.As().runFin.Map(fb => ea.Combine(fb).As()), _ => M.Pure(ea) })); static K, A> Choice>.Choose(K, A> ma, K, A> mb) => new FinT( M.Bind(ma.As().runFin, ea => ea switch { Fin.Succ => M.Pure(ea), Fin.Fail => mb.As().runFin, _ => M.Pure(ea) })); static K, A> Choice>.Choose(K, A> ma, Memo, A> mb) => new FinT( M.Bind(ma.As().runFin, ea => ea switch { Fin.Succ => M.Pure(ea), Fin.Fail => mb.Value.As().runFin, _ => M.Pure(ea) })); static K, A> Fallible>.Fail(Error error) => FinT.Fail(error); static K, A> Fallible>.Catch( K, A> fa, Func Predicate, Func, A>> Fail) => fa.As().BindFail(l => Predicate(l) ? Fail(l).As() : FinT.Fail(l)); static K, A> Natural, EitherT>.Transform(K, A> fa) => new EitherT(fa.As().runFin.Map(Natural.transform, A>).Map(ma => ma.As())); static K, A> Natural, OptionT>.Transform(K, A> fa) => new OptionT(fa.As().runFin.Map(Natural.transform).Map(ma => ma.As())); static K, A> Natural, TryT>.Transform(K, A> fa) => new TryT(fa.As().runFin.Map(Natural.transform).Map(ma => ma.As())); } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/Nullable/Nullable.Prelude.cs ================================================ using System; using System.Collections.Generic; using System.Threading.Tasks; using System.Diagnostics.Contracts; using LanguageExt.Traits; namespace LanguageExt; public static partial class Prelude { /// /// Convert NullableT to OptionT /// /// /// Value to convert /// OptionT with Some or None, depending on HasValue [Pure] public static Option toOption(T? self) where T : struct => self.HasValue ? Some(self.Value) : None; /// /// Match the two states of the Nullable and return a non-null R. /// /// Return type /// Some handler /// None handler /// A non-null R [Pure] public static R match(T? self, Func Some, Func None) where T : struct => self.HasValue ? Some(self.Value) : None(); /// /// Match the two states of the Nullable and return a promise for an R. /// /// Return type /// Some handler /// None handler /// A promise to return an R public static async Task matchAsync(T? self, Func> Some, Func None) where T : struct => self.HasValue ? await Some(self.Value) : None(); /// /// Match the two states of the Nullable and return a promise for an R. /// /// Return type /// Some handler /// None handler /// A promise to return an R public static async Task matchAsync(T? self, Func> Some, Func> None) where T : struct => self.HasValue ? await Some(self.Value) : await None(); /// /// Match the two states of the Nullable T /// /// Some match /// None match public static Unit match(T? self, Action Some, Action None) where T : struct { if (self.HasValue) { Some(self.Value); } else { None(); } return Unit.Default; } /// /// Invokes the someHandler if Nullable is in the Some state, otherwise nothing /// happens. /// public static Unit ifSome(T? self, Action someHandler) where T : struct { if (self.HasValue) { someHandler(self.Value); } return unit; } /// /// Invokes the someHandler if Nullable is in the Some state, otherwise nothing /// happens. /// public static Unit ifSome(T? self, Func someHandler) where T : struct { if (self.HasValue) { someHandler(self.Value); } return unit; } [Pure] public static T ifNone(T? self, Func None) where T : struct => self ?? None(); [Pure] public static T ifNone(T? self, T noneValue) where T : struct => self ?? noneValue; [Pure] public static Either toEither(T? self, L defaultLeftValue) where T : struct => self.HasValue ? Right(self.Value) : Left(defaultLeftValue); [Pure] public static Either toEither(T? self, Func Left) where T : struct => self.HasValue ? Right(self.Value) : Left(Left()); /// /// Append the Some(x) of one option to the Some(y) of another. /// For numeric values the behaviour is to sum the Somes (lhs + rhs) /// For string values the behaviour is to concatenate the strings /// For Lst/Stck/Que values the behaviour is to concatenate the lists /// For Map or Set values the behaviour is to merge the sets /// Otherwise if the T type derives from IAppendable then the behaviour /// is to call lhs.Append(rhs); /// /// Left-hand side of the operation /// Right-hand side of the operation /// lhs + rhs [Pure] public static T? append(T? lhs, T? rhs) where T : struct, Semigroup { if (!lhs.HasValue && !rhs.HasValue) return lhs; // None + None = None if (!rhs.HasValue) return lhs; // Value + None = Value if (!lhs.HasValue) return rhs; // None + Value = Value return lhs.Value.Combine(rhs.Value); } /// /// Sum the Some(x) of one nullable from the Some(y) of another. /// For numeric values the behaviour is to find the subtract between the Somes (lhs - rhs) /// For Lst values the behaviour is to remove items in the rhs from the lhs /// For Map or Set values the behaviour is to remove items in the rhs from the lhs /// Otherwise if the T type derives from ISubtractable then the behaviour /// is to call lhs.Plus(rhs); /// /// Left-hand side of the operation /// Right-hand side of the operation /// lhs - rhs [Pure] public static T? plus(T? lhs, T? rhs) where T : struct where NUM : Arithmetic { if (!lhs.HasValue) return rhs; if (!rhs.HasValue) return lhs; return NUM.Add(lhs.Value, rhs.Value); } /// /// Subtract the Some(x) of one nullable from the Some(y) of another. /// For numeric values the behaviour is to find the subtract between the Somes (lhs - rhs) /// For Lst values the behaviour is to remove items in the rhs from the lhs /// For Map or Set values the behaviour is to remove items in the rhs from the lhs /// Otherwise if the T type derives from ISubtractable then the behaviour /// is to call lhs.Subtract(rhs); /// /// Left-hand side of the operation /// Right-hand side of the operation /// lhs - rhs [Pure] public static T? subtract(T? lhs, T? rhs) where T : struct where NUM : Arithmetic { if (!lhs.HasValue) return rhs; if (!rhs.HasValue) return lhs; return NUM.Subtract(lhs.Value, rhs.Value); } /// /// Find the product of the Somes. /// For numeric values the behaviour is to multiply the Somes (lhs * rhs) /// For Lst values the behaviour is to multiply all combinations of values in both lists /// to produce a new list /// Otherwise if the T type derives from IMultiplicable then the behaviour /// is to call lhs.Product(rhs); /// /// Left-hand side of the operation /// Right-hand side of the operation /// lhs * rhs [Pure] public static T? product(T? lhs, T? rhs) where T : struct where NUM : Arithmetic { if (!lhs.HasValue) return lhs; // zero * rhs = zero if (!rhs.HasValue) return rhs; // lhs * zero = zero return NUM.Multiply(lhs.Value, rhs.Value); } /// /// Divide the Somes. /// For numeric values the behaviour is to divide the Somes (lhs / rhs) /// For Lst values the behaviour is to divide all combinations of values in both lists /// to produce a new list /// Otherwise if the T type derives from IDivisible then the behaviour /// is to call lhs.Divide(rhs); /// /// Left-hand side of the operation /// Right-hand side of the operation /// lhs / rhs [Pure] public static T? divide(T? lhs, T? rhs) where T : struct where NUM : Num { if (!lhs.HasValue) return lhs; // zero / rhs = zero if (!rhs.HasValue) return rhs; // lhs / zero = undefined: zero return NUM.Divide(lhs.Value, rhs.Value); } /// /// Extracts from a list of 'Option' all the 'Some' elements. /// All the 'Some' elements are extracted in order. /// [Pure] public static IEnumerable somes(IEnumerable self) where T : struct { foreach (var item in self) { if (item.HasValue) { yield return item.Value; } } } /// /// Iterate Nullable. Imagine the item has zero or one items depending on whether /// it's in a None state or not. /// /// Action to invoke with the value if not in None state public static Unit iter(T? self, Action action) where T : struct { if (self.HasValue) action(self.Value); return default; } /// /// Returns 1 if there is a value, 0 otherwise /// /// 1 if there is a value, 0 otherwise [Pure] public static int count(T? self) where T : struct => self.HasValue ? 1 : 0; /// /// ForAll Nullable. Imagine the item has zero or one items depending on whether /// it's in a None state or not. This function runs a predicate against the value /// if it exists, returns true if it doesn't (because the predicate holds 'for all' /// items). /// /// Predicate [Pure] public static bool forall(T? self, Func pred) where T : struct => !self.HasValue || pred(self.Value); /// /// ForAll Nullable. Imagine the item has zero or one items depending on whether /// it's in a None state or not. This function runs a predicate against the value /// if it exists, returns true if it doesn't (because the predicate holds 'for all' /// items). /// /// Some predicate /// None predicate [Pure] public static bool forall(T? self, Func Some, Func None) where T : struct => self.HasValue ? Some(self.Value) : None(); /// /// Exists Nullable. Imagine the item has zero or one items depending on whether /// it's in a None state or not. This function runs a predicate against the value /// if it exists, returns false if it doesn't. /// /// Predicate [Pure] public static bool exists(T? self, Func pred) where T : struct => self.HasValue && pred(self.Value); /// /// Exists Nullable. Imagine the item has zero or one items depending on whether /// it's in a None state or not. This function runs a predicate against the value /// if it exists, returns false if it doesn't. /// /// Some predicate /// None predicate [Pure] public static bool exists(T? self, Func Some, Func None) where T : struct => self.HasValue ? Some(self.Value) : None(); [Pure] public static R? map(T? self, Func mapper) where T : struct where R : struct => self.HasValue ? mapper(self.Value) : default(R?); [Pure] public static R? map(T? self, Func Some, Func None) where T : struct where R : struct => self.HasValue ? Some(self.Value) : default(R?); [Pure] public static T? filter(T? self, Func pred) where T : struct => self.HasValue ? pred(self.Value) ? self : default : self; [Pure] public static T? filter(T? self, Func Some, Func None) where T : struct => self.HasValue ? Some(self.Value) ? self : default : None() ? self : default; [Pure] public static R? bind(T? self, Func binder) where T : struct where R : struct => self.HasValue ? binder(self.Value) : default; [Pure] public static R? bind(T? self, Func Some, Func None) where T : struct where R : struct => self.HasValue ? Some(self.Value) : None(); [Pure] public static int sum(int? self) => self ?? 0; } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/Nullable/README.md ================================================ The `Nullable` extensions turns the `Nullable` type from .NET into a monad. This means you can use them in LINQ expressions, just like the other monadic types in this library. There are natural transformation functions to help convert from a nullable into other types, i.e. int? x = ... Option mx = x.ToOption(); ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/Option/Extensions/Option.Extensions.MapApply.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public static partial class OptionExtensions { /// /// Functor map operation /// /// /// Unwraps the value within the functor, passes it to the map function `f` provided, and /// then takes the mapped value and wraps it back up into a new functor. /// /// Functor to map /// Mapping function /// Mapped functor public static Option Map(this Func f, K ma) => Functor.map(f, ma).As(); /// /// Functor map operation /// /// /// Unwraps the value within the functor, passes it to the map function `f` provided, and /// then takes the mapped value and wraps it back up into a new functor. /// /// Functor to map /// Mapping function /// Mapped functor public static Option Map(this Func f, Option ma) => Functor.map(f, ma).As(); /// /// Applicative action: runs the first applicative, ignores the result, and returns the second applicative /// public static Option Action(this Option ma, K mb) => Applicative.action(ma, mb).As(); /// /// Applicative action: runs the first applicative, ignores the result, and returns the second applicative /// public static Option Action(this K ma, K mb) => Applicative.action(ma, mb).As(); /// /// Applicative functor apply operation /// /// /// Unwraps the value within the `ma` applicative-functor, passes it to the unwrapped function(s) within `mf`, and /// then takes the resulting value and wraps it back up into a new applicative-functor. /// /// Value(s) applicative functor /// Mapping function(s) /// Mapped applicative functor public static Option Apply(this Option> mf, K ma) => Applicative.apply(mf, ma).As(); /// /// Applicative functor apply operation /// /// /// Unwraps the value within the `ma` applicative-functor, passes it to the unwrapped function(s) within `mf`, and /// then takes the resulting value and wraps it back up into a new applicative-functor. /// /// Value(s) applicative functor /// Mapping function(s) /// Mapped applicative functor public static Option Apply(this K> mf, K ma) => Applicative.apply(mf, ma).As(); } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/Option/Extensions/Option.Extensions.cs ================================================ using System; using LanguageExt.Traits; using static LanguageExt.Prelude; using static LanguageExt.Trait; using System.Diagnostics.Contracts; using System.Collections.Generic; using System.Runtime.CompilerServices; namespace LanguageExt; /// /// Extension methods for Option /// public static partial class OptionExtensions { public static Option As(this K ma) => (Option)ma; [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Validation ToValidation(Option ma, F defaultFailureValue) where F : Monoid => ma.IsSome ? Validation.Success(ma.Value!) : Validation.Fail(defaultFailureValue); /// /// Monadic join /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Option Flatten(this Option> ma) => ma.Bind(identity); /// /// Extracts from a list of `Option` all the `Some` elements. /// All the `Some` elements are extracted in order. /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static IEnumerable Somes(this IEnumerable> self) { foreach (var item in self) { if (item.IsSome) { yield return item.Value!; } } } /// /// Extracts from a list of `Option` all the `Some` elements. /// All the `Some` elements are extracted in order. /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Seq Somes(this Seq> self) { IEnumerable ToSequence(Seq> items) { foreach (var item in items) { if (item.IsSome) { yield return item.Value!; } } } return toSeq(ToSequence(self)); } /// /// Add the bound values of x and y, uses an Add trait to provide the add /// operation for type A. For example x.Add〈TInteger, int〉(y) /// /// Add of A /// Bound value type /// Left hand side of the operation /// Right hand side of the operation /// An option with y added to x [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Option Add(this Option x, Option y) where ARITH : Arithmetic => from a in x from b in y select plus(a, b); /// /// Find the difference between the two bound values of x and y, uses a Subtract trait /// to provide the subtract operation for type A. For example x.Subtract〈TInteger, int〉(y) /// /// Subtract of A /// Bound value type /// Left hand side of the operation /// Right hand side of the operation /// An option with the difference between x and y [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Option Subtract(this Option x, Option y) where ARITH : Arithmetic => from a in x from b in y select subtract(a, b); /// /// Find the product between the two bound values of x and y, uses a Product trait /// to provide the product operation for type A. For example x.Product〈TInteger, int〉(y) /// /// Product of A /// Bound value type /// Left hand side of the operation /// Right hand side of the operation /// An option with the product of x and y [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Option Product(this Option x, Option y) where ARITH : Arithmetic => from a in x from b in y select product(a, b); /// /// Divide the two bound values of x and y, uses a Divide trait to provide the divide /// operation for type A. For example x.Divide〈TDouble, double〉(y) /// /// Divide of A /// Bound value type /// Left hand side of the operation /// Right hand side of the operation /// An option x / y [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Option Divide(this Option x, Option y) where NUM : Num => from a in x from b in y select divide(a, b); /// /// Convert the Option type to a Nullable of A /// /// Type of the bound value /// Option to convert /// Nullable of A [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static A? ToNullable(this Option ma) where A : struct => ma.IsNone ? null : ma.Value; /// /// Match for an optional boolean /// /// Optional boolean /// Match for Some(true) /// Match for Some(false) /// Match for None /// /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static R Match(this Option ma, Func True, Func False, Func None) => ma.Match(Some: x => x ? True() : False(), None: None()); /// /// Match over a list of options /// /// Type of the bound values /// Result type /// List of options to match against /// Operation to perform when an Option is in the Some state /// Operation to perform when an Option is in the None state /// An enumerable of results of the match operations [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static IEnumerable Match(this IEnumerable> list, Func> Some, Func> None) => match(list, Some, None); /// /// Match over a list of options /// /// Type of the bound values /// Result type /// List of options to match against /// Operation to perform when an Option is in the Some state /// Default if the list is empty /// An enumerable of results of the match operations [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static IEnumerable Match(this IEnumerable> list, Func> Some, IEnumerable None) => match(list, Some, () => None); } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/Option/Operators/Option.Operators.Applicative.cs ================================================ using System; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; public static partial class OptionExtensions { extension(K self) { /// /// Applicative sequence operator /// public static Option operator >>> (K ma, K mb) => ma.Action(mb).As(); /// /// Applicative apply operator /// public static Option operator * (K> mf, K ma) => mf.Apply(ma); /// /// Applicative apply operator /// public static Option operator * (K ma, K> mf) => mf.Apply(ma); } extension(K self) { /// /// Applicative apply operator /// public static Option> operator * ( K> mf, K ma) => curry * mf * ma; /// /// Applicative apply operator /// public static Option> operator * ( K ma, K> mf) => curry * mf * ma; } extension(K self) { /// /// Applicative apply operator /// public static Option>> operator * ( K> mf, K ma) => curry * mf * ma; /// /// Applicative apply operator /// public static Option>> operator * ( K ma, K> mf) => curry * mf * ma; } extension(K self) { /// /// Applicative apply operator /// public static Option>>> operator * ( K> mf, K ma) => curry * mf * ma; /// /// Applicative apply operator /// public static Option>>> operator * ( K ma, K> mf) => curry * mf * ma; } extension(K self) { /// /// Applicative apply operator /// public static Option>>>> operator * ( K> mf, K ma) => curry * mf * ma; /// /// Applicative apply operator /// public static Option>>>> operator * ( K ma, K> mf) => curry * mf * ma; } extension(K self) { /// /// Applicative apply operator /// public static Option>>>>> operator * ( K> mf, K ma) => curry * mf * ma; /// /// Applicative apply operator /// public static Option>>>>> operator * ( K ma, K> mf) => curry * mf * ma; } extension(K self) { /// /// Applicative apply operator /// public static Option>>>>>> operator * ( K> mf, K ma) => curry * mf * ma; /// /// Applicative apply operator /// public static Option>>>>>> operator * ( K ma, K> mf) => curry * mf * ma; } extension(K self) { /// /// Applicative apply operator /// public static Option>>>>>>> operator * ( K> mf, K ma) => curry * mf * ma; /// /// Applicative apply operator /// public static Option>>>>>>> operator * ( K ma, K> mf) => curry * mf * ma; } extension(K self) { /// /// Applicative apply operator /// public static Option>>>>>>>> operator * ( K> mf, K ma) => curry * mf * ma; /// /// Applicative apply operator /// public static Option>>>>>>>> operator * ( K ma, K> mf) => curry * mf * ma; } extension(K self) { /// /// Applicative apply operator /// public static Option>>>>>>>>> operator * ( K> mf, K ma) => curry * mf * ma; /// /// Applicative apply operator /// public static Option>>>>>>>>> operator *( K ma, K> mf) => curry * mf * ma; } } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/Option/Operators/Option.Operators.Choice.cs ================================================ using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt; public static partial class OptionExtensions { extension(Option self) { public static Option operator |(Option lhs, Option rhs) => +lhs.Choose(rhs); public static Option operator |(Option lhs, Pure rhs) => +lhs.Choose(rhs.ToOption()); } extension(K self) { public static Option operator |(K lhs, K rhs) => +lhs.Choose(rhs); public static Option operator |(K lhs, Pure rhs) => +lhs.Choose(rhs.ToOption()); } } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/Option/Operators/Option.Operators.Fallible.cs ================================================ using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt; public static partial class OptionExtensions { extension(Option self) { public static Option operator |(Option lhs, CatchM rhs) => +lhs.Catch(rhs); public static Option operator |(Option lhs, Fail rhs) => +lhs.Catch(rhs); public static Option operator |(Option lhs, Unit rhs) => +lhs.Catch(rhs); } extension(K self) { public static Option operator |(K lhs, CatchM rhs) => +lhs.Catch(rhs); public static Option operator |(K lhs, Fail rhs) => +lhs.Catch(rhs); public static Option operator |(K lhs, Unit rhs) => +lhs.Catch(rhs); } } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/Option/Operators/Option.Operators.Functor.cs ================================================ using System; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; public static partial class OptionExtensions { extension(K _) { /// /// Functor map operator /// public static Option operator *(Func f, K ma) => +ma.Map(f); /// /// Functor map operator /// public static Option operator *(K ma, Func f) => +ma.Map(f); } extension(K _) { /// /// Functor map operator /// public static Option> operator * ( Func f, K ma) => curry(f) * ma; /// /// Functor map operator /// public static Option> operator * ( K ma, Func f) => curry(f) * ma; } extension(K _) { /// /// Functor map operator /// public static Option>> operator * ( Func f, K ma) => curry(f) * ma; /// /// Functor map operator /// public static Option>> operator * ( K ma, Func f) => curry(f) * ma; } extension(K _) { /// /// Functor map operator /// public static Option>>> operator * ( Func f, K ma) => curry(f) * ma; /// /// Functor map operator /// public static Option>>> operator * ( K ma, Func f) => curry(f) * ma; } extension(K _) { /// /// Functor map operator /// public static Option>>>> operator * ( Func f, K ma) => curry(f) * ma; /// /// Functor map operator /// public static Option>>>> operator * ( K ma, Func f) => curry(f) * ma; } extension(K _) { /// /// Functor map operator /// public static Option>>>>> operator * ( Func f, K ma) => curry(f) * ma; /// /// Functor map operator /// public static Option>>>>> operator * ( K ma, Func f) => curry(f) * ma; } extension(K _) { /// /// Functor map operator /// public static Option>>>>>> operator * ( Func f, K ma) => curry(f) * ma; /// /// Functor map operator /// public static Option>>>>>> operator * ( K ma, Func f) => curry(f) * ma; } extension(K _) { /// /// Functor map operator /// public static Option>>>>>>> operator * ( Func f, K ma) => curry(f) * ma; /// /// Functor map operator /// public static Option>>>>>>> operator * ( K ma, Func f) => curry(f) * ma; } extension(K _) { /// /// Functor map operator /// public static Option>>>>>>>> operator * ( Func f, K ma) => curry(f) * ma; /// /// Functor map operator /// public static Option>>>>>>>> operator * ( K ma, Func f) => curry(f) * ma; } extension(K _) { /// /// Functor map operator /// public static Option>>>>>>>>> operator * ( Func f, K ma) => curry(f) * ma; /// /// Functor map operator /// public static Option>>>>>>>>> operator * ( K ma, Func f) => curry(f) * ma; } } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/Option/Operators/Option.Operators.Monad.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public static partial class OptionExtensions { extension(K self) { /// /// Monad bind operator /// /// Monad to bind /// Binding function /// Mapped monad public static Option operator >> (K ma, Func> f) => +ma.Bind(f); /// /// Sequentially compose two actions, discarding any value produced by the first, like sequencing operators (such /// as the semicolon) in C#. /// /// First action to run /// Second action to run /// Result of the second action public static Option operator >> (K lhs, K rhs) => lhs >> (_ => rhs); } extension(K self) { /// /// Sequentially compose two actions. The second action is a unit-returning action, so the result of the /// first action is propagated. /// /// First action to run /// Second action to run /// Result of the first action public static Option operator >> (K lhs, K rhs) => lhs >> (x => (_ => x) * rhs); } } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/Option/Operators/Option.Operators.cs ================================================ using LanguageExt.Traits; namespace LanguageExt; public static partial class OptionExtensions { extension(K _) { /// /// Downcast operator /// public static Option operator +(K ma) => (Option)ma; /// /// Downcast operator /// public static Option operator >> (K ma, Lower lower) => +ma; } } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/Option/Option.Module.cs ================================================ using System.Diagnostics.Contracts; using System.Runtime.CompilerServices; namespace LanguageExt; public partial class Option { /// /// None /// public static readonly Fail None = new (default); /// /// Construct an Option of A in a Some state /// /// Value to bind, must be non-null /// Option of A [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Option Some(A value) => new (value); } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/Option/Option.cs ================================================ using System; using System.Linq; using System.Collections.Generic; using static LanguageExt.Prelude; using System.Diagnostics.Contracts; using LanguageExt.ClassInstances; using System.Runtime.Serialization; using System.Collections; using System.Runtime.CompilerServices; using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt; /// /// Discriminated union type. Can be in one of two states: /// /// Some(a) /// None /// /// /// Bound value [Serializable] public readonly struct Option : IOptional, IEquatable>, IComparable>, IComparable, ISerializable, K, Monoid> { internal readonly A? Value; internal readonly bool isSome; /// /// None /// public static readonly Option None = default; /// /// Constructor /// internal Option(A value) { Value = value; isSome = true; } /// /// Ctor that facilitates serialisation /// /// None or Some A. public Option(IEnumerable option) { var first = option.Take(1).ToArray(); isSome = first.Length == 1; Value = isSome ? first[0] : default; } Option(SerializationInfo info, StreamingContext context) { isSome = info.GetValue("IsSome", typeof(bool)) is true; if(isSome) { Value = info.GetValue("Value", typeof(A)) is A x ? x : throw new SerializationException(); } else { Value = default; } } public void GetObjectData(SerializationInfo info, StreamingContext context) { info.AddValue("IsSome", IsSome); if(IsSome) info.AddValue("Value", Value); } /// /// Reference version of option for use in pattern-matching /// /// /// /// Some = result is A /// None = result is null /// /// [Pure] public object? Case => IsSome ? Value : null; /// /// Uses the `EqDefault` instance to do an equality check on the bound value. /// To use anything other than the default call `oa.Equals〈EqA〉(ob)` /// where `EqA` is an instance derived from `Eq〈A〉` /// /// /// This uses the `EqDefault` instance for comparison of the bound `A` values. /// The `EqDefault` instance wraps up the .NET `EqualityComparer.Default` /// behaviour. /// /// The `Option` type to compare this type with /// `True` if `this` and `other` are equal [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(Option other) => Equals>(other); /// /// Uses the `EqA` instance to do an equality check on the bound value. /// /// The `Option` type to compare this type with /// `True` if `this` and `other` are equal [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(Option other) where EqA : Eq { var yIsSome = other.IsSome; var xIsNone = !isSome; var yIsNone = !yIsSome; return xIsNone && yIsNone || isSome && yIsSome && EqA.Equals(Value!, other.Value!); } /// /// Uses the `OrdDefault` instance to do an ordering comparison on the bound /// value. To use anything other than the default call `this.Compare〈OrdA〉(this, other)`, /// where `OrdA` is an instance derived from `Ord〈A〉` /// /// The `Option` type to compare `this` type with [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public int CompareTo(Option other) => CompareTo>(other); /// /// Uses the `Ord` instance provided to do an ordering comparison on the bound /// value. /// /// The `Option` type to compare `this` type with [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public int CompareTo(Option other) where OrdA : Ord { var yIsSome = other.IsSome; var xIsNone = !isSome; var yIsNone = !yIsSome; if (xIsNone && yIsNone) return 0; if (isSome && yIsNone) return 1; if (xIsNone) return -1; return OrdA.Compare(Value!, other.Value!); } /// /// Must exist here to make `operator true` work /// public static Option operator |(Option lhs, Option rhs) => lhs.Choose(rhs).As(); /// /// Explicit conversion operator from `Option〈A〉` to `A` /// /// None value [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static explicit operator A(Option ma) { var opExplicit = ma.IsSome ? ma.Value : throw new InvalidCastException("Option is not in a Some state"); return opExplicit!; } /// /// Implicit conversion operator from A to Option〈A〉 /// /// Unit value [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator Option(A? a) => Optional(a); /// /// Implicit conversion operator from None to Option〈A〉 /// /// None value [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator Option(Fail a) => default; /// /// Implicit conversion operator from None to Option〈A〉 /// /// None value [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator Option(in Unit fail) => default; /// /// Comparison operator /// /// The left hand side of the operation /// The right hand side of the operation /// True if lhs〈 rhs [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator <(Option lhs, Option rhs) => lhs.CompareTo(rhs) < 0; /// /// Comparison operator /// /// The left hand side of the operation /// The right hand side of the operation /// True if lhs〈= rhs [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator <=(Option lhs, Option rhs) => lhs.CompareTo(rhs) <= 0; /// /// Comparison operator /// /// The left hand side of the operation /// The right hand side of the operation /// True if lhs 〉rhs [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator >(Option lhs, Option rhs) => lhs.CompareTo(rhs) > 0; /// /// Comparison operator /// /// The left hand side of the operation /// The right hand side of the operation /// True if lhs 〉= rhs [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator >=(Option lhs, Option rhs) => lhs.CompareTo(rhs) >= 0; /// /// Equality operator /// /// /// This uses the EqDefault instance for comparison of the bound A values. /// The EqDefault instance wraps up the .NET EqualityComparer.Default /// behaviour. For more control over equality you can call: /// /// equals〈EQ, A〉(lhs, rhs); /// /// Where EQ is a struct derived from Eq〈A〉. For example: /// /// equals〈EqString, string〉(lhs, rhs); /// equals〈EqArray〈int〉, int[]〉(lhs, rhs); /// /// /// Left hand side of the operation /// Right hand side of the operation /// True if the values are equal [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(Option lhs, Option rhs) => lhs.Equals(rhs); /// /// Non-equality operator /// /// /// This uses the EqDefault instance for comparison of the A value. /// The EqDefault trait wraps up the .NET EqualityComparer.Default /// behaviour. For more control over equality you can call: /// /// !equals〈EQ, A〉(lhs, rhs); /// /// Where EQ is a struct derived from Eq〈A〉. For example: /// /// !equals〈EqString, string〉(lhs, rhs); /// !equals〈EqArray〈int〉, int[]〉(lhs, rhs); /// /// /// Left hand side of the operation /// Right hand side of the operation /// True if the values are equal [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(Option lhs, Option rhs) => !(lhs == rhs); /// /// Truth operator /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator true(Option value) => value.IsSome; /// /// Falsity operator /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator false(Option value) => value.IsNone; /// /// DO NOT USE - Use the Structural equality variant of this method Equals〈EQ, A〉(y) /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public override bool Equals(object? obj) => obj is Option opt && Equals(opt); /// /// Calculate the hash-code from the bound value, unless the Option is in a None /// state, in which case the hash-code will be 0 /// /// Hash-code from the bound value, unless the Option is in a None /// state, in which case the hash-code will be 0 [Pure] public override int GetHashCode() => isSome ? Value?.GetHashCode() ?? 0 : 0; [Pure] public int CompareTo(object? obj) => obj is Option t ? CompareTo(t) : 1; /// /// Get a string representation of the Option /// /// String representation of the Option [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public override string ToString() => isSome ? $"Some({Value?.ToString() ?? ""})" : "None"; /// /// Is the option in a Some state /// [Pure] public bool IsSome => isSome; /// /// Is the option in a None state /// [Pure] public bool IsNone => !isSome; /// /// Impure iteration of the bound value in the structure /// /// /// Returns the original unmodified structure /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public Option Do(Action f) { Iter(f); return this; } /// /// Projection from one value to another /// /// Resulting functor value type /// Projection function /// Mapped functor [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Option Select(Func f) => isSome ? Option.Some(f(Value!)) : default; /// /// Projection from one value to another /// /// Resulting functor value type /// Projection function /// Mapped functor [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Option Map(Func f) => isSome ? Option.Some(f(Value!)) : default; /// /// Map each element of a structure to an action, evaluate these actions from /// left to right, and collect the results. /// /// /// Traversable structure /// Applicative functor trait /// Bound value (output) [Pure] public K> Traverse(Func> f) where F : Applicative => F.Map(x => x.As(), Traversable.traverse(f, this)); /// /// Map each element of a structure to an action, evaluate these actions from /// left to right, and collect the results. /// /// /// Traversable structure /// Monad trait /// Bound value (output) [Pure] public K> TraverseM(Func> f) where M : Monad => M.Map(x => x.As(), Traversable.traverseM(f, this)); /// /// Monad bind operation /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Option Bind(Func> f) => isSome ? f(Value!) : default; /// /// Monad bind operation /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Option Bind(Func> f) => isSome ? f(Value!).As() : default; /// /// Bi-bind. Allows mapping of both monad states /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Option BiBind(Func> Some, Func> None) => isSome ? Some(Value!) : None(); /// /// Monad bind operation /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Option SelectMany( Func> bind, Func project) { if (IsNone) return default; var mb = bind(Value!); if (mb.IsNone) return default; return project(Value!, mb.Value!); } /// /// Monad bind operation /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Option SelectMany( Func> bind, Func project) => Option.None; /// /// Match operation with an untyped value for Some. This can be /// useful for serialisation and dealing with the IOptional interface /// /// The return type /// Operation to perform if the option is in a Some state /// Operation to perform if the option is in a None state /// The result of the match operation [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public R MatchUntyped(Func Some, Func None) => IsSome ? Some(Value) : None(); /// /// Get the Type of the bound value /// /// Type of the bound value [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Type GetUnderlyingType() => typeof(A); /// /// If the Option is in a `Some` state then the span will contain one itemm otherwise empty. /// /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public ReadOnlySpan ToSpan() => IsSome ? new([Value!]) : ReadOnlySpan.Empty; /// /// Convert the Option to an enumerable of zero or one items /// /// An enumerable of zero or one items [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Arr ToArray() => isSome ? Arr.create(Value!) : []; /// /// Convert the Option to an immutable list of zero or one items /// /// An immutable list of zero or one items [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Lst ToList() => isSome ? List.create(Value!) : []; /// /// Convert the Option to an enumerable sequence of zero or one items /// /// An enumerable sequence of zero or one items [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Seq ToSeq() => isSome ? [Value!] : []; /// /// Convert the `Option` to an enumerable of zero or one items /// /// An enumerable of zero or one items [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public IEnumerable AsEnumerable() => IsSome ? [Value!] : []; /// /// Convert the Option to an enumerable of zero or one items /// /// An enumerable of zero or one items [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Iterable ToIterable() => IsSome ? [Value!] : []; /// /// Convert the structure to an Eff /// /// An Eff representation of the structure [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Eff ToEff() => ToEff(Errors.None); /// /// Convert to an Option transformer with embedded IO /// /// [Pure] public OptionT ToIO() => OptionT.lift(this); /// /// Convert the structure to an `Eff` /// /// Default value if the structure is in a None state /// An Eff representation of the structure [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Eff ToEff(Error Fail) => isSome ? Pure(Value!) : Prelude.Fail(Fail); /// /// Convert the structure to a Fin /// /// A Fin representation of the structure [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Fin ToFin() => ToFin(Errors.None); /// /// Convert the structure to a Fin /// /// Default value if the structure is in a None state /// A Fin representation of the structure [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Fin ToFin(Error Fail) => isSome ? Fin.Succ(Value!) : Fin.Fail(Fail); /// /// Convert the structure to an Either /// /// Default value if the structure is in a None state /// An Either representation of the structure [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Either ToEither(L defaultLeftValue) => isSome ? Right(Value!) : Left(defaultLeftValue); /// /// Convert the structure to an Either /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Either ToEither() where L : Monoid => isSome ? Right(Value!) : Left(L.Empty); /// /// Convert the structure to an Either /// /// Function to invoke to get a default value if the /// structure is in a None state /// An Either representation of the structure [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Either ToEither(Func Left) => isSome ? Right(Value!) : Left(Left()); /// /// Convert the structure to a Validation /// /// Function to invoke to get a default value if the /// structure is in a None state /// An Validation representation of the structure [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Validation ToValidation(Func Fail) where L : Monoid => isSome ? Success(Value!) : Fail(Fail()); /// /// Convert the structure to a Validation /// /// Default value if the structure is in a None state /// An Validation representation of the structure [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Validation ToValidation(L Fail) where L : Monoid => isSome ? Success(Value!) : Fail(Fail); /// /// Convert the structure to a Validation /// /// An Validation representation of the structure [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Validation ToValidation() where L : Monoid => isSome ? Success(Value!) : Fail(L.Empty); /// /// Fluent pattern matching. Provide a Some handler and then follow /// on fluently with .None(...) to complete the matching operation. /// This is for dispatching actions, use Some〈A, B〉(...) to return a value /// from the match operation. /// /// The `Some(x)` match operation [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public SomeUnitContext Some(Action f) => new (this, f); /// /// Fluent pattern matching. Provide a Some handler and then follow /// on fluently with .None(...) to complete the matching operation. /// This is for returning a value from the match operation, to dispatch /// an action instead, use Some〈A〉(...) /// /// Match operation return value type /// The `Some(x)` match operation /// The result of the match operation [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public SomeContext Some(Func f) => new (this, f); /// /// Match the two states of the Option and return a non-null R. /// /// Return type /// Some match operation. Must not return null. /// None match operation. Must not return null. /// A non-null B [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public B Match(Func Some, Func None) => isSome ? Some(Value!) : None(); /// /// Match the two states of the Option and return a non-null R. /// /// Return type /// Some match operation. Must not return null. /// None match operation. Must not return null. /// A non-null B [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public B Match(Func Some, B None) => isSome ? Some(Value!) : None; /// /// Match the two states of the Option /// /// Some match operation /// None match operation [MethodImpl(MethodImplOptions.AggressiveInlining)] public Unit Match(Action Some, Action None) { if(isSome) { Some(Value!); } else { None(); } return default; } /// /// Invokes the action if Option is in the Some state, otherwise nothing happens. /// /// Action to invoke if Option is in the Some state [MethodImpl(MethodImplOptions.AggressiveInlining)] public Unit IfSome(Action f) { if(isSome) { f(Value!); } return default; } /// /// Invokes the f function if Option is in the Some state, otherwise nothing /// happens. /// /// Function to invoke if Option is in the Some state [MethodImpl(MethodImplOptions.AggressiveInlining)] public Unit IfSome(Func f) { if (isSome) { f(Value!); } return default; } /// /// Returns the result of invoking the None() operation if the optional /// is in a None state, otherwise the bound Some(x) value is returned. /// /// Will not accept a null return value from the None operation /// Operation to invoke if the structure is in a None state /// Result of invoking the None() operation if the optional /// is in a None state, otherwise the bound Some(x) value is returned. [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public A IfNone(Func None) => isSome ? Value! : None(); /// /// Invokes the action if Option is in the None state, otherwise nothing happens. /// /// Action to invoke if Option is in the None state public Unit IfNone(Action None) { if (IsNone) None(); return unit; } /// /// Returns the noneValue if the optional is in a None state, otherwise /// the bound Some(x) value is returned. /// /// Will not accept a null noneValue /// Value to return if in a None state /// noneValue if the optional is in a None state, otherwise /// the bound Some(x) value is returned [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public A IfNone(A noneValue) => isSome ? Value! : noneValue; /// /// /// Option types are like lists of 0 or 1 items, and therefore follow the /// same rules when folding. /// /// In the case of lists, 'Fold', when applied to a binary /// operator, a starting value(typically the left-identity of the operator), /// and a list, reduces the list using the binary operator, from left to /// right: /// /// /// Note that, since the head of the resulting expression is produced by /// an application of the operator to the first element of the list, /// 'Fold' can produce a terminating expression from an infinite list. /// /// /// Aggregate state type /// Initial state /// Folder function, applied if Option is in a Some state /// The aggregate state [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public S Fold(S state, Func folder) => isSome ? folder(state, Value!) : state; /// /// /// Option types are like lists of 0 or 1 items, and therefore follow the /// same rules when folding. /// /// In the case of lists, 'Fold', when applied to a binary /// operator, a starting value(typically the left-identity of the operator), /// and a list, reduces the list using the binary operator, from left to /// right: /// /// /// Note that, since the head of the resulting expression is produced by /// an application of the operator to the first element of the list, /// 'Fold' can produce a terminating expression from an infinite list. /// /// /// Aggregate state type /// Initial state /// Folder function, applied if Option is in a Some state /// The aggregate state [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public S FoldBack(S state, Func folder) => isSome ? folder(state, Value!) : state; /// /// /// Option types are like lists of 0 or 1 items, and therefore follow the /// same rules when folding. /// /// In the case of lists, 'Fold', when applied to a binary /// operator, a starting value(typically the left-identity of the operator), /// and a list, reduces the list using the binary operator, from left to /// right: /// /// /// Note that, since the head of the resulting expression is produced by /// an application of the operator to the first element of the list, /// 'Fold' can produce a terminating expression from an infinite list. /// /// /// Aggregate state type /// Initial state /// Folder function, applied if Option is in a Some state /// Folder function, applied if Option is in a None state /// The aggregate state [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public S BiFold(S state, Func Some, Func None) => isSome ? Some(state, Value!) : None(state, unit); /// /// /// Option types are like lists of 0 or 1 items, and therefore follow the /// same rules when folding. /// /// In the case of lists, 'Fold', when applied to a binary /// operator, a starting value(typically the left-identity of the operator), /// and a list, reduces the list using the binary operator, from left to /// right: /// /// /// Note that, since the head of the resulting expression is produced by /// an application of the operator to the first element of the list, /// 'Fold' can produce a terminating expression from an infinite list. /// /// /// Aggregate state type /// Initial state /// Folder function, applied if Option is in a Some state /// Folder function, applied if Option is in a None state /// The aggregate state [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public S BiFold(S state, Func Some, Func None) => isSome ? Some(state, Value!) : None(state); /// /// Projection from one value to another /// /// Resulting functor value type /// Projection function /// Projection function /// Mapped functor [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Option BiMap(Func Some, Func None) => Check.NullReturn( isSome ? Some(Value!) : None(unit)); /// /// Projection from one value to another /// /// Resulting functor value type /// Projection function /// Projection function /// Mapped functor [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Option BiMap(Func Some, Func None) => isSome ? Some(Value!) : None(); /// /// /// Return the number of bound values in this structure: /// /// /// None = 0 /// /// /// Some = 1 /// /// /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public int Count() => isSome ? 1 : 0; /// /// Apply a predicate to the bound value. If the Option is in a None state /// then True is returned (because the predicate applies for-all values). /// If the Option is in a Some state the value is the result of running /// applying the bound value to the predicate supplied. /// /// /// If the Option is in a None state then True is returned (because /// the predicate applies for-all values). If the Option is in a Some state /// the value is the result of running applying the bound value to the /// predicate supplied. [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool ForAll(Func pred) => !isSome || pred(Value!); /// /// Apply a predicate to the bound value. If the Option is in a None state /// then True is returned if invoking None returns True. /// If the Option is in a Some state the value is the result of running /// applying the bound value to the Some predicate supplied. /// /// Predicate to apply if in a Some state /// Predicate to apply if in a None state /// If the Option is in a None state then True is returned if /// invoking None returns True. If the Option is in a Some state the value /// is the result of running applying the bound value to the Some predicate /// supplied. [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool BiForAll(Func Some, Func None) => isSome ? Some(Value!) : None(unit); /// /// Apply a predicate to the bound value. If the Option is in a None state /// then True is returned if invoking None returns True. /// If the Option is in a Some state the value is the result of running /// applying the bound value to the Some predicate supplied. /// /// Predicate to apply if in a Some state /// Predicate to apply if in a None state /// If the Option is in a None state then True is returned if /// invoking None returns True. If the Option is in a Some state the value /// is the result of running applying the bound value to the Some predicate /// supplied. [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool BiForAll(Func Some, Func None) => isSome ? Some(Value!) : None(); /// /// Apply a predicate to the bound value. If the Option is in a None state /// then False is returned. /// If the Option is in a Some state the value is the result of running /// applying the bound value to the predicate supplied. /// /// /// If the Option is in a None state then False is returned. If the Option is in a Some state the value /// is the result of running applying the bound value to the predicate /// supplied. [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Exists(Func pred) => isSome && pred(Value!); /// /// Apply a predicate to the bound value. If the Option is in a None state /// then True is returned if invoking None returns True. /// If the Option is in a Some state the value is the result of running /// applying the bound value to the Some predicate supplied. /// /// /// If the Option is in a None state then True is returned if /// invoking None returns True. If the Option is in a Some state the value /// is the result of running applying the bound value to the Some predicate /// supplied. [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool BiExists(Func Some, Func None) => isSome ? Some(Value!) : None(unit); /// /// Apply a predicate to the bound value. If the Option is in a None state /// then True is returned if invoking None returns True. /// If the Option is in a Some state the value is the result of running /// applying the bound value to the Some predicate supplied. /// /// /// If the Option is in a None state then True is returned if /// invoking None returns True. If the Option is in a Some state the value /// is the result of running applying the bound value to the Some predicate /// supplied. [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool BiExists(Func Some, Func None) => isSome ? Some(Value!) : None(); /// /// Invoke an action for the bound value (if in a Some state) /// /// Action to invoke [MethodImpl(MethodImplOptions.AggressiveInlining)] public Unit Iter(Action Some) { if(isSome) { Some(Value!); } return unit; } /// /// Invoke an action depending on the state of the Option /// /// Action to invoke if in a Some state /// Action to invoke if in a None state [MethodImpl(MethodImplOptions.AggressiveInlining)] public Unit BiIter(Action Some, Action None) { if (isSome) { Some(Value!); } else { None(unit); } return unit; } /// /// Invoke an action depending on the state of the Option /// /// Action to invoke if in a Some state /// Action to invoke if in a None state [MethodImpl(MethodImplOptions.AggressiveInlining)] public Unit BiIter(Action Some, Action None) { if (isSome) { Some(Value!); } else { None(); } return unit; } /// /// Apply a predicate to the bound value (if in a Some state) /// /// Predicate to apply /// Some(x) if the Option is in a Some state and the predicate /// returns True. None otherwise. [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Option Filter(Func pred) => isSome && pred(Value!) ? this : default; /// /// Apply a predicate to the bound value (if in a Some state) /// /// Predicate to apply /// Some(x) if the Option is in a Some state and the predicate /// returns True. None otherwise. [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Option Where(Func pred) => isSome && pred(Value!) ? this : default; /// /// Partial application map /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Option> ParMap(Func func) => Map(curry(func)); /// /// Partial application map /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Option>> ParMap(Func func) => Map(curry(func)); //////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // `Pure` support // /// /// Monadic bind /// /// Bind function [Pure] public Option Bind(Func> f) => IsSome ? f(Value!).ToOption() : Option.None; /// /// Monadic bind /// /// Bind function [Pure] public Option Bind(Func> f) => Option.None; /// /// Monadic bind and project /// /// Bind function /// Project function [Pure] public Option SelectMany(Func> bind, Func project) => IsSome ? Option.Some(project(Value!, bind(Value!).Value)) : Option.None; [Pure] public static implicit operator Option(Pure mr) => mr.Value is null ? None : Option.Some(mr.Value); /// /// Semigroup combine /// /// Alternative to return if this is None /// This if in a Some state, `rhs` otherwise [Pure] public Option Combine(Option rhs) => IsSome ? this : rhs; /// /// Monoid empty (aka None) /// [Pure] public static Option Empty => None; } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/Option/Prelude/Option.Prelude.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics.Contracts; using LanguageExt.Traits; using System.Runtime.CompilerServices; namespace LanguageExt; public static partial class Prelude { /// /// Monadic join /// [Pure] public static Option flatten(Option> ma) => ma.Bind(identity); /// /// Subtract the Ts /// /// Left-hand side of the operation /// Right-hand side of the operation /// lhs - rhs [Pure] public static Option subtract(Option lhs, Option rhs) where NUM : Num => lhs.Subtract(rhs); /// /// Find the product of the Ts /// [Pure] public static Option product(Option lhs, Option rhs) where NUM : Num => lhs.Product(rhs); /// /// Divide the Ts /// /// Left-hand side of the operation /// Right-hand side of the operation /// lhs / rhs [Pure] public static Option divide(Option lhs, Option rhs) where NUM : Num => lhs.Divide(rhs); /// /// Add the Ts /// /// Left-hand side of the operation /// Right-hand side of the operation /// lhs / rhs [Pure] public static Option add(Option lhs, Option rhs) where NUM : Num => lhs.Add(rhs); /// /// Check if Option is in a Some state /// /// T /// Option /// True if value is in a Some state [Pure] public static bool isSome(Option value) => value.IsSome; /// /// Check if Option is in a None state /// /// T /// Option /// True if value is in a None state [Pure] public static bool isNone(Option value) => value.IsNone; /// /// 'No value' state of Option T. /// public static readonly Fail None = new (default); /// /// Create a `Some` of `A` /// /// Bound value type /// Non-null value to be made optional /// `Option〈A〉` in a `Some` state or throws `ValueIsNullException` /// if `isnull(value)`. [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Option Some(A value) => Option.Some(value); /// /// Create a `Some` of `A` from a `Nullable〈A〉` /// /// Bound value type /// Non-null value to be made optional /// `Option〈A〉` in a `Some` state or throws `ValueIsNullException` /// if `isnull(value)` [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Option Some(A? value) where A : struct => value.HasValue ? new Option(value.Value) : throw new ValueIsNullException(); /// /// Create an `Option` of `A` /// /// Bound value type /// Value to be made optional, or `null` /// If the value is `null` it will be `None` else `Some(value)` [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Option Optional(A? value) => value is null ? default : new Option(value); /// /// Create an `Option` of `A` /// /// Bound value type /// Value to be made optional, or null /// If the value is `null` it will be `None` else `Some(value)` [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Option Optional(A? value) where A : struct => value.HasValue ? new Option(value.Value) : default; /// /// Invokes the action if Option is in the Some state, otherwise nothing happens. /// /// Action to invoke if Option is in the Some state [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Unit ifSome(Option option, Action Some) => option.IfSome(Some); /// /// Returns the result of invoking the None() operation if the optional /// is in a None state, otherwise the bound Some(x) value is returned. /// /// Will not accept a null return value from the None operation /// Operation to invoke if the structure is in a None state /// Result of invoking the None() operation if the optional /// is in a None state, otherwise the bound Some(x) value is returned. [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static T ifNone(Option option, Func None) => option.IfNone(None); /// /// Returns the noneValue if the optional is in a None state, otherwise /// the bound Some(x) value is returned. /// /// Will not accept a null noneValue /// Value to return if in a None state /// noneValue if the optional is in a None state, otherwise /// the bound Some(x) value is returned [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static T ifNone(Option option, T noneValue) => option.IfNone(noneValue); /// /// Match the two states of the Option and return a non-null R. /// /// Return type /// Some match operation. Must not return null. /// None match operation. Must not return null. /// A non-null B [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static R match(Option option, Func Some, Func None) => option.Match(Some, None); /// /// Match the two states of the Option /// /// Some match operation /// None match operation [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Unit match(Option option, Action Some, Action None) => option.Match(Some, None); /// /// /// Option types are like lists of 0 or 1 items, and therefore follow the /// same rules when folding. /// /// In the case of lists, 'Fold', when applied to a binary /// operator, a starting value(typically the left-identity of the operator), /// and a list, reduces the list using the binary operator, from left to /// right: /// /// Note that, since the head of the resulting expression is produced by /// an application of the operator to the first element of the list, /// 'Fold' can produce a terminating expression from an infinite list. /// /// /// Aggregate state type /// Initial state /// Folder function, applied if Option is in a Some state /// Folder function, applied if Option is in a None state /// The aggregate state [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static S bifold(Option option, S state, Func Some, Func None) => option.BiFold(state, Some, None); /// /// /// Option types are like lists of 0 or 1 items, and therefore follow the /// same rules when folding. /// /// In the case of lists, 'Fold', when applied to a binary /// operator, a starting value(typically the left-identity of the operator), /// and a list, reduces the list using the binary operator, from left to /// right: /// /// Note that, since the head of the resulting expression is produced by /// an application of the operator to the first element of the list, /// 'Fold' can produce a terminating expression from an infinite list. /// /// /// Aggregate state type /// Initial state /// Folder function, applied if Option is in a Some state /// Folder function, applied if Option is in a None state /// The aggregate state [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static S bifold(Option option, S state, Func Some, Func None) => option.BiFold(state, Some, None); /// /// Apply a predicate to the bound value. If the Option is in a None state /// then True is returned (because the predicate applies for-all values). /// If the Option is in a Some state the value is the result of running /// applying the bound value to the predicate supplied. /// /// Predicate to apply /// If the Option is in a None state then True is returned (because /// the predicate applies for-all values). If the Option is in a Some state /// the value is the result of running applying the bound value to the /// predicate supplied. [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool forall(Option option, Func pred) => option.ForAll(pred); /// /// Apply a predicate to the bound value. If the Option is in a None state /// then True is returned if invoking None returns True. /// If the Option is in a Some state the value is the result of running /// applying the bound value to the Some predicate supplied. /// /// Predicate to apply if in a Some state /// Predicate to apply if in a None state /// If the Option is in a None state then True is returned if /// invoking None returns True. If the Option is in a Some state the value /// is the result of running applying the bound value to the Some predicate /// supplied. [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool biforall(Option option, Func Some, Func None) => option.BiForAll(Some, None); /// /// Apply a predicate to the bound value. If the Option is in a None state /// then True is returned if invoking None returns True. /// If the Option is in a Some state the value is the result of running /// applying the bound value to the Some predicate supplied. /// /// Predicate to apply if in a Some state /// Predicate to apply if in a None state /// If the Option is in a None state then True is returned if /// invoking None returns True. If the Option is in a Some state the value /// is the result of running applying the bound value to the Some predicate /// supplied. [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool biforall(Option option, Func Some, Func None) => option.BiForAll(Some, None); /// /// /// Return the number of bound values in this structure: /// /// /// None = 0 /// /// /// Some = 1 /// /// /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int count(Option option) => option.Count(); /// /// Apply a predicate to the bound value. If the Option is in a None state /// then True is returned if invoking None returns True. /// If the Option is in a Some state the value is the result of running /// applying the bound value to the Some predicate supplied. /// /// Predicate to apply /// If the Option is in a None state then True is returned if /// invoking None returns True. If the Option is in a Some state the value /// is the result of running applying the bound value to the Some predicate /// supplied. [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool exists(Option option, Func pred) => option.Exists(pred); /// /// Apply a predicate to the bound value. If the Option is in a None state /// then True is returned if invoking None returns True. /// If the Option is in a Some state the value is the result of running /// applying the bound value to the Some predicate supplied. /// /// Predicate to apply if in a Some state /// Predicate to apply if in a None state /// If the Option is in a None state then True is returned if /// invoking None returns True. If the Option is in a Some state the value /// is the result of running applying the bound value to the Some predicate /// supplied. [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool biexists(Option option, Func Some, Func None) => option.BiExists(Some, None); /// /// Apply a predicate to the bound value. If the Option is in a None state /// then True is returned if invoking None returns True. /// If the Option is in a Some state the value is the result of running /// applying the bound value to the Some predicate supplied. /// /// Predicate to apply if in a Some state /// Predicate to apply if in a None state /// If the Option is in a None state then True is returned if /// invoking None returns True. If the Option is in a Some state the value /// is the result of running applying the bound value to the Some predicate /// supplied. [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool biexists(Option option, Func Some, Func None) => option.BiExists(Some, None); /// /// Projection from one value to another /// /// Resulting functor value type /// Projection function /// Mapped functor [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Option map(Option option, Func f) => option.Map(f); /// /// Projection from one value to another /// /// Resulting functor value type /// Projection function /// Projection function /// Mapped functor [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Option bimap(Option option, Func Some, Func None) => option.BiMap(Some, None); /// /// Projection from one value to another /// /// Resulting functor value type /// Projection function /// Projection function /// Mapped functor [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Option bimap(Option option, Func Some, Func None) => option.BiMap(Some, None); /// /// Apply a predicate to the bound value (if in a Some state) /// /// Predicate to apply /// Some(x) if the Option is in a Some state and the predicate /// returns True. None otherwise. [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Option filter(Option option, Func pred) => option.Filter(pred); /// /// Monadic bind operation /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Option bind(Option option, Func> binder) => option.Bind(binder); /// /// Match the two states of the list of Options /// /// Some match operation /// None match operation [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static IEnumerable match( IEnumerable> list, Func> Some, Func> None) => list.Match( None, opt => match(opt, Some, None), (x, xs) => match(x, Some, None).ConcatFast(match(xs, Some, None))); /// /// Match the two states of the list of Options /// /// Some match operation /// None match operation [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static IEnumerable match(IEnumerable> list, Func> Some, IEnumerable None) => match(list, Some, () => None); /// /// Extracts from a list of 'Option' all the 'Some' elements. /// All the 'Some' elements are extracted in order. /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static IEnumerable somes(IEnumerable> list) => list.Somes(); /// /// Convert the Option to an immutable list of zero or one items /// /// An immutable list of zero or one items [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Lst toList(Option option) => option.ToList(); /// /// Convert the Option to an enumerable of zero or one items /// /// An enumerable of zero or one items [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Arr toArray(Option option) => option.ToArray(); } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/Option/Prelude/Option.Prelude.mapapply.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public static partial class Prelude { /// /// Functor map operation /// /// /// Unwraps the value within the functor, passes it to the map function `f` provided, and /// then takes the mapped value and wraps it back up into a new functor. /// /// Functor to map /// Mapping function /// Mapped functor public static Option map(Func f, K ma) => Functor.map(f, ma).As(); /// /// Applicative action: runs the first applicative, ignores the result, and returns the second applicative /// public static Option action(K ma, K mb) => Applicative.action(ma, mb).As(); /// /// Applicative functor apply operation /// /// /// Unwraps the value within the `ma` applicative-functor, passes it to the unwrapped function(s) within `mf`, and /// then takes the resulting value and wraps it back up into a new applicative-functor. /// /// Value(s) applicative functor /// Mapping function(s) /// Mapped applicative functor public static Option apply(K> mf, K ma) => Applicative.apply(mf, ma).As(); } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/Option/README.md ================================================ `Option` monads support either an `A` (success) value, or a `None` (no-value) option. You can think of this as an alternative to using `null` to represent a lack of a value. `null` unfortunately still allows you to `.` into the interface of the decalred type, which means there's a ticking time-bomb in every reference type. C# does now have the nullable references feature, which goes some way to removing the need for an optional type, however there's still edge cases that mean the reference types are problematic. It's also useful to build generic types and say this is an `Option` - I don't care if it's a value-type or reference-type, it's optional. And finally, there's the automatic checking of `None` values when using `Option` in LINQ expressions, or if you call `Map`. This makes working with optional values, and the implications for all of the code that works with it, fully declarative. Here we have two flavours of `Option`: 1. `Option` the default optional monad. It does not allow `null` in its `Some` case. 2. `OptionUnsafe` as above, but it does allow `null` in its `Some` case. You can construct a `Some` using the constructor functions in the `Prelude`: Option ma = Some(123); Option mb = None; ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/Option/Shared/IOptional.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace LanguageExt; public interface IOptional { bool IsSome { get; } bool IsNone { get; } R MatchUntyped(Func Some, Func None); Type GetUnderlyingType(); } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/Option/Shared/SomeContext.cs ================================================ using System; namespace LanguageExt; /// /// Provides a fluent context when calling the Some(Func) method from /// a member of the Optional〈A〉 trait. Must call None(Func) or /// None(Value) on this context to complete the matching operation. /// /// Bound optional value type /// The operation return value type public class SomeContext { readonly Option option; readonly Func someHandler; internal SomeContext(Option option, Func someHandler) { this.option = option; this.someHandler = someHandler; } /// /// The None branch of the matching operation /// /// None branch operation public B None(Func noneHandler) => option.Match(someHandler, noneHandler); /// /// The None branch of the matching operation /// /// None branch operation public B None(B noneValue) => option.Match(someHandler, noneValue); } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/Option/Shared/SomeUnitContext.cs ================================================ using System; using static LanguageExt.Prelude; namespace LanguageExt; /// /// Provides a fluent context when calling the `Some(Action)` method from /// Optional〈A〉 trait. Must call `None(Action)` or `None(Value)` on this /// context to complete the matching operation. /// /// Bound optional value type public class SomeUnitContext { readonly Option option; readonly Action someHandler; Action? noneHandler; internal SomeUnitContext(Option option, Action someHandler) { this.option = option; this.someHandler = someHandler; } /// /// The None branch of the matching operation /// /// None branch operation public Unit None(Action f) { noneHandler = f; return option.Match(HandleSome, HandleNone); } Unit HandleSome(A value) { someHandler(value); return unit; } Unit HandleNone() { noneHandler?.Invoke(); return unit; } } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/Option/Trait/Option.TraitImpl.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public partial class Option : Monad(A value) => Some(value); static K Applicative() => Option.None; static K Choice(K ma, K mb) => ma.As() switch { { IsSome: true } => ma, _ => mb }; static K Choice(K ma, Memo mb) => ma.As() switch { { IsSome: true } => ma, _ => mb.Value }; public static B Match(K fa, Func Some, Func None) => fa.As().Match(Some, None); static K Fallible.Fail(Unit _) => Option.None; static K Fallible.Catch( K fa, Func Predicate, Func> Fail) => fa.As().Match(Some: Some, None: () => Predicate(default) ? Fail(default).As() : Option.None); static K Natural.Transform(K fa) => fa.As().ToArr(); static K Natural.Transform(K fa) => fa.As().ToLst(); static K Natural.Transform(K fa) => fa.As().ToSeq(); static K Natural.Transform(K fa) => FoldableExtensions.ToIterable(fa.As()); static K Natural.Transform(K fa) => fa.As().ToEff(); static K, A> Natural>.Transform(K fa) => fa.As().ToIO(); static K Natural.Transform(K fa) => fa.As().ToFin(); static Fold Foldable(this Task> tma) => OptionT.lift(IO.liftAsync(async () => await tma.ConfigureAwait(false))) .Flatten(); /// /// Lift the task /// public static OptionT ToIO(this Task> ma) => liftIO(ma); /// /// Monadic join /// [Pure] public static OptionT Flatten(this OptionT> mma) where M : Monad => mma.Bind(identity); /// /// Monad bind operation /// /// Monadic bind function /// Projection function /// Intermediate bound value type /// Target bound value type /// `OptionT` [Pure] public static OptionT SelectMany( this K ma, Func, B>> bind, Func project) where M : Monad => OptionT.lift(ma).SelectMany(bind, project); /// /// Monad bind operation /// /// Monadic bind function /// Projection function /// Intermediate bound value type /// Target bound value type /// `OptionT` [Pure] public static OptionT SelectMany( this K ma, Func> bind, Func project) where M : Monad => OptionT.lift(ma).SelectMany(bind, project); /// /// Monad bind operation /// /// Monadic bind function /// Projection function /// Intermediate bound value type /// Target bound value type /// `OptionT` public static OptionT SelectMany( this K, A> ma, Func> bind, Func project) where M : MonadIO => ma.As().SelectMany(x => M.LiftIO(bind(x)), project); } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/OptionT/Operators/OptionT.Operators.Applicative.cs ================================================ using System; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; public static partial class OptionTExtensions { extension(K, A> self) where M : Monad { /// /// Applicative sequence operator /// public static OptionT operator >>> (K, A> ma, K, B> mb) => ma.Action(mb).As(); /// /// Applicative apply operator /// public static OptionT operator * (K, Func> mf, K, A> ma) => mf.Apply(ma); /// /// Applicative apply operator /// public static OptionT operator * (K, A> ma, K, Func> mf) => mf.Apply(ma); } extension(K, A> self) where M : Monad { /// /// Applicative apply operator /// public static OptionT> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static OptionT> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) where M : Monad { /// /// Applicative apply operator /// public static OptionT>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static OptionT>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) where M : Monad { /// /// Applicative apply operator /// public static OptionT>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static OptionT>>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) where M : Monad { /// /// Applicative apply operator /// public static OptionT>>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static OptionT>>>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) where M : Monad { /// /// Applicative apply operator /// public static OptionT>>>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static OptionT>>>>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) where M : Monad { /// /// Applicative apply operator /// public static OptionT>>>>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static OptionT>>>>>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) where M : Monad { /// /// Applicative apply operator /// public static OptionT>>>>>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static OptionT>>>>>>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) where M : Monad { /// /// Applicative apply operator /// public static OptionT>>>>>>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static OptionT>>>>>>>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) where M : Monad { /// /// Applicative apply operator /// public static OptionT>>>>>>>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static OptionT>>>>>>>>> operator *( K, A> ma, K, Func> mf) => curry * mf * ma; } } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/OptionT/Operators/OptionT.Operators.Choice.cs ================================================ using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt; public static partial class OptionTExtensions { extension(K, A> self) where M : Monad { public static OptionT operator |(K, A> lhs, K, A> rhs) => +lhs.Choose(rhs); public static OptionT operator |(K, A> lhs, Pure rhs) => +lhs.Choose(OptionT.lift(rhs.ToOption())); } } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/OptionT/Operators/OptionT.Operators.Fallible.cs ================================================ using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt; public static partial class OptionTExtensions { extension(K, A> self) where M : Monad { public static OptionT operator |(K, A> lhs, CatchM, A> rhs) => +lhs.Catch(rhs); public static OptionT operator |(K, A> lhs, Fail rhs) => +lhs.Catch(rhs); public static OptionT operator |(K, A> lhs, Unit rhs) => +lhs.Catch(rhs); } } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/OptionT/Operators/OptionT.Operators.Final.cs ================================================ namespace LanguageExt.Traits; public static class OptionTExtensions { extension(K, A> _) where M : Monad, Final { /// /// Run a `finally` operation after the main operation regardless of whether it succeeds or not. /// /// Primary operation /// Finally operation /// Result of primary operation public static OptionT operator |(K, A> lhs, Finally rhs) => new (lhs.As().runOption.Finally(rhs.Operation)); } } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/OptionT/Operators/OptionT.Operators.Functor.cs ================================================ using System; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; public static partial class OptionTExtensions { extension(K, A> _) where M : Monad { /// /// Functor map operator /// public static OptionT operator *(Func f, K, A> ma) => +ma.Map(f); /// /// Functor map operator /// public static OptionT operator *(K, A> ma, Func f) => +ma.Map(f); } extension(K, A> _) where M : Monad { /// /// Functor map operator /// public static OptionT> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static OptionT> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) where M : Monad { /// /// Functor map operator /// public static OptionT>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static OptionT>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) where M : Monad { /// /// Functor map operator /// public static OptionT>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static OptionT>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) where M : Monad { /// /// Functor map operator /// public static OptionT>>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static OptionT>>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) where M : Monad { /// /// Functor map operator /// public static OptionT>>>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static OptionT>>>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) where M : Monad { /// /// Functor map operator /// public static OptionT>>>>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static OptionT>>>>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) where M : Monad { /// /// Functor map operator /// public static OptionT>>>>>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static OptionT>>>>>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) where M : Monad { /// /// Functor map operator /// public static OptionT>>>>>>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static OptionT>>>>>>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) where M : Monad { /// /// Functor map operator /// public static OptionT>>>>>>>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static OptionT>>>>>>>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/OptionT/Operators/OptionT.Operators.Monad.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public static partial class OptionTExtensions { extension(K, A> self) where M : Monad { /// /// Monad bind operator /// /// Monad to bind /// Binding function /// Mapped monad public static OptionT operator >> (K, A> ma, Func, B>> f) => +ma.Bind(f); /// /// Sequentially compose two actions, discarding any value produced by the first, like sequencing operators (such /// as the semicolon) in C#. /// /// First action to run /// Second action to run /// Result of the second action public static OptionT operator >> (K, A> lhs, K, B> rhs) => lhs >> (_ => rhs); } extension(K, A> self) where M : Monad { /// /// Sequentially compose two actions. The second action is a unit-returning action, so the result of the /// first action is propagated. /// /// First action to run /// Second action to run /// Result of the first action public static OptionT operator >> (K, A> lhs, K, Unit> rhs) => lhs >> (x => (_ => x) * rhs); } } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/OptionT/Operators/OptionT.Operators.SemigroupK.cs ================================================ using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt; public static partial class ChronicleTExtensions { extension(K, A> self) where M : Monad, SemigroupK { /// /// Semigroup combine operator: an associative binary operation. /// /// Left-hand side operand /// Right-hand side operand /// public static OptionT operator +(K, A> lhs, K, A> rhs) => new (lhs.As().runOption + rhs.As().runOption); } extension(K, A> self) where M : Monad, SemigroupK, Applicative { /// /// Semigroup combine operator: an associative binary operation. /// /// Left-hand side operand /// Right-hand side operand /// public static OptionT operator +(K, A> lhs, Pure rhs) => new(lhs.As().runOption + M.Pure(rhs.Value).Map(Option.Some)); } extension(K, A> self) where M : Monad, SemigroupK, Fallible { /// /// Semigroup combine operator: an associative binary operation. /// /// Left-hand side operand /// Right-hand side operand /// public static OptionT operator +(K, A> lhs, Fail rhs) => new (lhs.As().runOption + M.Fail>(rhs.Value)); } } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/OptionT/Operators/OptionT.Operators.cs ================================================ using LanguageExt.Traits; namespace LanguageExt; public static partial class OptionTExtensions { extension(K, A> _) where M : Monad { /// /// Downcast operator /// public static OptionT operator +(K, A> ma) => (OptionT)ma; /// /// Downcast operator /// public static OptionT operator >> (K, A> ma, Lower lower) => +ma; } } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/OptionT/OptionT.Module.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public partial class OptionT { /// /// Lift a pure value into the monad-transformer /// /// Value to lift /// `OptionT` public static OptionT Some(A value) where M : Monad => lift(M.Pure(value)); /// /// Lift a `None` value into the monad-transformer /// /// `OptionT` public static OptionT None() where M : Monad => OptionT.None; /// /// Lifts a given monad into the transformer /// /// Monad to lift /// `OptionT` public static OptionT lift(Pure monad) where M : Monad => Some(monad.Value); /// /// Lifts a given monad into the transformer /// /// Monad to lift /// `OptionT` public static OptionT lift(Option monad) where M : Monad => new(M.Pure(monad)); /// /// Lifts a given monad into the transformer /// /// Monad to lift /// `OptionT` public static OptionT lift(Fail monad) where M : Monad => lift(Option.None); /// /// Lifts a given monad into the transformer /// /// Monad to lift /// `OptionT` public static OptionT lift(K monad) where M : Monad => new(M.Map(Option.Some, monad)); /// /// Lifts a given monad into the transformer /// /// Monad to lift /// `OptionT` public static OptionT lift(K> monad) where M : Monad => new(monad); /// /// Lifts a given monad into the transformer /// /// Monad to lift /// `OptionT` public static OptionT liftIO(IO monad) where M : MonadIO => lift(M.LiftIO(monad)); /// /// Lifts a given monad into the transformer /// /// Monad to lift /// `OptionT` internal static OptionT liftIOMaybe(IO monad) where M : Monad => lift(M.LiftIOMaybe(monad)); /// /// Lifts a given monad into the transformer /// /// Monad to lift /// `OptionT` public static OptionT liftIO(IO> monad) where M : MonadIO => lift(M.LiftIO(monad)); /// /// Lifts a given monad into the transformer /// /// Monad to lift /// `OptionT` internal static OptionT liftIOMaybe(IO> monad) where M : Monad => lift(M.LiftIOMaybe(monad)); } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/OptionT/OptionT.cs ================================================ using System; using System.Diagnostics.Contracts; using System.IO; using System.Runtime.CompilerServices; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; /// /// `OptionT` monad transformer, which allows for an optional result. /// /// Given monad trait /// Bound value type public record OptionT(K> runOption) : K, A> where M : Monad { /// /// Lift a pure value into the monad-transformer /// /// Value to lift /// `OptionT` public static readonly OptionT None = OptionT.lift(Option.None); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Match // /// /// Match the two states of the Option and return a B, which can be null. /// /// Return type /// Some match operation. May return null. /// None match operation. May return null. /// B, or null public K Match(Func Some, Func None) => M.Map(mx => mx.Match(Some, None), runOption); /// /// Match the two states of the Option /// /// Some match operation /// None match operation public K Match(Action Some, Action None) => M.Map(mx => mx.Match(Some, None), runOption); /// /// Invokes the action if Option is in the `Some` state, otherwise nothing happens. /// /// Action to invoke if Option is in the `Some` state [MethodImpl(MethodImplOptions.AggressiveInlining)] public K IfSome(Action f) => M.Map(mx => mx.IfSome(f), runOption); /// /// Invokes the f function if Option is in the `Some` state, otherwise nothing /// happens. /// /// Function to invoke if Option is in the `Some` state [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public K IfSome(Func f) => M.Map(mx => mx.IfSome(f), runOption); /// /// Returns the result of invoking the `None()` operation if the optional /// is in a None state, otherwise the bound `Some(x)` value is returned. /// /// Will not accept a null return value from the None operation /// Operation to invoke if the structure is in a None state /// Result of invoking the `None()` operation if the optional /// is in a None state, otherwise the bound `Some(x)` value is returned. [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public K IfNone(Func None) => M.Map(mx => mx.IfNone(None), runOption); /// /// Invokes the action if Option is in the None state, otherwise nothing happens. /// /// Action to invoke if Option is in the None state [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public K IfNone(Action None) => M.Map(mx => mx.IfNone(None), runOption); /// /// Returns the noneValue if the optional is in a None state, otherwise /// the bound Some(x) value is returned. /// /// Will not accept a null noneValue /// Value to return if in a None state /// noneValue if the optional is in a None state, otherwise /// the bound Some(x) value is returned [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public K IfNone(A noneValue) => M.Map(mx => mx.IfNone(noneValue), runOption); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Run // /// /// Runs the OptionT exposing the outer monad with an inner wrapped `Option` /// public K> Run() => runOption; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Map // /// /// Maps the bound monad /// /// Mapping function /// Target monad type /// Target bound value type /// Mapped monad public OptionT MapT(Func>, K>> f) where M1 : Monad => new (f(runOption)); /// /// Maps the given monad /// /// Mapping function public OptionT MapM(Func, K> f) => new(runOption.Bind( fv => fv.Match(Some: v => f(M.Pure(v)).Map(Option.Some), None: () => M.Pure(Option.None)))); /// /// Maps the bound value /// /// Mapping function /// Target bound value type /// `OptionT` public OptionT Map(Func f) => new(M.Map(mx => mx.Map(f), runOption)); /// /// Maps the bound value /// /// Mapping transducer /// Target bound value type /// `OptionT` public OptionT Select(Func f) => new(M.Map(mx => mx.Map(f), runOption)); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Bind // /// /// Monad bind operation /// /// Mapping function /// Target bound value type /// `OptionT` public OptionT Bind(Func, B>> f) => Bind(x => f(x).As()); /// /// Monad bind operation /// /// Mapping function /// Target bound value type /// `OptionT` public OptionT Bind(Func> f) => new(M.Bind(runOption, ox => ox.Match( Some: x => f(x).runOption, None: () => M.Pure(Option.None)))); /// /// Monad bi-bind operation /// /// Some state mapping function /// None state mapping function /// Target bound value type /// `OptionT` public OptionT BiBind(Func> Some, Func> None) => new(M.Bind(runOption, ox => ox.Match( Some: x => Some(x).runOption, None: () => None().runOption))); /// /// Monad bi-bind operation /// /// None state mapping function /// Target bound value type /// `OptionT` public OptionT BindNone(Func> None) => BiBind(OptionT.Some, None); /// /// Monad bind operation /// /// Mapping function /// Target bound value type /// `OptionT` public OptionT Bind(Func> f) => Map(a => f(a).Value); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // SelectMany // /// /// Monad bind operation /// /// Monadic bind function /// Projection function /// Intermediate bound value type /// Target bound value type /// `OptionT` public OptionT SelectMany(Func, B>> bind, Func project) => SelectMany(x => bind(x).As(), project); /// /// Monad bind operation /// /// Monadic bind function /// Projection function /// Intermediate bound value type /// Target bound value type /// `OptionT` public OptionT SelectMany(Func> bind, Func project) => Bind(x => bind(x).Map(y => project(x, y))); /// /// Monad bind operation /// /// Monadic bind function /// Projection function /// Intermediate bound value type /// Target bound value type /// `OptionT` public OptionT SelectMany(Func> bind, Func project) => SelectMany(x => OptionT.lift(bind(x)), project); /// /// Monad bind operation /// /// Monadic bind function /// Projection function /// Intermediate bound value type /// Target bound value type /// `OptionT` public OptionT SelectMany(Func> bind, Func project) => SelectMany(x => OptionT.lift(bind(x)), project); /// /// Monad bind operation /// /// Monadic bind function /// Projection function /// Intermediate bound value type /// Target bound value type /// `OptionT` public OptionT SelectMany(Func> bind, Func project) => Map(x => project(x, bind(x).Value)); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Operators // public static implicit operator OptionT(in Option ma) => OptionT.lift(ma); public static implicit operator OptionT(Pure ma) => Some(ma.Value); public static implicit operator OptionT(Fail ma) => OptionT.lift(Option.None); public static implicit operator OptionT(in Unit fail) => OptionT.lift(Option.None); public static implicit operator OptionT(IO ma) => OptionT.liftIOMaybe(ma); public static implicit operator OptionT(Lift ma) => OptionT.liftIOMaybe(ma); public static implicit operator OptionT(Lift ma) => OptionT.liftIOMaybe(ma); public static implicit operator OptionT(IO> ma) => OptionT.liftIOMaybe(ma); public EitherT ToEither(L left) => new(runOption.Map(ma => ma.ToEither(left))); public EitherT ToEither(Func left) => new(runOption.Map(ma => ma.ToEither(left))); public EitherT ToEither() where L : Monoid => new(runOption.Map(ma => ma.ToEither())); } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/OptionT/Prelude/OptionT.Prelude.mapapply.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public static partial class Prelude { /// /// Functor map operation /// /// /// Unwraps the value within the functor, passes it to the map function `f` provided, and /// then takes the mapped value and wraps it back up into a new functor. /// /// Functor to map /// Mapping function /// Mapped functor public static OptionT map(Func f, K, A> ma) where M : Monad => Functor.map(f, ma).As(); /// /// Applicative action: runs the first applicative, ignores the result, and returns the second applicative /// public static OptionT action(K, A> ma, OptionT mb) where M : Monad => Applicative.action(ma, mb).As(); /// /// Applicative functor apply operation /// /// /// Unwraps the value within the `ma` applicative-functor, passes it to the unwrapped function(s) within `mf`, and /// then takes the resulting value and wraps it back up into a new applicative-functor. /// /// Value(s) applicative functor /// Mapping function(s) /// Mapped applicative functor public static OptionT apply(K, Func> mf, K, A> ma) where M : Monad => Applicative.apply(mf, ma).As(); } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/OptionT/Trait/OptionT.TraitImpl.cs ================================================ using System; using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt; /// /// Trait implementation for `OptionT` /// /// Given monad trait public partial class OptionT : MonadT, M>, Alternative>, Fallible>, MonadIO> where M : Monad { static K, B> Monad>.Bind(K, A> ma, Func, B>> f) => ma.As().Bind(f); static K, B> Monad>.Recur(A value, Func, Next>> f) => new OptionT( M.Recur>( value, a => f(a).As() .runOption .Map(e => e switch { { IsNone: true } => Next.Done>(default), { IsSome: true, Value: { IsDone: true } n } => Next.Done>(n.Done), { IsSome: true, Value: { IsLoop: true } n } => Next.Loop>(n.Loop), _ => throw new NotSupportedException() }))); static K, B> Functor>.Map(Func f, K, A> ma) => ma.As().Map(f); static K, A> Applicative>.Pure(A value) => OptionT.Some(value); static K, B> Applicative>.Apply(K, Func> mf, K, A> ma) => mf.As().Bind(x => ma.As().Map(x)); static K, B> Applicative>.Apply(K, Func> mf, Memo, A> ma) => mf.As().Bind(x => ma.Value.As().Map(x)); static K, A> MonadT, M>.Lift(K ma) => OptionT.lift(ma); static K, A> MonadIO>.LiftIO(IO ma) => OptionT.lift(M.LiftIOMaybe(ma)); static K, A> Alternative>.Empty() => OptionT.None; static K, A> Choice>.Choose(K, A> ma, K, A> mb) => new OptionT( M.Bind(ma.As().runOption, ea => ea.IsSome ? M.Pure(ea) : mb.As().runOption)); static K, A> Choice>.Choose(K, A> ma, Memo, A> mb) => new OptionT( M.Bind(ma.As().runOption, ea => ea.IsSome ? M.Pure(ea) : mb.Value.As().runOption)); static K, A> Fallible>.Fail(Unit error) => OptionT.None(); static K, A> Fallible>.Catch( K, A> fa, Func Predicate, Func, A>> Fail) => fa.As().BindNone(() => Predicate(default) ? Fail(default).As() : OptionT.None); } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/README.md ================================================ _Alternative Monads_ are monadic types that can have an alternative value! What does this mean? If first we think about a tuple: (int, string) This type can represent an `int` _AND_ a `string`. Now consider the `Either` monad, this means the value can be`Left` _OR_ `Right` (`L` or `R`). So, this: Either Means either `int` _OR_ `string`. It is the natural _dual_ of tuple. In the case of `Either` the _Right_ value is considered the _bound_ value of the monad, and the _Left_ value is considered the _alternative_ value. All the other _alternative value monads_ can be seen as derivatives or specialisations of `Either`. | Type | Alternative Value Type | Bound Value Type | Notes | |--------------------------|------------------------|------------------|-------------------------------------------------------------| | `Either` | `L` | `R` | | | `Fin` | `Error` | `A` | Equivalent to `Either` | | `Try` | `Error` | `A` | Equivalent to `Either` (that catches exceptions!) | | `Option` | `None` | `A` | Equivalent to `Either` | | `Nullable` | `null` | `A` | Equivalent to `Either` | | `Validation` | `Fail` | `Succ` | `Fail` must be a `Monoid` to collect errors | > _The alternative value is usually used to carry errors, but that doesn't have to be the case. It is > important to remember that the alternative-value can be for any purpose you want._ ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/These/Extensions/These.Extensions.cs ================================================ using System; using LanguageExt.Traits; using static LanguageExt.Prelude; using NSE = System.NotSupportedException; namespace LanguageExt; public static partial class TheseExtensions { public static These As(this K, B> ma) => (These)ma; /// /// Coalesce with the provided operation /// /// Coalesce operation /// Coalesced value public static A Merge(this K, A> these, Func f) => these.As().Match(x => x, x => x, f); extension(K> theses) where F : Foldable { /// /// Select each constructor and partition them into separate lists. /// /// Partitioned sequences public (Seq This, Seq That, Seq<(A, B)> Both) Partition() => theses.Fold((This: Seq(), That: Seq(), Both: Seq<(A First, B Second)>()), (s, ts) => ts switch { These.This (var x) => (s.This.Add(x), s.That, s.Both), These.That (var y) => (s.This, s.That.Add(y), s.Both), These.Both (var x, var y) => (s.This, s.That, s.Both.Add((x, y))), _ => throw new NSE() }); /// /// Select each constructor and partition them into separate lists. /// /// Partitioned sequences public (Seq This, Seq That) Partition2() => theses.Fold((This: Seq(), That: Seq()), (state, these) => these switch { These.This (var x) => (state.This.Add(x), state.That), These.That (var y) => (state.This, state.That.Add(y)), These.Both (var x, var y) => (state.This.Add(x), state.That.Add(y)), _ => throw new NSE() }); } extension(K, B> first) where A : Semigroup where B : Semigroup { /// /// Semigroup combine /// /// Second `These` /// `These` combined using semigroup rules public These Combine(K, B> second) => Combine(first, second, Semigroup.combine, Semigroup.combine); } extension(K, B> first) { /// /// Semigroup combine /// /// Second `These` /// `These` combined using semigroup rules public These Combine(K, B> second, Func combineFst, Func combineSnd) => (first, second) switch { (These.This (var fx), These.This (var sx)) => This(combineFst(fx, sx)), (These.This (var fx), These.That (var sx)) => Both(fx, sx), (These.This (var fx), These.Both (var s1, var s2)) => Both(combineFst(fx, s1), s2), (These.That (var fx), These.This (var sx)) => Both(sx, fx), (These.That (var fx), These.That (var sx)) => That(combineSnd(fx, sx)), (These.That (var fx), These.Both (var s1, var s2)) => Both(s1, combineSnd(fx, s2)), (These.Both (var f1, var f2), These.This (var sx)) => Both(combineFst(f1, sx), f2), (These.Both (var f1, var f2), These.That (var sx)) => Both(f1, combineSnd(f2, sx)), (These.Both (var f1, var f2), These.Both (var s1, var s2)) => Both(combineFst(f1, s1), combineSnd(f2, s2)), _ => throw new NSE() }; } extension(K, Func> mf) where A : Semigroup { public These Apply(K, B> ma) => mf.Apply(ma, Semigroup.combine); } extension(K, Func> mf) { public These Apply(K, B> ma, Func combine) => (mf, ma) switch { (These>.This (var a), _) => This(a), (These>.That, These.This (var a)) => This(a), (These>.That (var f), These.That (var b)) => That(f(b)), (These>.That (var f), These.Both (var a, var b)) => Both(a, f(b)), (These>.Both (var a1, _), These.This (var a2)) => This(combine(a1, a2)), (These>.Both (var a1, var f), These.That (var b)) => Both(a1, f(b)), (These>.Both (var a1, var f), These.Both (var a, var b)) => Both(combine(a1, a), f(b)), _ => throw new NSE() }; } extension(K, B> mb) where A : Semigroup { /// /// Monad bind operation /// /// Chaining function public These Bind(Func, C>> f) => mb switch { These.This (var v) => This(v), These.That (var v) => f(v).As(), These.Both (var x, var y) => f(y) switch { These.This (var a) => This(x + a), These.That (var b) => Both(x, b), These.Both (var a, var b) => Both(x + a, b), _ => throw new NSE() }, _ => throw new NSE() }; public These SelectMany(Func, C>> bind, Func project) => mb.Bind(b => bind(b).Map(c => project(b, c))); } } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/These/Operators/These.Operators.Functor.cs ================================================ using System; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; public static partial class TheseExtensions { extension(K, A> _) { /// /// Functor map operator /// public static These operator *(Func f, K, A> ma) => +ma.Map(f); /// /// Functor map operator /// public static These operator *(K, A> ma, Func f) => +ma.Map(f); } extension(K, A> _) { /// /// Functor map operator /// public static These> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static These> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) { /// /// Functor map operator /// public static These>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static These>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) { /// /// Functor map operator /// public static These>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static These>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) { /// /// Functor map operator /// public static These>>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static These>>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) { /// /// Functor map operator /// public static These>>>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static These>>>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) { /// /// Functor map operator /// public static These>>>>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static These>>>>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) { /// /// Functor map operator /// public static These>>>>>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static These>>>>>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) { /// /// Functor map operator /// public static These>>>>>>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static These>>>>>>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) { /// /// Functor map operator /// public static These>>>>>>>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static These>>>>>>>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/These/Operators/These.Operators.SemigroupK.cs ================================================ using LanguageExt.Traits; namespace LanguageExt; public static partial class TheseExtensions { extension(K, B> _) where A : Semigroup where B : Semigroup { /// /// Semigroup combine operator: an associative binary operation. /// /// Left-hand side operand /// Right-hand side operand /// public static K, B> operator +(K, B> lhs, K, B> rhs) => lhs.Combine(rhs); } } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/These/Operators/These.Operators.cs ================================================ using LanguageExt.Traits; namespace LanguageExt; public static partial class TheseExtensions { extension(K, A> _) { /// /// Downcast operator /// public static These operator +(K, A> ma) => (These)ma; /// /// Downcast operator /// public static These operator >> (K, A> ma, Lower lower) => +ma; } } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/These/Prelude/These.Prelude.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public partial class Prelude { /// /// This constructor /// /// Value to set /// Constructed `These` structure public static These This(A value) => new These.This(value); /// /// That constructor /// /// Value to set /// Constructed `These` structure public static These That(B value) => new These.That(value); /// /// Both constructor /// /// First value to set /// Second value to set /// Constructed `These` structure public static These Both(A first, B second) => new These.Both(first, second); /// /// Coalesce with the provided operation /// /// Coalesce operation /// Coalesced value public static A merge(Func f, These these) => these.Merge(f); /// /// Select each constructor and partition them into separate lists. /// /// Selection /// Foldable structure /// Partitioned sequences public static (Seq This, Seq That, Seq<(A, B)> Both) partition(K> theses) where F : Foldable => theses.Partition(); /// /// Select each constructor and partition them into separate lists. /// /// Selection /// Foldable structure /// Partitioned sequences public static (Seq This, Seq That) partition2(K> theses) where F : Foldable => theses.Partition2(); } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/These/These.Both.cs ================================================ using System; namespace LanguageExt; public abstract partial record These { public sealed record Both(A First, B Second) : These { public override C Match(Func This, Func That, Func Both) => Both(First, Second); public override (A, B) ToTuple(A x, B y) => (First, Second); public override These Map(Func f) => new These.Both(First, f(Second)); public override These BiMap(Func This, Func That) => new These.Both(This(First), That(Second)); } } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/These/These.Module.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public partial class These { /// /// This constructor /// /// Value to set /// Constructed `These` structure public static These This(A value) => new These.This(value); /// /// That constructor /// /// Value to set /// Constructed `These` structure public static These That(B value) => new These.That(value); /// /// Both constructor /// /// First value to set /// Second value to set /// Constructed `These` structure public static These Both(A first, B second) => new These.Both(first, second); /// /// Coalesce with the provided operation /// /// Coalesce operation /// Coalesced value public static A merge(Func f, These these) => these.Merge(f); /// /// Select each constructor and partition them into separate lists. /// /// Selection /// Foldable structure /// Partitioned sequences public static (Seq This, Seq That, Seq<(A, B)> Both) partition( K> theses) where F : Foldable => theses.Partition(); /// /// Select each constructor and partition them into separate lists. /// /// Selection /// Foldable structure /// Partitioned sequences public static (Seq This, Seq That) partition2( K> theses) where F : Foldable => theses.Partition2(); } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/These/These.That.cs ================================================ using System; namespace LanguageExt; public abstract partial record These { public sealed record That(B Value) : These { public override C Match(Func This, Func That, Func Both) => That(Value); public override (A, B) ToTuple(A x, B y) => (x, Value); public override These Map(Func f) => new These.That(f(Value)); public override These BiMap(Func This, Func That) => new These.That(That(Value)); } } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/These/These.This.cs ================================================ using System; namespace LanguageExt; public abstract partial record These { public sealed record This(A Value) : These { public override C Match(Func This, Func That, Func Both) => This(Value); public override (A, B) ToTuple(A x, B y) => (Value, y); public override These Map(Func f) => new These.This(Value); public override These BiMap(Func This, Func That) => new These.This(This(Value)); } } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/These/These.cs ================================================ using System; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; public abstract partial record These : K, B> { /// /// Stop other types deriving from These /// private These() {} /// /// Case analysis for the `These` type /// /// Match for `This` state /// Match for `That` state /// Match for `Both` state /// Result type /// Result of running either `This`, `That`, or `Both` public abstract C Match(Func This, Func That, Func Both); /// /// Takes two default values and produces a tuple /// /// Default value A /// Default value B /// Tuple public abstract (A, B) ToTuple(A x, B y); /// /// Bi-functor map operation /// /// Mapping of `This` /// Mapping of `That` /// Resulting `This` bound value type /// Resulting `That` bound value type /// public abstract These BiMap(Func This, Func That) where C : Semigroup; /// /// Functor map operation /// /// Mapping function public abstract These Map(Func f); /// /// Traverse /// public K> Traverse(Func> f) where F : Applicative => this.Kind().Traverse(f).Map(ac => ac.As()); /// /// Bi-map and coalesce results with the provided operation. /// /// This mapping /// That mapping /// Both mapping /// /// public C Merge(Func This, Func That, Func Both) where C : Semigroup => BiMap(This, That).Merge(Both); } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/These/Trait/These.TraitImpl.cs ================================================ using System; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; public class These : Traversable> { public static K, C> Map(Func f, K, B> ma) => ma.As().Map(f); public static S FoldWhile( Func> f, Func<(S State, B Value), bool> predicate, S state, K, B> ta) => ta switch { These.This => state, These.That (var b) => predicate((state, b)) ? f(b)(state) : state, These.Both (_, var b) => predicate((state, b)) ? f(b)(state) : state, _ => throw new NotSupportedException() }; public static S FoldBackWhile( Func> f, Func<(S State, B Value), bool> predicate, S state, K, B> ta) => ta switch { These.This => state, These.That (var b) => predicate((state, b)) ? f(state)(b) : state, These.Both (_, var b) => predicate((state, b)) ? f(state)(b) : state, _ => throw new NotSupportedException() }; public static K, C>> Traverse(Func> f, K, B> ta) where F : Applicative => ta switch { These.This (var a) => F.Pure(This(a).Kind()), These.That (var b) => F.Map(x => That(x).Kind(), f(b)), These.Both (var a, var b) => F.Map(x => Both(a, x).Kind(), f(b)), _ => throw new NotSupportedException() }; static Fold Foldable>.FoldStep(K, B> ta, S initialState) { var ma = ta.As(); return ma switch { These.That(var b) => Fold.Loop(initialState, b, Fold.Done), These.Both(_, var b) => Fold.Loop(initialState, b, Fold.Done), _ => Fold.Done(initialState) }; } static Fold Foldable>.FoldStepBack(K, B> ta, S initialState) => ta.FoldStep(initialState); } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/Try/Extensions/Try.Extensions.MapApply.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public static partial class TryExtensions { /// /// Functor map operation /// /// /// Unwraps the value within the functor, passes it to the map function `f` provided, and /// then takes the mapped value and wraps it back up into a new functor. /// /// Functor to map /// Mapping function /// Mapped functor public static Try Map(this Func f, K ma) => Functor.map(f, ma).As(); /// /// Functor map operation /// /// /// Unwraps the value within the functor, passes it to the map function `f` provided, and /// then takes the mapped value and wraps it back up into a new functor. /// /// Functor to map /// Mapping function /// Mapped functor public static Try Map(this Func f, Try ma) => Functor.map(f, ma).As(); /// /// Applicative action: runs the first applicative, ignores the result, and returns the second applicative /// public static Try Action(this Try ma, K mb) => Applicative.action(ma, mb).As(); /// /// Applicative action: runs the first applicative, ignores the result, and returns the second applicative /// public static Try Action(this K ma, K mb) => Applicative.action(ma, mb).As(); /// /// Applicative functor apply operation /// /// /// Unwraps the value within the `ma` applicative-functor, passes it to the unwrapped function(s) within `mf`, and /// then takes the resulting value and wraps it back up into a new applicative-functor. /// /// Value(s) applicative functor /// Mapping function(s) /// Mapped applicative functor public static Try Apply(this Try> mf, K ma) => Applicative.apply(mf, ma).As(); /// /// Applicative functor apply operation /// /// /// Unwraps the value within the `ma` applicative-functor, passes it to the unwrapped function(s) within `mf`, and /// then takes the resulting value and wraps it back up into a new applicative-functor. /// /// Value(s) applicative functor /// Mapping function(s) /// Mapped applicative functor public static Try Apply(this K> mf, K ma) => Applicative.apply(mf, ma).As(); } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/Try/Extensions/Try.Extensions.cs ================================================ using System; using static LanguageExt.Prelude; using System.Diagnostics.Contracts; using LanguageExt.Traits; namespace LanguageExt; /// /// Try monad extensions /// public static partial class TryExtensions { public static Try As(this K ma) => (Try)ma; /// /// Run the `Try` /// public static Fin Run(this K ma) { try { return ma.As().runTry(); } catch (Exception e) { return Fin.Fail(e); } } /// /// Monadic join /// [Pure] public static Try Flatten(this K> mma) => new(() => mma.As().Run() switch { Fin>.Succ (var succ) => succ.Run(), Fin>.Fail (var fail) => Fin.Fail(fail), _ => throw new NotSupportedException() }); /// /// Monadic join /// [Pure] public static Try Flatten(this K> mma) => new(() => mma.Run() switch { Fin>.Succ (var succ) => succ.Run(), Fin>.Fail (var fail) => Fin.Fail(fail), _ => throw new NotSupportedException() }); } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/Try/Operators/Try.Operators.Applicative.cs ================================================ using System; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; public static partial class TryExtensions { extension(K self) { /// /// Applicative sequence operator /// public static Try operator >>> (K ma, K mb) => ma.Action(mb).As(); /// /// Applicative apply operator /// public static Try operator * (K> mf, K ma) => mf.Apply(ma); /// /// Applicative apply operator /// public static Try operator * (K ma, K> mf) => mf.Apply(ma); } extension(K self) { /// /// Applicative apply operator /// public static Try> operator * ( K> mf, K ma) => curry * mf * ma; /// /// Applicative apply operator /// public static Try> operator * ( K ma, K> mf) => curry * mf * ma; } extension(K self) { /// /// Applicative apply operator /// public static Try>> operator * ( K> mf, K ma) => curry * mf * ma; /// /// Applicative apply operator /// public static Try>> operator * ( K ma, K> mf) => curry * mf * ma; } extension(K self) { /// /// Applicative apply operator /// public static Try>>> operator * ( K> mf, K ma) => curry * mf * ma; /// /// Applicative apply operator /// public static Try>>> operator * ( K ma, K> mf) => curry * mf * ma; } extension(K self) { /// /// Applicative apply operator /// public static Try>>>> operator * ( K> mf, K ma) => curry * mf * ma; /// /// Applicative apply operator /// public static Try>>>> operator * ( K ma, K> mf) => curry * mf * ma; } extension(K self) { /// /// Applicative apply operator /// public static Try>>>>> operator * ( K> mf, K ma) => curry * mf * ma; /// /// Applicative apply operator /// public static Try>>>>> operator * ( K ma, K> mf) => curry * mf * ma; } extension(K self) { /// /// Applicative apply operator /// public static Try>>>>>> operator * ( K> mf, K ma) => curry * mf * ma; /// /// Applicative apply operator /// public static Try>>>>>> operator * ( K ma, K> mf) => curry * mf * ma; } extension(K self) { /// /// Applicative apply operator /// public static Try>>>>>>> operator * ( K> mf, K ma) => curry * mf * ma; /// /// Applicative apply operator /// public static Try>>>>>>> operator * ( K ma, K> mf) => curry * mf * ma; } extension(K self) { /// /// Applicative apply operator /// public static Try>>>>>>>> operator * ( K> mf, K ma) => curry * mf * ma; /// /// Applicative apply operator /// public static Try>>>>>>>> operator * ( K ma, K> mf) => curry * mf * ma; } extension(K self) { /// /// Applicative apply operator /// public static Try>>>>>>>>> operator * ( K> mf, K ma) => curry * mf * ma; /// /// Applicative apply operator /// public static Try>>>>>>>>> operator *( K ma, K> mf) => curry * mf * ma; } } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/Try/Operators/Try.Operators.Choice.cs ================================================ using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt; public static partial class TryExtensions { extension(K self) { public static Try operator |(K lhs, K rhs) => +lhs.Choose(rhs); public static Try operator |(K lhs, Pure rhs) => +lhs.Choose(rhs.ToTry()); } } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/Try/Operators/Try.Operators.Fallible.cs ================================================ using System; using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt; public static partial class TryExtensions { extension(K self) { public static Try operator |(K lhs, CatchM rhs) => +lhs.Catch(rhs); public static Try operator |(K lhs, Fail rhs) => +lhs.Catch(rhs); public static Try operator |(K lhs, Fail rhs) => +lhs.Choose(Try.Fail(rhs.Value)); public static Try operator |(K lhs, Error rhs) => +lhs.Catch(rhs); public static Try operator |(K lhs, Exception rhs) => +lhs.Catch(rhs); } } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/Try/Operators/Try.Operators.Final.cs ================================================ namespace LanguageExt.Traits; public static class TryExtensions { extension(K _) { /// /// Run a `finally` operation after the main operation regardless of whether it succeeds or not. /// /// Primary operation /// Finally operation /// Result of primary operation public static Try operator |(K lhs, Finally rhs) => +lhs.Finally(rhs.Operation); } } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/Try/Operators/Try.Operators.Functor.cs ================================================ using System; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; public static partial class TryExtensions { extension(K _) { /// /// Functor map operator /// public static Try operator *(Func f, K ma) => +ma.Map(f); /// /// Functor map operator /// public static Try operator *(K ma, Func f) => +ma.Map(f); } extension(K _) { /// /// Functor map operator /// public static Try> operator * ( Func f, K ma) => curry(f) * ma; /// /// Functor map operator /// public static Try> operator * ( K ma, Func f) => curry(f) * ma; } extension(K _) { /// /// Functor map operator /// public static Try>> operator * ( Func f, K ma) => curry(f) * ma; /// /// Functor map operator /// public static Try>> operator * ( K ma, Func f) => curry(f) * ma; } extension(K _) { /// /// Functor map operator /// public static Try>>> operator * ( Func f, K ma) => curry(f) * ma; /// /// Functor map operator /// public static Try>>> operator * ( K ma, Func f) => curry(f) * ma; } extension(K _) { /// /// Functor map operator /// public static Try>>>> operator * ( Func f, K ma) => curry(f) * ma; /// /// Functor map operator /// public static Try>>>> operator * ( K ma, Func f) => curry(f) * ma; } extension(K _) { /// /// Functor map operator /// public static Try>>>>> operator * ( Func f, K ma) => curry(f) * ma; /// /// Functor map operator /// public static Try>>>>> operator * ( K ma, Func f) => curry(f) * ma; } extension(K _) { /// /// Functor map operator /// public static Try>>>>>> operator * ( Func f, K ma) => curry(f) * ma; /// /// Functor map operator /// public static Try>>>>>> operator * ( K ma, Func f) => curry(f) * ma; } extension(K _) { /// /// Functor map operator /// public static Try>>>>>>> operator * ( Func f, K ma) => curry(f) * ma; /// /// Functor map operator /// public static Try>>>>>>> operator * ( K ma, Func f) => curry(f) * ma; } extension(K _) { /// /// Functor map operator /// public static Try>>>>>>>> operator * ( Func f, K ma) => curry(f) * ma; /// /// Functor map operator /// public static Try>>>>>>>> operator * ( K ma, Func f) => curry(f) * ma; } extension(K _) { /// /// Functor map operator /// public static Try>>>>>>>>> operator * ( Func f, K ma) => curry(f) * ma; /// /// Functor map operator /// public static Try>>>>>>>>> operator * ( K ma, Func f) => curry(f) * ma; } } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/Try/Operators/Try.Operators.Monad.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public static partial class TryExtensions { extension(K self) { /// /// Monad bind operator /// /// Monad to bind /// Binding function /// Mapped monad public static Try operator >> (K ma, Func> f) => +ma.Bind(f); /// /// Sequentially compose two actions, discarding any value produced by the first, like sequencing operators (such /// as the semicolon) in C#. /// /// First action to run /// Second action to run /// Result of the second action public static Try operator >> (K lhs, K rhs) => lhs >> (_ => rhs); } extension(K self) { /// /// Sequentially compose two actions. The second action is a unit-returning action, so the result of the /// first action is propagated. /// /// First action to run /// Second action to run /// Result of the first action public static Try operator >> (K lhs, K rhs) => lhs >> (x => (_ => x) * rhs); } } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/Try/Operators/Try.Operators.cs ================================================ using LanguageExt.Traits; namespace LanguageExt; public static partial class TryExtensions { extension(K _) { /// /// Downcast operator /// public static Try operator +(K ma) => (Try)ma; /// /// Downcast operator /// public static Try operator >> (K ma, Lower lower) => +ma; } } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/Try/Prelude/Try.Prelude.mapapply.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public static partial class Prelude { /// /// Functor map operation /// /// /// Unwraps the value within the functor, passes it to the map function `f` provided, and /// then takes the mapped value and wraps it back up into a new functor. /// /// Functor to map /// Mapping function /// Mapped functor public static Try map(Func f, K ma) => Functor.map(f, ma).As(); /// /// Applicative action: runs the first applicative, ignores the result, and returns the second applicative /// public static Try action(K ma, K mb) => Applicative.action(ma, mb).As(); /// /// Applicative functor apply operation /// /// /// Unwraps the value within the `ma` applicative-functor, passes it to the unwrapped function(s) within `mf`, and /// then takes the resulting value and wraps it back up into a new applicative-functor. /// /// Value(s) applicative functor /// Mapping function(s) /// Mapped applicative functor public static Try apply(K> mf, K ma) => Applicative.apply(mf, ma).As(); } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/Try/Trait/Try.TraitImpl.cs ================================================ using System; using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt; /// /// Trait implementation for `Try` /// public partial class Try : Monad, Fallible, Final, Alternative { static K Monad.Bind(K ma, Func> f) => new Try(() => ma.Run() switch { Fin.Succ(var x) => f(x).Run(), Fin.Fail(var e) => Fin.Fail(e), _ => throw new NotSupportedException() }); static K Monad.Recur(A value, Func>> f) => lift(() => { while (true) { var mr = +f(value).Run(); if (mr.IsFail) return Fin.Fail(mr.FailValue); var next = (Next)mr; if (next.IsDone) return Fin.Succ(next.Done); value = next.Loop; } }); static K Functor.Map(Func f, K ma) => new Try(() => ma.Run().Map(f)); static K Applicative.Pure(A value) => Succ(value); static K Applicative.Apply(K> mf, K ma) => new Try(() => mf.Run().Apply(ma.Run())); static K Applicative.Apply(K> mf, Memo ma) => new Try(() => mf.Run().Apply(ma.Value.Run())); static K Alternative.Empty() => Try.Fail(Error.Empty); static K Choice.Choose(K ma, K mb) => new Try(() => ma.Run() switch { Fin.Succ(var x) => Fin.Succ(x), Fin.Fail => mb.Run(), _ => throw new NotSupportedException() }); static K Choice.Choose(K ma, Memo mb) => new Try(() => ma.Run() switch { Fin.Succ(var x) => Fin.Succ(x), Fin.Fail => mb.Value.Run(), _ => throw new NotSupportedException() }); static K Fallible.Fail(Error value) => Fail(value); static K Fallible.Catch( K fa, Func Predicate, Func> Fail) => new Try(() => fa.Run() switch { Fin.Succ ma => ma, Fin.Fail(var e) when Predicate(e) => Fail(e).Run(), var ma => ma }); static K Final.Finally(K fa, K @finally) => new Try(() => { try { return fa.Run(); } finally { @finally.Run().ThrowIfFail(); } }); } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/Try/Try.Module.cs ================================================ using System; using LanguageExt.Common; namespace LanguageExt; public partial class Try { /// /// Lifts a pure success value into a `Try` monad /// /// Value to lift /// `Try` public static Try Succ(A value) => new (() => value); /// /// Lifts a failure value into a `Try` monad /// /// Failure value to lift /// `Try` public static Try Fail(Error value) => new (() => value); /// /// Lifts a given function into a `Try` monad /// /// Function to lift /// `Try` public static Try lift(Func> f) => new(() => { try { return f(); } catch (Exception e) { return Fin.Fail(e); } }); /// /// Lifts a given value into a `Try` monad /// /// Value to lift /// `Try` public static Try lift(Fin ma) => new (() => ma); /// /// Lifts a given value into a `Try` monad /// /// Value to lift /// `Try` public static Try lift(Pure ma) => new (() => ma.Value); /// /// Lifts a given value into a `Try` monad /// /// Value to lift /// `Try` public static Try lift(Fail ma) => new (() => ma.Value); /// /// Lifts a given function into a `Try` monad /// /// Function to lift /// `Try` public static Try lift(Func f) => new(() => { try { return f(); } catch (Exception e) { return Fin.Fail(e); } }); /// /// Lifts a given function into a `Try` monad /// /// Function to lift /// `Try` public static Try lift(Action f) => lift(() => { f(); return default; }); } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/Try/Try.cs ================================================ using System; using System.Diagnostics.Contracts; using LanguageExt.Common; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; /// /// `Try` monad which allows for an optional `Error` result and catches exceptions, converting them to `Error`. /// /// Given monad trait /// Bound value type public record Try(Func> runTry) : K { //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Match // /// /// Match the bound value and return a result (which gets packages back up inside the inner monad) /// /// Success branch /// Fail branch /// Inner monad with the result of the `Succ` or `Fail` branches public B Match(Func Succ, Func Fail) => this.Run().Match(Succ, Fail); /// /// Match the bound value and return a result (which gets packages back up inside the inner monad) /// /// Success branch /// Fail branch /// Inner monad with the result of the `Succ` or `Fail` branches public A IfFail(Func Fail) => Match(identity, Fail); /// /// Match the bound value and return a result (which gets packages back up inside the inner monad) /// /// Success branch /// Fail branch /// Inner monad with the result of the `Succ` or `Fail` branches public Fin IfFailM(Func> Fail) => Match(Fin.Succ, Fail); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Map // /// /// Maps the bound value /// /// Mapping function /// Target bound value type /// `TryT` public Try Map(Func f) => new(() => runTry().Map(f)); /// /// Maps the bound value /// /// Mapping function /// `TryT` public Try MapFail(Func f) => this.Catch(f).As(); /// /// Maps the bound value /// /// Mapping transducer /// Target bound value type /// `TryT` public Try Select(Func f) => new(() => runTry().Map(f)); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Bind // /// /// Monad bind operation /// /// Mapping function /// Target bound value type /// `TryT` public Try Bind(Func> f) => new(() => { var r = runTry(); return r.IsFail ? Fin.Fail((Error)r) : f((A)r).As().runTry(); }); /// /// Monad bind operation /// /// Mapping function /// Target bound value type /// `TryT` public Try Bind(Func> f) => new(() => { var r = runTry(); return r.IsFail ? Fin.Fail((Error)r) : f((A)r).runTry(); }); /// /// Monad bind operation /// /// Mapping function /// Target bound value type /// `TryT` public Try Bind(Func> f) => Map(a => f(a).Value); /// /// Monad bind operation /// /// Mapping function /// Target bound value type /// `TryT` public Try BiBind(Func> Succ, Func> Fail) => Bind(Succ).Catch(Fail).As(); /// /// Monad bind operation /// /// Mapping function /// Target bound value type /// `TryT` public Try BindFail(Func> Fail) => this.Catch(Fail).As(); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // SelectMany // /// /// Monad bind operation /// /// Monadic bind function /// Projection function /// Intermediate bound value type /// Target bound value type /// `TryT` public Try SelectMany(Func> bind, Func project) => SelectMany(x => bind(x).As(), project); /// /// Monad bind operation /// /// Monadic bind function /// Projection function /// Intermediate bound value type /// Target bound value type /// `TryT` public Try SelectMany(Func> bind, Func project) => Bind(x => bind(x).Map(y => project(x, y))); /// /// Monad bind operation /// /// Monadic bind function /// Projection function /// Intermediate bound value type /// Target bound value type /// `TryT` public Try SelectMany(Func> bind, Func project) => SelectMany(x => Try.lift(bind(x)), project); /// /// Monad bind operation /// /// Monadic bind function /// Projection function /// Intermediate bound value type /// Target bound value type /// `TryT` public Try SelectMany(Func> bind, Func project) => Map(x => project(x, bind(x).Value)); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Conversion operators // public static implicit operator Try(Pure ma) => Try.Succ(ma.Value); public static implicit operator Try(Error ma) => Try.lift(Fin.Fail(ma)); public static implicit operator Try(Fail ma) => Try.lift(Fin.Fail(ma.Value)); public static implicit operator Try(Fail ma) => Try.lift(Fin.Fail(ma.Value)); public Option ToOption() => this.Run().ToOption(); public Either ToEither() => this.Run().ToEither(); public Fin ToFin() => this.Run(); public IO ToIO() => IO.lift(runTry); [Pure] public Try Combine(Try rhs) => new(() => this.Run() switch { Fin.Fail fa => fa.Combine(rhs.Run()).As(), var fa => fa }); } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/TryT/Extensions/TryT.Extensions.MapApply.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public static partial class TryTExtensions { /// /// Functor map operation /// /// /// Unwraps the value within the functor, passes it to the map function `f` provided, and /// then takes the mapped value and wraps it back up into a new functor. /// /// Functor to map /// Mapping function /// Mapped functor public static TryT Map(this Func f, K, A> ma) where M : Monad => Functor.map(f, ma).As(); /// /// Functor map operation /// /// /// Unwraps the value within the functor, passes it to the map function `f` provided, and /// then takes the mapped value and wraps it back up into a new functor. /// /// Functor to map /// Mapping function /// Mapped functor public static TryT Map(this Func f, TryT ma) where M : Monad => Functor.map(f, ma).As(); /// /// Applicative action: runs the first applicative, ignores the result, and returns the second applicative /// public static TryT Action(this TryT ma, K, B> mb) where M : Monad => Applicative.action(ma, mb).As(); /// /// Applicative action: runs the first applicative, ignores the result, and returns the second applicative /// public static TryT Action(this K, A> ma, K, B> mb) where M : Monad => Applicative.action(ma, mb).As(); /// /// Applicative functor apply operation /// /// /// Unwraps the value within the `ma` applicative-functor, passes it to the unwrapped function(s) within `mf`, and /// then takes the resulting value and wraps it back up into a new applicative-functor. /// /// Value(s) applicative functor /// Mapping function(s) /// Mapped applicative functor public static TryT Apply(this TryT> mf, K, A> ma) where M : Monad => Applicative.apply(mf, ma).As(); /// /// Applicative functor apply operation /// /// /// Unwraps the value within the `ma` applicative-functor, passes it to the unwrapped function(s) within `mf`, and /// then takes the resulting value and wraps it back up into a new applicative-functor. /// /// Value(s) applicative functor /// Mapping function(s) /// Mapped applicative functor public static TryT Apply(this K, Func> mf, K, A> ma) where M : Monad => Applicative.apply(mf, ma).As(); } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/TryT/Extensions/TryT.Extensions.cs ================================================ using System; using static LanguageExt.Prelude; using System.Diagnostics.Contracts; using LanguageExt.Traits; namespace LanguageExt; /// /// `TryT` monad extensions /// public static partial class TryTExtensions { public static TryT As(this K, A> ma) where M : Monad => (TryT)ma; /// /// Run the transformer /// /// /// This is where the exceptions are caught /// public static K> Run(this K, A> ma) where M : Monad => ma.As().runTry.Map(t => t.Run()); /// /// Monad bind operation /// /// Mapping function /// Target bound value type /// `TryT` public static TryT Bind(this K, A> ma, Func> f) where M : MonadIO => ma.As().Bind(a => TryT.liftIO(f(a))); /// /// Monadic join /// [Pure] public static TryT Flatten(this K, TryT> mma) where M : Monad => new(mma.As().runTry.Bind( ta => ta.Run() switch { Fin>.Succ(var ma) => ma.runTry, Fin>.Fail(var e) => M.Pure(Try.Fail(e)), _ => throw new NotSupportedException() })); /// /// Monadic join /// [Pure] public static TryT Flatten(this K, K, A>> mma) where M : Monad => new(mma.As().runTry.Bind( ta => ta.Run() switch { Fin, A>>.Succ(var ma) => ma.As().runTry, Fin, A>>.Fail(var e) => M.Pure(Try.Fail(e)), _ => throw new NotSupportedException() })); /// /// Monad bind operation /// /// Monadic bind function /// Projection function /// Intermediate bound value type /// Target bound value type /// `TryT` [Pure] public static TryT SelectMany( this K ma, Func, B>> bind, Func project) where M : Monad => TryT.lift(ma).SelectMany(bind, project); /// /// Monad bind operation /// /// Monadic bind function /// Projection function /// Intermediate bound value type /// Target bound value type /// `TryT` [Pure] public static TryT SelectMany( this K ma, Func> bind, Func project) where M : Monad => TryT.lift(ma).SelectMany(bind, project); /// /// Monad bind operation /// /// Monadic bind function /// Projection function /// Intermediate bound value type /// Target bound value type /// `TryT` [Pure] public static TryT SelectMany( this K, A> ma, Func> bind, Func project) where M : MonadIO => ma.As().SelectMany(x => M.LiftIO(bind(x)), project); } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/TryT/Operators/TryT.Operators.Applicative.cs ================================================ using System; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; public static partial class TryTExtensions { extension(K, A> self) where M : Monad { /// /// Applicative sequence operator /// public static TryT operator >>> (K, A> ma, K, B> mb) => ma.Action(mb).As(); /// /// Applicative apply operator /// public static TryT operator * (K, Func> mf, K, A> ma) => mf.Apply(ma); /// /// Applicative apply operator /// public static TryT operator * (K, A> ma, K, Func> mf) => mf.Apply(ma); } extension(K, A> self) where M : Monad { /// /// Applicative apply operator /// public static TryT> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static TryT> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) where M : Monad { /// /// Applicative apply operator /// public static TryT>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static TryT>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) where M : Monad { /// /// Applicative apply operator /// public static TryT>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static TryT>>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) where M : Monad { /// /// Applicative apply operator /// public static TryT>>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static TryT>>>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) where M : Monad { /// /// Applicative apply operator /// public static TryT>>>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static TryT>>>>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) where M : Monad { /// /// Applicative apply operator /// public static TryT>>>>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static TryT>>>>>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) where M : Monad { /// /// Applicative apply operator /// public static TryT>>>>>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static TryT>>>>>>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) where M : Monad { /// /// Applicative apply operator /// public static TryT>>>>>>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static TryT>>>>>>>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) where M : Monad { /// /// Applicative apply operator /// public static TryT>>>>>>>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static TryT>>>>>>>>> operator *( K, A> ma, K, Func> mf) => curry * mf * ma; } } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/TryT/Operators/TryT.Operators.Choice.cs ================================================ using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt; public static partial class TryTExtensions { extension(K, A> self) where M : Monad { public static TryT operator |(K, A> lhs, K, A> rhs) => +lhs.Choose(rhs); public static TryT operator |(K, A> lhs, Pure rhs) => +lhs.Choose(TryT.Succ(rhs.Value)); } } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/TryT/Operators/TryT.Operators.Fallible.cs ================================================ using System; using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt; public static partial class TryTExtensions { extension(K, A> self) where M : Monad { public static TryT operator |(K, A> lhs, CatchM, A> rhs) => +lhs.Catch(rhs); public static TryT operator |(K, A> lhs, Fail rhs) => +lhs.Catch(rhs); public static TryT operator |(K, A> lhs, Fail rhs) => +lhs.Catch(rhs.Value); public static TryT operator |(K, A> lhs, Error rhs) => +lhs.Catch(rhs); public static TryT operator |(K, A> lhs, Exception rhs) => +lhs.Catch(rhs); } } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/TryT/Operators/TryT.Operators.Functor.cs ================================================ using System; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; public static partial class TryTExtensions { extension(K, A> _) where M : Monad { /// /// Functor map operator /// public static TryT operator *(Func f, K, A> ma) => +ma.Map(f); /// /// Functor map operator /// public static TryT operator *(K, A> ma, Func f) => +ma.Map(f); } extension(K, A> _) where M : Monad { /// /// Functor map operator /// public static TryT> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static TryT> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) where M : Monad { /// /// Functor map operator /// public static TryT>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static TryT>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) where M : Monad { /// /// Functor map operator /// public static TryT>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static TryT>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) where M : Monad { /// /// Functor map operator /// public static TryT>>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static TryT>>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) where M : Monad { /// /// Functor map operator /// public static TryT>>>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static TryT>>>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) where M : Monad { /// /// Functor map operator /// public static TryT>>>>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static TryT>>>>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) where M : Monad { /// /// Functor map operator /// public static TryT>>>>>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static TryT>>>>>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) where M : Monad { /// /// Functor map operator /// public static TryT>>>>>>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static TryT>>>>>>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) where M : Monad { /// /// Functor map operator /// public static TryT>>>>>>>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static TryT>>>>>>>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/TryT/Operators/TryT.Operators.Monad.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public static partial class TryTExtensions { extension(K, A> self) where M : Monad { /// /// Monad bind operator /// /// Monad to bind /// Binding function /// Mapped monad public static TryT operator >> (K, A> ma, Func, B>> f) => +ma.Bind(f); /// /// Sequentially compose two actions, discarding any value produced by the first, like sequencing operators (such /// as the semicolon) in C#. /// /// First action to run /// Second action to run /// Result of the second action public static TryT operator >> (K, A> lhs, K, B> rhs) => lhs >> (_ => rhs); } extension(K, A> self) where M : Monad { /// /// Sequentially compose two actions. The second action is a unit-returning action, so the result of the /// first action is propagated. /// /// First action to run /// Second action to run /// Result of the first action public static TryT operator >> (K, A> lhs, K, Unit> rhs) => lhs >> (x => (_ => x) * rhs); } } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/TryT/Operators/TryT.Operators.cs ================================================ using LanguageExt.Traits; namespace LanguageExt; public static partial class TryTExtensions { extension(K, A> _) where M : Monad { /// /// Downcast operator /// public static TryT operator +(K, A> ma) => (TryT)ma; /// /// Downcast operator /// public static TryT operator >> (K, A> ma, Lower lower) => +ma; } } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/TryT/Prelude/TryT.Prelude.mapapply.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public static partial class Prelude { /// /// Functor map operation /// /// /// Unwraps the value within the functor, passes it to the map function `f` provided, and /// then takes the mapped value and wraps it back up into a new functor. /// /// Functor to map /// Mapping function /// Mapped functor public static TryT map(Func f, K, A> ma) where M : Monad => Functor.map(f, ma).As(); /// /// Applicative action: runs the first applicative, ignores the result, and returns the second applicative /// public static TryT action(K, A> ma, K, B> mb) where M : Monad => Applicative.action(ma, mb).As(); /// /// Applicative functor apply operation /// /// /// Unwraps the value within the `ma` applicative-functor, passes it to the unwrapped function(s) within `mf`, and /// then takes the resulting value and wraps it back up into a new applicative-functor. /// /// Value(s) applicative functor /// Mapping function(s) /// Mapped applicative functor public static TryT apply(K, Func> mf, K, A> ma) where M : Monad => Applicative.apply(mf, ma).As(); } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/TryT/Trait/TryT.TraitImpl.cs ================================================ using System; using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt; /// /// Trait implementation for `TryT` /// /// Given monad trait public partial class TryT : Fallible>, MonadT, M>, Alternative>, MonadIO> where M : Monad { static K, B> Monad>.Bind(K, A> ma, Func, B>> f) => ma.As().Bind(f); static K, B> Monad>.Recur(A value, Func, Next>> f) => new TryT( M.Recur>( value, a => f(a).As() .Run() .Map(e => e switch { Fin>.Fail(var err) => Next.Done>(err), Fin>.Succ({ IsDone: true } n) => Next.Done>(Try.Succ(n.Done)), Fin>.Succ({ IsLoop: true } n) => Next.Loop>(n.Loop), _ => throw new NotSupportedException() }))); static K, B> Functor>.Map(Func f, K, A> ma) => ma.As().Map(f); static K, A> Applicative>.Pure(A value) => TryT.Succ(value); static K, B> Applicative>.Apply(K, Func> mf, K, A> ma) => new TryT(mf.As().runTry.Bind( mf1 => ma.As().runTry.Bind( ma1 => M.Pure(mf1.Apply(ma1))))); static K, B> Applicative>.Apply(K, Func> mf, Memo, A> ma) => new TryT(mf.As().runTry.Bind( mf1 => ma.Value.As().runTry.Bind( ma1 => M.Pure(mf1.Apply(ma1))))); static K, A> MonadT, M>.Lift(K ma) => TryT.lift(ma); static K, A> MonadIO>.LiftIO(IO ma) => TryT.liftIOMaybe(ma); static K, A> Alternative>.Empty() => TryT.Fail(Error.Empty); static K, A> Choice>.Choose(K, A> ma, K, A> mb) => new TryT(ma.Run().Bind( lhs => lhs switch { Fin.Succ (var x) => M.Pure(Try.Succ(x)), Fin.Fail => mb.As().runTry, _ => throw new NotSupportedException() })); static K, A> Choice>.Choose(K, A> ma, Memo, A> mb) => new TryT(ma.Run().Bind( lhs => lhs switch { Fin.Succ (var x) => M.Pure(Try.Succ(x)), Fin.Fail => mb.Value.As().runTry, _ => throw new NotSupportedException() })); static K, A> Fallible>.Fail(Error error) => TryT.Fail(error); static K, A> Fallible>.Catch( K, A> fa, Func Predicate, Func, A>> Fail) => fa.As().BindFail(e => Predicate(e) ? Fail(e).As() : TryT.Fail(e)); } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/TryT/TryT.Module.cs ================================================ using System; using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt; public class TryT { /// /// Lift a pure success value into the monad-transformer /// /// Value to lift /// `TryT` public static TryT Succ(A value) where M : Monad => lift(M.Pure(value)); /// /// Lift a fail value into the monad-transformer /// /// Value to lift /// `TryT` public static TryT Fail(Error value) where M : Monad => lift(Fin.Fail(value)); /// /// Lifts a given monad into the transformer /// /// Monad to lift /// `TryT` public static TryT lift(K ma) where M : Monad => new(M.Map(Try.Succ, ma)); /// /// Lifts a given lazy-value into the transformer /// /// Lazy value to lift /// `TryT` public static TryT lift(Func> f) where M : Monad => new(M.Pure(Try.lift(f))); /// /// Lifts a given value into the transformer /// /// Value to lift /// `TryT` public static TryT lift(Fin ma) where M : Monad => new(M.Pure(Try.lift(ma))); /// /// Lifts a given value into the transformer /// /// Value to lift /// `TryT` public static TryT lift(Pure ma) where M : Monad => Succ(ma.Value); /// /// Lifts a given value into the transformer /// /// Value to lift /// `TryT` public static TryT lift(Fail ma) where M : Monad => lift(Fin.Fail(ma.Value)); /// /// Lifts a given `IO` monad into the `TryT` transformer /// /// IO monad to lift /// `TryT` public static TryT liftIO(IO ma) where M : MonadIO => new(M.LiftIO(ma.Try().Run()).Map(Try.lift)); /// /// Lifts a given `IO` monad into the `TryT` transformer /// /// IO monad to lift /// `TryT` internal static TryT liftIOMaybe(IO ma) where M : Monad => new(M.LiftIOMaybe(ma.Try().Run()).Map(Try.lift)); } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/TryT/TryT.cs ================================================ using System; using LanguageExt.Common; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; /// /// `TryT` monad transformer, which allows for an optional `Error` result and catches exceptions, /// converting them to `Error`. /// /// Given monad trait /// Bound value type public record TryT(K> runTry) : K, A> where M : Monad { //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Match // /// /// Match the bound value and return a result (which gets packages back up inside the inner monad) /// /// Success branch /// Fail branch /// Inner monad with the result of the `Succ` or `Fail` branches public K Match(Func Succ, Func Fail) => M.Map(mx => mx.Match(Succ, Fail), this.Run()); /// /// Match the bound value and return a result (which gets packages back up inside the inner monad) /// /// Success branch /// Fail branch /// Inner monad with the result of the `Succ` or `Fail` branches public K IfFail(Func Fail) => Match(identity, Fail); /// /// Match the bound value and return a result (which gets packages back up inside the inner monad) /// /// Success branch /// Fail branch /// Inner monad with the result of the `Succ` or `Fail` branches public K IfFailM(Func> Fail) => Match(M.Pure, Fail).Flatten(); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Map // /// /// Maps the bound monad /// /// Mapping function /// Target monad type /// Target bound value type /// Mapped monad public TryT MapT(Func>, K>> f) where M1 : Monad => new(f(this.Run()).Map(Try.lift)); /// /// Maps the bound value /// /// Mapping function /// Target bound value type /// `TryT` public TryT Map(Func f) => new(M.Map(mx => mx.Map(f), runTry)); /// /// Maps the bound value /// /// Mapping function /// `TryT` public TryT MapFail(Func f) => new(M.Map(mx => mx.MapFail(f), runTry)); /// /// Maps the bound value /// /// Mapping transducer /// Target bound value type /// `TryT` public TryT Select(Func f) => new(M.Map(mx => mx.Map(f), runTry)); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Bind // /// /// Monad bind operation /// /// Mapping function /// Target bound value type /// `TryT` public TryT Bind(Func, B>> f) => Map(f).Flatten(); /// /// Monad bind operation /// /// Mapping function /// Target bound value type /// `TryT` public TryT Bind(Func> f) => Map(f).Flatten(); /// /// Monad bind operation /// /// Success mapping function /// Failure mapping function /// Target bound value type /// `TryT` public TryT BiBind(Func> Succ, Func> Fail) => new (runTry.Bind( ta => ta.runTry() .Match(Succ: Succ, Fail: Fail) .runTry)); /// /// Monad bind operation /// /// Failure mapping function /// Target bound value type /// `TryT` public TryT BindFail(Func> Fail) => BiBind(TryT.Succ, Fail); /// /// Monad bind operation /// /// Mapping function /// Target bound value type /// `TryT` public TryT Bind(Func> f) => Map(a => f(a).Value); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // SelectMany // /// /// Monad bind operation /// /// Monadic bind function /// Projection function /// Intermediate bound value type /// Target bound value type /// `TryT` public TryT SelectMany(Func, B>> bind, Func project) => SelectMany(x => bind(x).As(), project); /// /// Monad bind operation /// /// Monadic bind function /// Projection function /// Intermediate bound value type /// Target bound value type /// `TryT` public TryT SelectMany(Func> bind, Func project) => Bind(x => bind(x).Map(y => project(x, y))); /// /// Monad bind operation /// /// Monadic bind function /// Projection function /// Intermediate bound value type /// Target bound value type /// `TryT` public TryT SelectMany(Func> bind, Func project) => SelectMany(x => TryT.lift(bind(x)), project); /// /// Monad bind operation /// /// Monadic bind function /// Projection function /// Intermediate bound value type /// Target bound value type /// `TryT` public TryT SelectMany(Func> bind, Func project) => SelectMany(x => TryT.lift(bind(x)), project); /// /// Monad bind operation /// /// Monadic bind function /// Projection function /// Intermediate bound value type /// Target bound value type /// `TryT` public TryT SelectMany(Func> bind, Func project) => Map(x => project(x, bind(x).Value)); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Operators // public static implicit operator TryT(Pure ma) => TryT.Succ(ma.Value); public static implicit operator TryT(Error ma) => TryT.lift(Fin.Fail(ma)); public static implicit operator TryT(Fail ma) => TryT.lift(Fin.Fail(ma.Value)); public static implicit operator TryT(Fail ma) => TryT.lift(Fin.Fail(ma.Value)); public static implicit operator TryT(IO ma) => TryT.liftIOMaybe(ma); } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/Validation/Extensions/Validation.Extensions.Apply.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public static partial class ValidationExtensions { /// /// Applicative action: runs the first applicative, ignores the result, and returns the second applicative /// public static Validation Action(this K, A> ma, K, B> mb) where F : Semigroup => ActionI(ma, mb, F.Instance).As(); /// /// Applicative-functor apply-operation /// /// /// Unwraps the value within the `ma` applicative-functor, passes it to the unwrapped function(s) within `mf`, and /// then takes the resulting value and wraps it back up into a new applicative-functor. /// /// Value(s) applicative functor /// Mapping function(s) /// Mapped applicative functor public static Validation Apply(this K, Func> mf, K, A> ma) where F : Semigroup => ApplyI(mf, ma, F.Instance).As(); /// /// Applicative action: runs the first applicative, ignores the result, and returns the second applicative /// internal static Validation ActionI( this K, A> ma, K, B> mb, Option> trait) => trait switch { { IsSome: true, Value: { } semigroup } => ma switch { Validation.Success => mb.As(), Validation.Fail (var e1) => mb switch { Validation.Fail (var e2) => Validation.FailI(semigroup.Combine(e1, e2)), _ => Validation.FailI(e1) }, _ => throw new NotSupportedException() }, _ => ma switch { Validation.Success => mb.As(), Validation.Fail (var e1) => Validation.FailI(e1), _ => throw new NotSupportedException() } }; /// /// Applicative-functor apply-operation /// /// /// Unwraps the value within the `ma` applicative-functor, passes it to the unwrapped function(s) within `mf`, and /// then takes the resulting value and wraps it back up into a new applicative-functor. /// /// Value(s) applicative functor /// Mapping function(s) /// Mapped applicative functor internal static Validation ApplyI( this K, Func> mf, K, A> ma, Option> trait) => trait switch { { IsSome: true, Value: { } semigroup } => mf switch { Validation>.Success (var f) => ma switch { Validation.Success (var a) => Validation.SuccessI(f(a)), Validation.Fail (var e) => Validation.FailI(e), _ => throw new NotSupportedException() }, Validation>.Fail (var e1) => ma switch { Validation.Fail (var e2) => Validation.FailI(semigroup.Combine(e1, e2)), _ => Validation.FailI(e1) }, _ => throw new NotSupportedException() }, _ => mf switch { Validation>.Success (var f) => ma switch { Validation.Success (var a) => Validation.SuccessI(f(a)), Validation.Fail (var e) => Validation.FailI(e), _ => throw new NotSupportedException() }, Validation>.Fail (var e1) => Validation.FailI(e1), _ => throw new NotSupportedException() } }; } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/Validation/Extensions/Validation.Extensions.Map.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public static partial class ValidationExtensions { /// /// Functor map operation /// /// /// Unwraps the value within the functor, passes it to the map function `f` provided, and /// then takes the mapped value and wraps it back up into a new functor. /// /// Functor to map /// Mapping function /// Mapped functor public static Validation Map(this Func f, K, A> ma) => Functor.map(f, ma).As(); /// /// Functor map operation /// /// /// Unwraps the value within the functor, passes it to the map function `f` provided, and /// then takes the mapped value and wraps it back up into a new functor. /// /// Functor to map /// Mapping function /// Mapped functor public static Validation Map(this Func f, Validation ma) where F : Monoid => Functor.map(f, ma).As(); } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/Validation/Extensions/Validation.Extensions.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics.Contracts; using LanguageExt.Common; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; public static partial class ValidationExtensions { [Pure] public static Validation As(this K, A> ma) => (Validation)ma; [Pure] public static Validation As2(this K ma) => (Validation)ma; [Pure] public static Validation Combine(this K, A> lhs, K, A> rhs) where F : Semigroup => lhs.As().CombineFirst(rhs.As(), F.Instance); [Pure] public static Validation Choose(this K, A> lhs, K, A> rhs) => Choice.choose(lhs, rhs).As(); /// /// Match Success and return a context. You must follow this with `.Fail(...)` to complete the match /// /// Action to invoke if in a Success state /// Context that must have `Fail()` called upon it. [Pure] public static ValidationUnitContext Success(this K, A> ma, Action success) => new (ma.As(), success); /// /// Match Success and return a context. You must follow this with `.Fail(...)` to complete the match /// /// Action to invoke if in a Success state /// Context that must have `Fail()` called upon it. [Pure] public static ValidationContext Success(this K, A> ma, Func success) => new (ma.As(), success); /// /// Monadic join /// [Pure] public static Validation Flatten(this Validation> mma) => mma.Bind(x => x); /// /// Filter the Validation /// /// /// If the predicate returns `false`, then the `Validation` goes into a failed state using `Monoid.Empty` of `F` as /// its failure value. /// [Pure] public static Validation Filter(this K, A> ma, Func pred) where F : Monoid => ma.As().Bind(x => pred(x) ? Validation.Success(x) : Validation.Fail(F.Empty)); /// /// Filter the Validation /// /// /// If the predicate returns `false`, then the `Validation` goes into a failed state using `Monoid.Empty` of `F` as /// its failure value. /// [Pure] public static Validation Where(this K, A> ma, Func pred) where F : Monoid => ma.Filter(pred); /// /// Extract only the successes /// /// Enumerable of validations /// Fail type /// Success type /// Enumerable of successes [Pure] public static IEnumerable Successes(this IEnumerable> vs) { foreach (var v in vs) { if (v.IsSuccess) yield return (S)v; } } /// /// Extract only the failures /// /// Enumerable of validations /// Fail type /// Success type /// Enumerable of failures [Pure] public static IEnumerable Fails(this IEnumerable> vs) { foreach (var v in vs) { if (v.IsFail) yield return (F)v; } } /// /// Extract only the successes /// /// Seq of validations /// Fail type /// Success type /// Enumerable of successes [Pure] public static Seq Successes(this Seq> vs) => toSeq(Successes(vs.AsEnumerable())); /// /// Extract only the failures /// /// Seq of validations /// Fail type /// Success type /// Enumerable of failures [Pure] public static Seq Fails(this Seq> vs) => toSeq(Fails(vs.AsEnumerable())); /// /// Convert `Validation` type to `Fin` type. /// [Pure] public static Fin ToFin(this Validation ma) => ma switch { Validation.Success (var x) => new Fin.Succ(x), Validation.Fail (var x) => new Fin.Fail(x), _ => throw new NotSupportedException() }; /// /// If any items are `Fail`, then the errors are collected and returned. If they all pass, then the Success values /// are collected into a `Seq`. /// /// /// /// Any `TypeLoadException` thrown is because the `F` type used does not derive from `Semigroup〈F〉`. /// This is a runtime error rather than a compile-time constraint error because we're resolving the `Semigroup〈F〉` /// trait ad hoc. /// /// /// That means we delay finding out that the provided `F` type isn't compatible for `Validation〈F, A〉`. That is /// annoying, we would prefer compile-time constraints, of course, but it enables much more freedom to implement the /// `Coproduct`, `Bifunctor`, and `Bimonad` traits which, in turn, give additional functionality for free (like /// `Partition`). /// /// /// Implementation of those traits would not be possible if we were to add compile-time constraints to `F`. So, the /// resolution of any type-exception thrown is to only use `Semigroup〈F〉` deriving types for `F`. /// /// [Pure] internal static Validation CombineFirst( this Validation lhs, Validation rhs, Option> trait) => trait switch { { IsSome: true, Case: SemigroupInstance semi } => (lhs, rhs) switch { ({ IsSuccess: true }, { IsSuccess: true }) => lhs, ({ IsFail: true }, { IsFail: true }) => semi.Combine(lhs.FailValue, rhs.FailValue), ({ IsFail: true }, _) => lhs.FailValue, _ => rhs.FailValue }, _ => (lhs, rhs) switch { ({ IsSuccess: true }, _) => lhs, _ => rhs } }; /// /// If any items are `Fail`, then the errors are collected and returned. If they all pass, then the Success values /// are collected into a `Seq`. /// /// /// /// Any `TypeLoadException` thrown is because the `F` type used does not derive from `Semigroup〈F〉`. /// This is a runtime error rather than a compile-time constraint error because we're resolving the `Semigroup〈F〉` /// trait ad hoc. /// /// /// That means we delay finding out that the provided `F` type isn't compatible for `Validation〈F, A〉`. That is /// annoying, we would prefer compile-time constraints, of course, but it enables much more freedom to implement the /// `Coproduct`, `Bifunctor`, and `Bimonad` traits which, in turn, give additional functionality for free (like /// `Partition`). /// /// /// Implementation of those traits would not be possible if we were to add compile-time constraints to `F`. So, the /// resolution of any type-exception thrown is to only use `Semigroup〈F〉` deriving types for `F`. /// /// [Pure] internal static Validation> CombineI( this Validation lhs, Validation rhs, Option> trait) => trait switch { { IsSome: true, Case: SemigroupInstance semi } => (lhs, rhs) switch { ({ IsSuccess: true }, { IsSuccess: true }) => Validation.SuccessI>([lhs.SuccessValue, rhs.SuccessValue]), ({ IsFail: true }, { IsFail: true }) => semi.Combine(lhs.FailValue, rhs.FailValue), ({ IsFail: true }, _) => lhs.FailValue, _ => rhs.FailValue }, _ => (lhs, rhs) switch { ({ IsSuccess: true, SuccessValue: var x }, { IsSuccess: true, SuccessValue: var y }) => Seq(x, y), ({ IsSuccess: false, FailValue : var lf }, _) => lf, (_, { IsSuccess: false, FailValue : var rf }) => rf } }; /// /// If any items are `Fail`, then the errors are collected and returned. If they all pass, then the Success values /// are collected into a `Seq`. /// /// /// /// Any `TypeLoadException` thrown is because the `F` type used does not derive from `Semigroup〈F〉`. /// This is a runtime error rather than a compile-time constraint error because we're resolving the `Semigroup〈F〉` /// trait ad hoc. /// /// /// That means we delay finding out that the provided `F` type isn't compatible for `Validation〈F, A〉`. That is /// annoying, we would prefer compile-time constraints, of course, but it enables much more freedom to implement the /// `Coproduct`, `Bifunctor`, and `Bimonad` traits which, in turn, give additional functionality for free (like /// `Partition`). /// /// /// Implementation of those traits would not be possible if we were to add compile-time constraints to `F`. So, the /// resolution of any type-exception thrown is to only use `Semigroup〈F〉` deriving types for `F`. /// /// [Pure] public static Validation> CombineI( this Validation> lhs, Validation rhs, Option> trait) => trait switch { { IsSome: true, Case: SemigroupInstance semi } => (lhs, rhs) switch { ({ IsSuccess: true }, { IsSuccess: true }) => Validation.SuccessI>(lhs.SuccessValue.Add(rhs.SuccessValue)), ({ IsFail: true }, { IsFail: true }) => semi.Combine(lhs.FailValue, rhs.FailValue), ({ IsFail: true }, _) => lhs.FailValue, _ => rhs.FailValue }, _ => (lhs, rhs) switch { ({ IsSuccess: true, SuccessValue: var xs }, { IsSuccess: true, SuccessValue: var y }) => xs.Add(y), ({ IsSuccess: false, FailValue : var lf }, _) => lf, (_, { IsSuccess: false, FailValue : var rf }) => rf } }; /// /// If any items are `Fail`, then the errors are collected and returned. If they all pass, then the Success values /// are collected into a `Seq`. /// /// /// /// Any `TypeLoadException` thrown is because the `F` type used does not derive from `Semigroup〈F〉`. /// This is a runtime error rather than a compile-time constraint error because we're resolving the `Semigroup〈F〉` /// trait ad hoc. /// /// /// That means we delay finding out that the provided `F` type isn't compatible for `Validation〈F, A〉`. That is /// annoying, we would prefer compile-time constraints, of course, but it enables much more freedom to implement the /// `Coproduct`, `Bifunctor`, and `Bimonad` traits which, in turn, give additional functionality for free (like /// `Partition`). /// /// /// Implementation of those traits would not be possible if we were to add compile-time constraints to `F`. So, the /// resolution of any type-exception thrown is to only use `Semigroup〈F〉` deriving types for `F`. /// /// [Pure] internal static Validation> CombineI( this Validation lhs, Validation> rhs, Option> trait) => trait switch { { IsSome: true, Case: SemigroupInstance semi } => (lhs, rhs) switch { ({ IsSuccess: true }, { IsSuccess: true }) => Validation.SuccessI>(lhs.SuccessValue.Cons(rhs.SuccessValue)), ({ IsFail: true }, { IsFail: true }) => semi.Combine(lhs.FailValue, rhs.FailValue), ({ IsFail: true }, _) => lhs.FailValue, _ => rhs.FailValue }, _ => (lhs, rhs) switch { ({ IsSuccess: true, SuccessValue: var x }, { IsSuccess: true, SuccessValue: var ys }) => x.Cons(ys), ({ IsSuccess: false, FailValue : var lf }, _) => lf, (_, { IsSuccess: false, FailValue : var rf }) => rf } }; } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/Validation/Extensions/Validation.Guard.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public static class ValidationGuardExtensions { /// /// Natural transformation to `Validation` /// public static Validation ToValidation(this Guard guard) where F : Monoid => guard.Flag ? Validation.Success(default) : Validation.Fail(guard.OnFalse()); /// /// Natural transformation to `Validation` /// internal static Validation ToValidationI(this Guard guard) => guard.Flag ? Validation.SuccessI(default) : Validation.FailI(guard.OnFalse()); /// /// Monadic binding support for `Validation` /// public static Validation Bind( this Guard guard, Func> f) where F : Monoid => guard.Flag ? f(default).As() : Validation.Fail(guard.OnFalse()); /// /// Monadic binding support for `Validation` /// public static Validation SelectMany( this Guard guard, Func> bind, Func project) where F : Monoid => guard.Flag ? bind(default).As().Map(b => project(default, b)) : Validation.Fail(guard.OnFalse()); } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/Validation/Operators/Validation.Operators.Applicative.cs ================================================ using System; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; public static partial class ValidationExtensions { extension(K, A> self) { /// /// Applicative sequence operator /// public static Validation operator >>> (K, A> ma, K, B> mb) => +ma.Action(mb); /// /// Applicative apply operator /// public static Validation operator * (K, Func> mf, K, A> ma) => +mf.Apply(ma); /// /// Applicative apply operator /// public static Validation operator * (K, A> ma, K, Func> mf) => +mf.Apply(ma); } extension(K, A> self) { /// /// Applicative apply operator /// public static Validation> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static Validation> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) { /// /// Applicative apply operator /// public static Validation>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static Validation>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) { /// /// Applicative apply operator /// public static Validation>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static Validation>>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) { /// /// Applicative apply operator /// public static Validation>>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static Validation>>>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) { /// /// Applicative apply operator /// public static Validation>>>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static Validation>>>>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) { /// /// Applicative apply operator /// public static Validation>>>>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static Validation>>>>>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) { /// /// Applicative apply operator /// public static Validation>>>>>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static Validation>>>>>>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) { /// /// Applicative apply operator /// public static Validation>>>>>>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static Validation>>>>>>>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) { /// /// Applicative apply operator /// public static Validation>>>>>>>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static Validation>>>>>>>>> operator *( K, A> ma, K, Func> mf) => curry * mf * ma; } } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/Validation/Operators/Validation.Operators.Choice.cs ================================================ using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt; public static partial class ValidationExtensions { extension(K, A> self) { /// /// Choice operator. Usually means if the first argument succeeds, return it, otherwise return the second /// argument. /// /// Left hand side operand /// Right hand side operand /// public static Validation operator |(K, A> lhs, K, A> rhs) => +lhs.Choose(rhs); /// /// Choice operator. Usually means if the first argument succeeds, return it, otherwise return the second /// argument. /// /// Left hand side operand /// Right hand side operand /// public static Validation operator |(K, A> lhs, Pure rhs) => +lhs.Choose(Validation.SuccessI(rhs.Value)); } } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/Validation/Operators/Validation.Operators.Combine.cs ================================================ using LanguageExt.Traits; namespace LanguageExt; public static partial class ValidationExtensions { extension(K, A> self) where F : Semigroup { /// /// Semigroup combine operator: an associative binary operation. /// /// Left-hand side operand /// Right-hand side operand /// public static Validation operator +(K, A> lhs, K, A> rhs) => +lhs.Combine(rhs); /// /// Semigroup combine operator: an associative binary operation. /// /// Left-hand side operand /// Right-hand side operand /// public static Validation operator +(K, A> lhs, Pure rhs) => +lhs.Combine(Validation.SuccessI(rhs.Value)); /// /// Semigroup combine operator: an associative binary operation. /// /// Left-hand side operand /// Right-hand side operand /// public static Validation operator +(K, A> lhs, Fail rhs) => +lhs.Combine(Validation.FailI(rhs.Value)); /// /// Semigroup combine operator: an associative binary operation. /// /// Left-hand side operand /// Right-hand side operand /// public static Validation operator +(K, A> lhs, F rhs) => +lhs.Combine(Validation.FailI(rhs)); } } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/Validation/Operators/Validation.Operators.CombineSeq.cs ================================================ using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt; public static partial class ValidationExtensions { extension(K, A> self) where F : Semigroup { /// /// Semigroup combine operator: an associative binary operation. /// /// Left-hand side operand /// Right-hand side operand /// public static Validation> operator &(K, A> lhs, K, A> rhs) => (+lhs).CombineI(+rhs, F.Instance); /// /// Semigroup combine operator: an associative binary operation. /// /// Left-hand side operand /// Right-hand side operand /// public static Validation> operator &(K, A> lhs, Pure rhs) => (+lhs).CombineI(Validation.SuccessI(rhs.Value), F.Instance); /// /// Semigroup combine operator: an associative binary operation. /// /// Left-hand side operand /// Right-hand side operand /// public static Validation> operator &(K, A> lhs, Fail rhs) => (+lhs).CombineI(Validation.FailI(rhs.Value), F.Instance); /// /// Semigroup combine operator: an associative binary operation. /// /// Left-hand side operand /// Right-hand side operand /// public static Validation> operator &(K, A> lhs, F rhs) => (+lhs).CombineI(Validation.FailI(rhs), F.Instance); } } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/Validation/Operators/Validation.Operators.Fallible.cs ================================================ using LanguageExt.Traits; namespace LanguageExt; public static partial class ValidationExtensions { extension(K, A> self) { public static Validation operator |(K, A> lhs, CatchM, A> rhs) => +lhs.Catch(rhs); public static Validation operator |(K, A> lhs, Fail rhs) => +lhs.Catch(rhs); } } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/Validation/Operators/Validation.Operators.Functor.cs ================================================ using System; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; public static partial class ValidationExtensions { extension(K, A> _) { /// /// Functor map operator /// public static Validation operator *(Func f, K, A> ma) => +ma.Map(f); /// /// Functor map operator /// public static Validation operator *(K, A> ma, Func f) => +ma.Map(f); } extension(K, A> _) { /// /// Functor map operator /// public static Validation> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static Validation> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) { /// /// Functor map operator /// public static Validation>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static Validation>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) { /// /// Functor map operator /// public static Validation>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static Validation>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) { /// /// Functor map operator /// public static Validation>>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static Validation>>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) { /// /// Functor map operator /// public static Validation>>>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static Validation>>>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) { /// /// Functor map operator /// public static Validation>>>>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static Validation>>>>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) { /// /// Functor map operator /// public static Validation>>>>>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static Validation>>>>>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) { /// /// Functor map operator /// public static Validation>>>>>>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static Validation>>>>>>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) { /// /// Functor map operator /// public static Validation>>>>>>>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static Validation>>>>>>>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/Validation/Operators/Validation.Operators.Monad.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public static partial class ValidationExtensions { extension(K, A> self) { /// /// Monad bind operator /// /// Monad to bind /// Binding function /// Mapped monad public static Validation operator >> (K, A> ma, Func, B>> f) => +ma.Bind(f); /// /// Sequentially compose two actions, discarding any value produced by the first, like sequencing operators (such /// as the semicolon) in C#. /// /// First action to run /// Second action to run /// Result of the second action public static Validation operator >> (K, A> lhs, K, B> rhs) => lhs >> (_ => rhs); } extension(K, A> self) { /// /// Sequentially compose two actions. The second action is a unit-returning action, so the result of the /// first action is propagated. /// /// First action to run /// Second action to run /// Result of the first action public static Validation operator >> (K, A> lhs, K, Unit> rhs) => lhs >> (x => (_ => x) * rhs); } } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/Validation/Operators/Validation.Operators.cs ================================================ using LanguageExt.Traits; namespace LanguageExt; public static partial class ValidationExtensions { extension(K, A> _) { /// /// Downcast operator /// public static Validation operator +(K, A> ma) => (Validation)ma; /// /// Downcast operator /// public static Validation operator >> (K, A> ma, Lower lower) => +ma; } } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/Validation/Prelude/Validation.Prelude.apply.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public static partial class Prelude { /// /// Applicative action: runs the first applicative, ignores the result, and returns the second applicative /// public static Validation action(K, A> ma, K, B> mb) where F : Semigroup => ma.Action(mb); /// /// Applicative-functor apply-operation /// /// /// Unwraps the value within the `ma` applicative-functor, passes it to the unwrapped function(s) within `mf`, and /// then takes the resulting value and wraps it back up into a new applicative-functor. /// /// Value(s) applicative functor /// Mapping function(s) /// Mapped applicative functor public static Validation apply(K, Func> mf, K, A> ma) where F : Semigroup => mf.Apply(ma); } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/Validation/Prelude/Validation.Prelude.cs ================================================  using LanguageExt.Traits; namespace LanguageExt; public static partial class Prelude { /// /// Represents a successful operation /// /// Error type /// Value type /// Value /// Validation applicative public static Validation Success(A value) where F : Monoid => new Validation.Success(value); /// /// Represents a failed operation /// /// Error type /// Value type /// Error value /// Validation applicative public static Validation Fail(F value) where F : Monoid => new Validation.Fail(value); /// /// Represents a failed operation /// /// Error type /// Value type /// Error value /// Validation applicative public static Validation Fail(Seq values) where F : Monoid => new Validation.Fail(Monoid.combine(values)); } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/Validation/Prelude/Validation.Prelude.map.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public static partial class Prelude { /// /// Functor map operation /// /// /// Unwraps the value within the functor, passes it to the map function `f` provided, and /// then takes the mapped value and wraps it back up into a new functor. /// /// Functor to map /// Mapping function /// Mapped functor public static Validation map(Func f, K, A> ma) => Functor.map(f, ma).As(); } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/Validation/Trait/Validation.TraitImpl.2.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public partial class Validation : Coproduct, Bimonad { static K CoproductCons.Left(A value) => FailI(value); static K CoproductCons.Right(B value) => SuccessI(value); static C Coproduct.Match( Func Left, Func Right, K fab) => fab.As2().Match(Fail: Left, Succ: Right); static K Bifunctor.BiMap( Func first, Func second, K fab) => fab.As2().BiMap(Succ: second, Fail: first); static K Bimonad.BindFirst( K ma, Func> f) => ma.As2().BiBind(x => f(x).As2(), SuccessI); static K Bimonad.BindSecond(K ma, Func> f) => ma.As2().BiBind(FailI, x => f(x).As2()); } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/Validation/Trait/Validation.TraitImpl.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; /// /// Trait implementation for `Validation` /// /// Given monad trait public partial class Validation : Monad>, MonoidK>, Alternative>, Traversable>, Fallible> { static K, B> Monad>.Bind( K, A> ma, Func, B>> f) => ma switch { Validation.Success (var x) => f(x), Validation.Fail (var e) => Validation.FailI(e), _ => throw new NotSupportedException() }; static K, B> Monad>.Recur(A value, Func, Next>> f) { while (true) { var mr = +f(value); if (mr.IsFail) return Validation.FailI(mr.FailValue); var next = (Next)mr; if(next.IsDone) return Validation.SuccessI(next.Done); value = next.Loop; } } static K, B> Functor>.Map( Func f, K, A> ma) => ma switch { Validation.Success (var x) => Validation.SuccessI(f(x)), Validation.Fail (var e) => Validation.FailI(e), _ => throw new NotSupportedException() }; static K, A> Applicative>.Pure(A value) => Validation.SuccessI(value); static K, B> Applicative>.Apply( K, Func> mf, K, A> ma) => mf.ApplyI(ma, SemigroupInstance.Instance); static K, B> Applicative>.Apply( K, Func> mf, Memo, A> ma) => mf.ApplyI(ma.Value, SemigroupInstance.Instance); static K, A> Alternative>.Empty() => MonoidInstance.Instance switch { { IsSome: true, Value: { } monoid } => Validation.FailI(monoid.Empty), _ => throw new NotSupportedException($"{typeof(FAIL).Name} must be a Monoid") }; static K, A> MonoidK>.Empty() => MonoidInstance.Instance switch { { IsSome: true, Value: { } monoid } => Validation.FailI(monoid.Empty), _ => throw new NotSupportedException($"{typeof(FAIL).Name} must be a Monoid") }; static K, A> SemigroupK>.Combine( K, A> ma, K, A> mb) => ma.As().CombineFirst(mb.As(), SemigroupInstance.Instance); static K, A> Choice>.Choose( K, A> ma, K, A> mb) => (ma, mb) switch { (Validation.Success, _) => ma, (_, Validation.Success) => mb, _ => ma }; static K, A> Choice>.Choose( K, A> ma, Memo, A> mb) => ma switch { Validation.Success => ma, _ => mb.Value switch { Validation.Success b => b, _ => ma } }; static S Foldable>.FoldWhile( Func> f, Func<(S State, A Value), bool> predicate, S initialState, K, A> ta) => ta switch { Validation.Success (var x) => predicate((initialState, x)) ? f(x)(initialState) : initialState, _ => initialState }; static S Foldable>.FoldBackWhile( Func> f, Func<(S State, A Value), bool> predicate, S initialState, K, A> ta) => ta switch { Validation.Success (var x) => predicate((initialState, x)) ? f(initialState)(x) : initialState, _ => initialState }; static K, B>> Traversable>.Traverse( Func> f, K, A> ta) => ta switch { Validation.Success (var x) => F.Map(Succ, f(x)), Validation.Fail (var e) => F.Pure(Fail(e)), _ => throw new NotSupportedException() }; static K, A> Succ(A value) => Validation.SuccessI(value); static K, A> Fail(FAIL value) => Validation.FailI(value); static K, A> Fallible>.Fail(FAIL error) => Validation.FailI(error); static K, A> Fallible>.Catch( K, A> fa, Func Predicate, Func, A>> Fail) => fa switch { Validation.Success mx => mx, Validation.Fail (var e) => Predicate(e) ? Fail(e) : Validation.FailI(e), _ => throw new NotSupportedException() }; static Fold Foldable>.FoldStep(K, A> ta, S initialState) { var ma = ta.As(); return ma.IsSuccess ? Fold.Loop(initialState, ma.SuccessValue, Fold.Done) : Fold.Done(initialState); } static Fold Foldable>.FoldStepBack(K, A> ta, S initialState) => ta.FoldStep(initialState); } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/Validation/Validation.Fail.cs ================================================ using System; using System.Diagnostics.Contracts; using LanguageExt.ClassInstances; namespace LanguageExt; public abstract partial record Validation { public sealed record Fail(F Value) : Validation { /// /// Is the Validation in a Success state? /// [Pure] public override bool IsSuccess => false; /// /// Is the Validation in a Fail state? /// [Pure] public override bool IsFail => true; /// /// Invokes the Success or Fail function depending on the state of the Validation /// /// Return type /// Function to invoke if in a Success state /// Function to invoke if in a Fail state /// The return value of the invoked function [Pure] public override B Match(Func Fail, Func Succ) => Fail(Value); /// /// Show the structure as a string /// [Pure] public override string ToString() => Value is null ? "Fail(null)" : $"Fail({Value})"; /// /// Get a hash code for the structure /// [Pure] public override int GetHashCode() => Value is null ? 0 : HashableDefault.GetHashCode(Value); /// /// Span of left value /// [Pure] public override ReadOnlySpan FailSpan() => new([Value]); /// /// Empty span /// [Pure] public override ReadOnlySpan SuccessSpan() => ReadOnlySpan.Empty; /// /// Compare this structure to another to find its relative ordering /// [Pure] public override int CompareTo(Validation other) => other switch { Fail l => OrdF.Compare(Value, l.Value), _ => -1 }; /// /// Equality override /// [Pure] public override bool Equals(Validation other) => other switch { Fail l => EqF.Equals(Value, l.Value), _ => false }; /// /// Unsafe access to the right-value /// /// internal override A SuccessValue => throw new InvalidCastException(); /// /// Unsafe access to the left-value /// internal override F FailValue => Value; /// /// Maps the value in the Validation if it's in a Success state /// /// Fail /// Success /// Mapped Validation type /// Map function /// Mapped Validation [Pure] public override Validation Map(Func f) => new Validation.Fail(Value); /// /// Bi-maps the value in the Validation if it's in a Success state /// /// Fail /// Success /// Fail return /// Success return /// Success map function /// Fail map function /// Mapped Validation [Pure] public override Validation BiMap(Func Fail, Func Succ) => new Validation.Fail(Fail(Value)); /// /// Monadic bind /// /// Fail /// Success /// Resulting bound value /// Bind function /// Bound Validation [Pure] public override Validation Bind(Func> f) => new Validation.Fail(Value); /// /// Bi-bind. Allows mapping of both monad states /// [Pure] public override Validation BiBind( Func> Fail, Func> Succ) => Fail(Value); } } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/Validation/Validation.Module.cs ================================================ using System.Diagnostics.Contracts; using LanguageExt.Traits; namespace LanguageExt; public partial class Validation { /// /// Empty failure value /// /// Failure type /// Success /// Validation structure in a failed state [Pure] public static Validation Empty() where F : Monoid => new Validation.Fail(F.Empty); /// /// Represents a successful operation /// /// Error type /// Value type /// Value /// Validation applicative [Pure] public static Validation Success(A value) where F : Monoid => new Validation.Success(value); /// /// Represents a failed operation /// /// Error type /// Value type /// Error value /// Validation applicative [Pure] public static Validation Fail(F value) where F : Monoid => new Validation.Fail(value); /// /// Represents a failed operation /// /// Error type /// Value type /// Error value /// Validation applicative public static Validation Fail(Seq values) where F : Monoid => new Validation.Fail(Monoid.combine(values)); /// /// Represents a successful operation /// /// Error type /// Value type /// Value /// Validation applicative [Pure] internal static Validation SuccessI(A value) => new Validation.Success(value); /// /// Represents a failed operation /// /// Error type /// Value type /// Error value /// Validation applicative [Pure] internal static Validation FailI(F value) => new Validation.Fail(value); } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/Validation/Validation.Success.cs ================================================ using System; using System.Diagnostics.Contracts; using LanguageExt.ClassInstances; namespace LanguageExt; public abstract partial record Validation { public sealed record Success(A Value) : Validation { /// /// Is the Validation in a Success state? /// [Pure] public override bool IsSuccess => true; /// /// Is the Validation in a Left state? /// [Pure] public override bool IsFail => false; /// /// Invokes the Success or Left function depending on the state of the Validation /// /// Return type /// Function to invoke if in a Left state /// Function to invoke if in a Success state /// The return value of the invoked function [Pure] public override B Match( Func Fail, Func Succ) => Succ(Value); /// /// Show the structure as a string /// [Pure] public override string ToString() => Value is null ? "Success(null)" : $"Success({Value})"; /// /// Get a hash code for the structure /// [Pure] public override int GetHashCode() => Value is null ? 0 : HashableDefault.GetHashCode(Value); /// /// Empty span /// [Pure] public override ReadOnlySpan FailSpan() => ReadOnlySpan.Empty; /// /// Span of right value /// [Pure] public override ReadOnlySpan SuccessSpan() => new([Value]); /// /// Compare this structure to another to find its relative ordering /// [Pure] public override int CompareTo(Validation other) => other switch { Success r => OrdA.Compare(Value, r.Value), _ => 1 }; /// /// Equality override /// [Pure] public override bool Equals(Validation other) => other switch { Success r => EqA.Equals(Value, r.Value), _ => false }; /// /// Unsafe access to the right-value /// /// internal override A SuccessValue => Value; /// /// Unsafe access to the left-value /// /// internal override F FailValue => throw new InvalidCastException(); /// /// Maps the value in the Validation if it's in a Success state /// /// Left /// Success /// Mapped Validation type /// Map function /// Mapped Validation [Pure] public override Validation Map(Func f) => new Validation.Success(f(Value)); /// /// Bi-maps the value in the Validation if it's in a Success state /// /// Left /// Success /// Left return /// Success return /// Success map function /// Left map function /// Mapped Validation [Pure] public override Validation BiMap( Func Fail, Func Succ) => new Validation.Success(Succ(Value)); /// /// Monadic bind /// /// Left /// Success /// Resulting bound value /// Bind function /// Bound Validation [Pure] public override Validation Bind(Func> f) => f(Value); /// /// Bi-bind. Allows mapping of both monad states /// [Pure] public override Validation BiBind( Func> Fail, Func> Succ) => Succ(Value); } } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/Validation/Validation.cs ================================================ using System; using System.Collections; using LanguageExt.Traits; using System.Collections.Generic; using static LanguageExt.Prelude; using LanguageExt.ClassInstances; using System.Diagnostics.Contracts; using System.Runtime.CompilerServices; namespace LanguageExt; /// /// Like `Either` but collects multiple failed values /// /// /// /// Any `TypeLoadException` thrown is because the `F` type used does not derive from `Monoid〈F〉` or `Semigroup〈F〉`. /// This is a runtime error rather than a compile-time constraint error because we're resolving the `Monoid〈F〉` and /// `Semigroup〈F〉` traits ad-hoc. /// /// /// That means we delay finding out that the provided `F` type isn't compatible for `Validation〈F, A〉`. That is /// annoying, we would prefer compile-time constraints, of course, but it enables much more freedom to implement the /// `Coproduct`, `Bifunctor`, and `Bimonad` traits which, in turn, give additional functionality for free (like /// `Partition`). /// /// /// Implementation of those traits would not be possible if we were to add compile-time constraints to `F`. So, the /// resolution of any type-exception thrown is to only use `Monoid〈F〉` or `Semigroup〈F〉` deriving types for `F`, /// depending on the functionality required. /// /// /// Failure value type: it is important that this implements `Monoid〈F〉` /// Success value type [Serializable] public abstract partial record Validation : IComparable>, IComparable, IComparable, IEquatable>, IComparable>, IEquatable, K, A>, K { /// /// Is the Validation in a Success state? /// [Pure] public abstract bool IsSuccess { get; } /// /// Is the Validation in a Fail state? /// [Pure] public abstract bool IsFail { get; } /// /// Invokes the Success or Fail function depending on the state of the Validation /// /// Return type /// Function to invoke if in a Fail state /// Function to invoke if in a Success state /// The return value of the invoked function [Pure] public abstract B Match(Func Fail, Func Succ); /// /// Empty span /// [Pure] public abstract ReadOnlySpan FailSpan(); /// /// Span of right value /// [Pure] public abstract ReadOnlySpan SuccessSpan(); /// /// Compare this structure to another to find its relative ordering /// [Pure] public abstract int CompareTo(Validation other) where OrdF : Ord where OrdA : Ord; /// /// Equality override /// [Pure] public abstract bool Equals(Validation other) where EqF : Eq where EqA : Eq; /// /// Unsafe access to the right-value /// /// internal abstract A SuccessValue { get; } /// /// Unsafe access to the left-value /// /// internal abstract F FailValue { get; } /// /// Maps the value in the Validation if it's in a Success state /// /// Fail /// Success /// Mapped Validation type /// Map function /// Mapped Validation [Pure] public abstract Validation Map(Func f); /// /// Bi-maps the value in the Validation if it's in a Success state /// /// Fail /// Success /// Fail return /// Success return /// Success map function /// Fail map function /// Mapped Validation [Pure] public abstract Validation BiMap(Func Fail, Func Succ); /// /// Monadic bind /// /// Fail /// Success /// Resulting bound value /// Bind function /// Bound Validation [Pure] public abstract Validation Bind(Func> f); /// /// Bi-bind. Allows mapping of both monad states /// [Pure] public abstract Validation BiBind( Func> Fail, Func> Succ); /// /// Bind the failure /// [Pure] public Validation BindFail( Func> Fail) where F1 : Monoid => BiBind(Fail, Validation.SuccessI); /// /// Explicit conversion operator from `` to `R` /// /// Value /// Value is not in a Right state [Pure] public static explicit operator A(Validation ma) => ma.SuccessValue; /// /// Explicit conversion operator from `Validation` to `L` /// /// Value /// Value is not in a Fail state [Pure] public static explicit operator F(Validation ma) => ma.FailValue; /// /// Implicit conversion operator from `A` to `Validation〈F, A〉` /// [Pure] public static implicit operator Validation(A value) => new Success(value); /// /// Implicit conversion operator from `F` to `Validation〈F, A〉` /// [Pure] public static implicit operator Validation(F value) => new Fail(value); /// /// Invokes the `Succ` or `Fail` action depending on the state of the value /// /// Action to invoke if in a Fail state /// Action to invoke if in a Success state /// Unit public Unit Match(Action Fail, Action Succ) => Match(fun(Fail), fun(Succ)); /// /// Executes the `Fail` function if the value is in a Fail state. /// Returns the Success value if the value is in a Success state. /// /// Function to generate a value if in the Fail state /// Returns an unwrapped value [Pure] public A IfFail(Func Fail) => Match(_ => Fail(), identity); /// /// Executes the `failMap` function if the value is in a Fail state. /// Returns the Success value if in a Success state. /// /// Function to generate a value if in the Fail state /// Returns an unwrapped value [Pure] public A IfFail(Func failMap) => Match(failMap, identity); /// /// Returns the `successValue` if in a Fail state. /// Returns the Success value if in a Success state. /// /// Value to return if in the Fail state /// Returns an unwrapped value [Pure] public A IfFail(A successValue) => Match(_ => successValue, identity); /// /// Executes the Fail action if in a Fail state. /// /// Function to generate a Success value if in the Fail state /// Unit public Unit IfFail(Action Fail) => Match(Fail, _ => { }); /// /// Invokes the `Success` action if in a Success state, otherwise does nothing /// /// Action to invoke /// Unit public Unit IfRight(Action Success) => Match(_ => { }, Success); [Pure] public int CompareTo(object? obj) => obj is Validation t ? CompareTo(t) : 1; /// /// Project into a `Lst〈A〉` /// /// If in a Success state, a `Lst` of `R` with one item. A zero length `Lst` of `R` otherwise public Lst ToList() => new(SuccessSpan()); /// /// Project into an `Arr〈A〉` /// /// If in a Success state, an `Arr` of `R` with one item. A zero length `Arr` of `R` otherwise public Arr ToArray() => new(SuccessSpan()); /// /// Convert to sequence of 0 or 1 success values /// [Pure] public Seq ToSeq() => new(SuccessSpan()); /// /// Convert to sequence of 0 or 1 success values /// [Pure] public Iterable ToIterable() => [..SuccessSpan()]; /// /// Convert to sequence of 0 or 1 success values /// [Pure] public IEnumerable AsEnumerable() => [..SuccessSpan()]; [Pure] public Either ToEither() => this switch { Success (var x) => new Either.Right(x), Fail (var x) => new Either.Left(x), _ => throw new NotSupportedException() }; /// /// Convert to an Option /// /// Some(Right) or None [Pure] public Option ToOption() => this switch { Success (var x) => Option.Some(x), Fail => Option.None, _ => throw new NotSupportedException() }; /// /// Comparison operator /// /// The left-hand side of the operation /// The right-hand side of the operation /// True if lhs〈 rhs [Pure] public static bool operator <(Validation lhs, Fail rhs) => lhs.CompareTo(rhs) < 0; /// /// Comparison operator /// /// The left-hand side of the operation /// The right-hand side of the operation /// True if lhs〈= rhs [Pure] public static bool operator <=(Validation lhs, Fail rhs) => lhs.CompareTo(rhs) <= 0; /// /// Comparison operator /// /// The left-hand side of the operation /// The right-hand side of the operation /// True if lhs 〉rhs [Pure] public static bool operator >(Validation lhs, Fail rhs) => lhs.CompareTo(rhs) > 0; /// /// Comparison operator /// /// The left-hand side of the operation /// The right-hand side of the operation /// True if lhs 〉= rhs [Pure] public static bool operator >=(Validation lhs, Fail rhs) => lhs.CompareTo(rhs) >= 0; /// /// Comparison operator /// /// The left-hand side of the operation /// The right-hand side of the operation /// True if lhs〈 rhs [Pure] public static bool operator <(Validation lhs, Pure rhs) => lhs.CompareTo(rhs) < 0; /// /// Comparison operator /// /// The left-hand side of the operation /// The right-hand side of the operation /// True if lhs〈= rhs [Pure] public static bool operator <=(Validation lhs, Pure rhs) => lhs.CompareTo(rhs) <= 0; /// /// Comparison operator /// /// The left-hand side of the operation /// The right-hand side of the operation /// True if lhs 〉rhs [Pure] public static bool operator >(Validation lhs, Pure rhs) => lhs.CompareTo(rhs) > 0; /// /// Comparison operator /// /// The left-hand side of the operation /// The right-hand side of the operation /// True if lhs 〉= rhs [Pure] public static bool operator >=(Validation lhs, Pure rhs) => lhs.CompareTo(rhs) >= 0; /// /// Comparison operator /// /// The left-hand side of the operation /// The right-hand side of the operation /// True if lhs〈 rhs [Pure] public static bool operator <(Fail lhs, Validation rhs) => ((Validation)lhs).CompareTo(rhs) < 0; /// /// Comparison operator /// /// The left-hand side of the operation /// The right-hand side of the operation /// True if lhs〈= rhs [Pure] public static bool operator <=(Fail lhs, Validation rhs) => ((Validation)lhs).CompareTo(rhs) <= 0; /// /// Comparison operator /// /// The left-hand side of the operation /// The right-hand side of the operation /// True if lhs 〉rhs [Pure] public static bool operator >(Fail lhs, Validationrhs) => ((Validation)lhs).CompareTo(rhs) > 0; /// /// Comparison operator /// /// The left-hand side of the operation /// The right-hand side of the operation /// True if lhs 〉= rhs [Pure] public static bool operator >=(Fail lhs, Validation rhs) => ((Validation)lhs).CompareTo(rhs) >= 0; /// /// Comparison operator /// /// The left-hand side of the operation /// The right-hand side of the operation /// True if lhs〈 rhs [Pure] public static bool operator <(Pure lhs, Validation rhs) => ((Validation)lhs).CompareTo(rhs) < 0; /// /// Comparison operator /// /// The left-hand side of the operation /// The right-hand side of the operation /// True if lhs〈= rhs [Pure] public static bool operator <=(Pure lhs, Validation rhs) => ((Validation)lhs).CompareTo(rhs) <= 0; /// /// Comparison operator /// /// The left-hand side of the operation /// The right-hand side of the operation /// True if lhs 〉rhs [Pure] public static bool operator >(Pure lhs, Validation rhs) => ((Validation)lhs).CompareTo(rhs) > 0; /// /// Comparison operator /// /// The left-hand side of the operation /// The right-hand side of the operation /// True if lhs 〉= rhs [Pure] public static bool operator >=(Pure lhs, Validation rhs) => ((Validation)lhs).CompareTo(rhs) >= 0; /// /// Comparison operator /// /// The left-hand side of the operation /// The right-hand side of the operation /// True if lhs〈 rhs [Pure] public static bool operator <(Validation lhs, Validation rhs) => lhs.CompareTo(rhs) < 0; /// /// Comparison operator /// /// The left-hand side of the operation /// The right-hand side of the operation /// True if lhs〈= rhs [Pure] public static bool operator <=(Validation lhs, Validation rhs) => lhs.CompareTo(rhs) <= 0; /// /// Comparison operator /// /// The left-hand side of the operation /// The right-hand side of the operation /// True if lhs 〉rhs [Pure] public static bool operator >(Validation lhs, Validation rhs) => lhs.CompareTo(rhs) > 0; /// /// Comparison operator /// /// The left-hand side of the operation /// The right-hand side of the operation /// True if lhs 〉= rhs [Pure] public static bool operator >=(Validation lhs, Validation rhs) => lhs.CompareTo(rhs) >= 0; /// /// Equality operator override /// [Pure] public static bool operator ==(Validation lhs, Fail rhs) => lhs.Equals(rhs); /// /// Equality operator override /// [Pure] public static bool operator ==(Validation lhs, Pure rhs) => lhs.Equals(rhs); /// /// Equality operator override /// [Pure] public static bool operator ==(Fail lhs, Validation rhs) => ((Validation)lhs).Equals(rhs); /// /// Equality operator override /// [Pure] public static bool operator ==(Pure lhs, Validation rhs) => ((Validation)lhs).Equals(rhs); /// /// Non-equality operator override /// [Pure] public static bool operator !=(Validation lhs, Fail rhs) => !(lhs == rhs); /// /// Non-equality operator override /// [Pure] public static bool operator !=(Validation lhs, Pure rhs) => !(lhs == rhs); /// /// Non-equality operator override /// [Pure] public static bool operator !=(Fail lhs, Validation rhs) => !(lhs == rhs); /// /// Non-equality operator override /// [Pure] public static bool operator !=(Pure lhs, Validation rhs) => !(lhs == rhs); /// /// If any items are `Fail`, then the errors are collected and returned. If they all pass, then the Success values /// are collected into a `Seq`. /// /// /// /// Any `TypeLoadException` thrown is because the `F` type used does not derive from `Semigroup〈F〉`. /// This is a runtime error rather than a compile-time constraint error because we're resolving the `Semigroup〈F〉` /// trait ad hoc. /// /// /// That means we delay finding out that the provided `F` type isn't compatible for `Validation〈F, A〉`. That is /// annoying, we would prefer compile-time constraints, of course, but it enables much more freedom to implement the /// `Coproduct`, `Bifunctor`, and `Bimonad` traits which, in turn, give additional functionality for free (like /// `Partition`). /// /// /// Implementation of those traits would not be possible if we were to add compile-time constraints to `F`. So, the /// resolution of any type-exception thrown is to only use `Semigroup〈F〉` deriving types for `F`. /// /// [Pure] public static Validation> operator &(Validation lhs, Validation rhs) => lhs.CombineI(rhs, SemigroupInstance.Instance); /// /// If any items are `Fail`, then the errors are collected and returned. If they all pass, then the Success values /// are collected into a `Seq`. /// /// /// /// Any `TypeLoadException` thrown is because the `F` type used does not derive from `Semigroup〈F〉`. /// This is a runtime error rather than a compile-time constraint error because we're resolving the `Semigroup〈F〉` /// trait ad hoc. /// /// /// That means we delay finding out that the provided `F` type isn't compatible for `Validation〈F, A〉`. That is /// annoying, we would prefer compile-time constraints, of course, but it enables much more freedom to implement the /// `Coproduct`, `Bifunctor`, and `Bimonad` traits which, in turn, give additional functionality for free (like /// `Partition`). /// /// /// Implementation of those traits would not be possible if we were to add compile-time constraints to `F`. So, the /// resolution of any type-exception thrown is to only use `Semigroup〈F〉` deriving types for `F`. /// /// [Pure] public static Validation> operator &(Validation> lhs, Validation rhs) => lhs.CombineI(rhs, SemigroupInstance.Instance); /// /// If any items are `Fail`, then the errors are collected and returned. If they all pass, then the Success values /// are collected into a `Seq`. /// /// /// /// Any `TypeLoadException` thrown is because the `F` type used does not derive from `Semigroup〈F〉`. /// This is a runtime error rather than a compile-time constraint error because we're resolving the `Semigroup〈F〉` /// trait ad hoc. /// /// /// That means we delay finding out that the provided `F` type isn't compatible for `Validation〈F, A〉`. That is /// annoying, we would prefer compile-time constraints, of course, but it enables much more freedom to implement the /// `Coproduct`, `Bifunctor`, and `Bimonad` traits which, in turn, give additional functionality for free (like /// `Partition`). /// /// /// Implementation of those traits would not be possible if we were to add compile-time constraints to `F`. So, the /// resolution of any type-exception thrown is to only use `Semigroup〈F〉` deriving types for `F`. /// /// [Pure] public static Validation> operator &(Validation lhs, Validation> rhs) => lhs.CombineI(rhs, SemigroupInstance.Instance); /// /// Must exist here to make `operator true` work /// public static Validation operator |(Validation lhs, Validation rhs) => lhs.Choose(rhs).As(); /// /// Override of the True operator to return True if the Either is Right /// [Pure] public static bool operator true(Validation value) => value.IsSuccess; /// /// Override of the False operator to return True if the Either is Left /// [Pure] public static bool operator false(Validation value) => value.IsFail; /// /// CompareTo override /// [Pure] public int CompareTo(Validation? other) => other is null ? 1 : CompareTo, OrdDefault>(other); /// /// CompareTo override /// [Pure] public int CompareTo(Validation other) where OrdR : Ord => CompareTo, OrdR>(other); /// /// CompareTo override /// [Pure] public int CompareTo(Fail other) => CompareTo((Validation)other); /// /// CompareTo override /// [Pure] public int CompareTo(Pure other) => CompareTo((Validation)other); /// /// CompareTo override /// [Pure] public int CompareTo(A? other) => other switch { null => 1, _ => CompareTo(Validation.SuccessI(other)) }; /// /// CompareTo override /// [Pure] public int CompareTo(F? other) => other switch { null => 1, _ => CompareTo(Fail(other)) }; /// /// Equality override /// [Pure] public bool Equals(A? other) => other is not null && Equals(Validation.SuccessI(other)); /// /// Equality override /// [Pure] public bool Equals(F? other) => other is not null && Equals(Fail(other)); /// /// Equality override /// [Pure] public virtual bool Equals(Validation? other) => other is not null && Equals, EqDefault>(other); /// /// Equality override /// [Pure] public virtual bool Equals(Validation other) where EqR : Eq => Equals, EqR>(other); /// /// Equality override /// [Pure] public bool Equals(Fail other) => Equals((Validation)other); /// /// Equality override /// [Pure] public bool Equals(Pure other) => Equals((Validation)other); /// /// Match the Success and Fail values but as untyped objects. /// [Pure] public B MatchUntyped(Func Succ, Func Fail) => Match(x => Succ(x), x => Fail(x)); /// /// Iterate the value /// action is invoked if in the Success state /// public Unit Iter(Action Succ) => Match(_ => { }, Succ); /// /// Invokes a predicate on the success value if it's in the Success state /// /// /// True if in a `Left` state. /// `True` if the in a `Right` state and the predicate returns `True`. /// `False` otherwise. [Pure] public bool ForAll(Func Succ) => Match(_ => true, Succ); /// /// Invokes a predicate on the values /// /// Left /// Right /// Either to forall /// Predicate /// Predicate /// True if either Predicate returns true [Pure] public bool BiForAll(Func Fail, Func Succ) => Match(Fail, Succ); /// /// Validation types are like lists of 0 or 1 items and therefore follow the /// same rules when folding. /// /// Aggregate state type /// Initial state /// Folder function, applied if structure is in a Success state /// The aggregate state [Pure] public S Fold(S state, Func Succ) => Match(_ => state, curry(Succ)(state)); /// /// Either types are like lists of 0 or 1 items, and therefore follow the /// Validation types are like lists of 0 or 1 items and therefore follow the /// same rules when folding. /// /// Aggregate state type /// Initial state /// Folder function, applied if in a Success state /// Folder function, applied if in a Fail state /// The aggregate state [Pure] public S BiFold(S state, Func Fail, Func Succ) => Match(curry(Fail)(state), curry(Succ)(state)); /// /// Invokes a predicate on the value if it's in the Success state /// /// Predicate /// True if in a Success state and the predicate returns `True`. `False` otherwise. [Pure] public bool Exists(Func pred) => Match(_ => false, pred); /// /// Impure iteration of the bound values in the structure /// /// /// Returns the original unmodified structure /// public Validation Do(Action f) => Map(r => { f(r); return r; }); /// /// Map each element of a structure to an action, evaluate these actions from /// left to right, and collect the results. /// /// /// Traversable structure /// Applicative functor trait /// Bound value (output) [Pure] public K> Traverse(Func> f) where AF : Applicative => AF.Map(x => x.As(), Traversable.traverse(f, this)); /// /// Maps the value in the Either if it's in a Left state /// /// Left /// Right /// Mapped Either type /// Map function /// Mapped Either [Pure] public Validation MapFail(Func f) where F1 : Monoid => Match(e => Validation.FailI(f(e)), Validation.SuccessI); /// /// Monadic bind /// /// Left /// Right /// /// /// Bound Either [Pure] public Validation Bind(Func, B>> f) => Bind(x => (Validation)f(x)); /// /// Maps the bound value /// [Pure] public Validation Select(Func f) => Map(f); /// /// Monadic bind function /// [Pure] public Validation SelectMany(Func> bind, Func project) => Bind(x => bind(x).Map(y => project(x, y))); //////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // `Pure` and `Fail` support // /// /// Monadic bind /// /// Bind function [Pure] public Validation Bind(Func> f) => Bind(x => (Validation)f(x)); /// /// Monadic bind /// /// Bind function [Pure] public Validation Bind(Func> f) => Bind(x => (Validation)f(x)); /// /// Monadic bind /// /// Bind function [Pure] public Validation Bind(Func> f)=> Bind(a => f(a).ToValidationI()); /// /// Monadic bind and project /// /// Bind function /// Project function [Pure] public Validation SelectMany(Func> bind, Func project) => Bind(x => bind(x).Map(y => project(x, y))); /// /// Monadic bind and project /// /// Bind function /// Project function [Pure] public Validation SelectMany(Func> bind, Func _) => Bind(x => Validation.FailI(bind(x).Value)); /// /// Monadic bind and project /// /// Bind function /// Project function [Pure] public Validation SelectMany( Func> f, Func project) => SelectMany(a => f(a).ToValidationI(), project); [Pure] public static implicit operator Validation(Pure mr) => Validation.SuccessI(mr.Value); [Pure] public static implicit operator Validation(Fail mr) => Validation.FailI(mr.Value); public override int GetHashCode() => HashCode.Combine(IsSuccess, IsFail, SuccessValue, FailValue); } /// /// Context for the fluent Either matching /// public readonly struct ValidationContext { readonly Validation validation; readonly Func success; internal ValidationContext(Validation validation, Func success) { this.validation = validation; this.success = success; } /// /// Fail match /// /// /// Result of the match [Pure] public B Fail(Func fail) => validation.Match(fail, success); } /// /// Context for the fluent Validation matching /// public readonly struct ValidationUnitContext { readonly Validation validation; readonly Action success; internal ValidationUnitContext(Validation validation, Action success) { this.validation = validation; this.success = success; } public Unit Left(Action fail) => validation.Match(fail, success); } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/ValidationT/Extensions/ValidationT.Extensions.Apply.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public static partial class ValidationTExtensions { /// /// Applicative action: runs the first applicative, ignores the result, and returns the second applicative /// public static ValidationT Action(this ValidationT ma, K, B> mb) where F : Monoid where M : Monad => Applicative.action(ma, mb).As(); /// /// Applicative action: runs the first applicative, ignores the result, and returns the second applicative /// public static ValidationT Action(this K, A> ma, K, B> mb) where F : Monoid where M : Monad => Applicative.action(ma, mb).As(); /// /// Applicative functor apply operation /// /// /// Unwraps the value within the `ma` applicative-functor, passes it to the unwrapped function(s) within `mf`, and /// then takes the resulting value and wraps it back up into a new applicative-functor. /// /// Value(s) applicative functor /// Mapping function(s) /// Mapped applicative functor public static ValidationT Apply(this ValidationT> mf, K, A> ma) where F : Monoid where M : Monad => Applicative.apply(mf, ma).As(); /// /// Applicative functor apply operation /// /// /// Unwraps the value within the `ma` applicative-functor, passes it to the unwrapped function(s) within `mf`, and /// then takes the resulting value and wraps it back up into a new applicative-functor. /// /// Value(s) applicative functor /// Mapping function(s) /// Mapped applicative functor public static ValidationT Apply(this K, Func> mf, K, A> ma) where F : Monoid where M : Monad => Applicative.apply(mf, ma).As(); } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/ValidationT/Extensions/ValidationT.Extensions.Map.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public static partial class ValidationTExtensions { /// /// Functor map operation /// /// /// Unwraps the value within the functor, passes it to the map function `f` provided, and /// then takes the mapped value and wraps it back up into a new functor. /// /// Functor to map /// Mapping function /// Mapped functor public static ValidationT Map(this Func f, K, A> ma) where F : Monoid where M : Monad => Functor.map(f, ma).As(); /// /// Functor map operation /// /// /// Unwraps the value within the functor, passes it to the map function `f` provided, and /// then takes the mapped value and wraps it back up into a new functor. /// /// Functor to map /// Mapping function /// Mapped functor public static ValidationT Map(this Func f, ValidationT ma) where F : Monoid where M : Monad => Functor.map(f, ma).As(); } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/ValidationT/Extensions/ValidationT.Extensions.cs ================================================ using System; using static LanguageExt.Prelude; using System.Diagnostics.Contracts; using System.Threading.Tasks; using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt; /// /// ValidationT monad transformer extensions /// public static partial class ValidationTExtensions { public static ValidationT As(this K, A> ma) where M : Monad => (ValidationT)ma; public static ValidationT As2(this K, F, A> ma) where M : Monad => (ValidationT)ma; public static K> Run(this K, A> ma) where M : Monad where F : Monoid => ((ValidationT)ma).Run(F.Instance); public static K Match( this K, A> ma, Func Fail, Func Succ) where M : Monad where F : Monoid => ma.As().MatchI(Fail, Succ)(F.Instance); /// /// Maps the bound value /// /// Mapping function /// Target bound value type /// `ValidationT` public static ValidationT MapFail( this K, A> ma, Func f) where M : Monad where F : Monoid where F1 : Monoid => new(_ => M.Map(mx => mx.MapFail(f), ma.Run())); /// /// Monadic join /// [Pure] public static ValidationT Flatten(this ValidationT> mma) where M : Monad where F : Monoid => mma.Bind(identity); /// /// Get the outer task and wrap it up in a new IO within the EitherT IO /// public static ValidationT Flatten(this Task> tma) where F : Monoid => ValidationT .lift>(IO.liftAsync(async () => await tma.ConfigureAwait(false))) .Flatten(); /// /// Lift the task /// public static ValidationT ToIO(this Task> ma) where L : Monoid => liftIO(ma); public static OptionT ToOption(this ValidationT ma) where F : Monoid where M : Monad => new(ma.runValidation(F.Instance).Map(ma => ma.ToOption())); public static EitherT ToEither(this ValidationT ma) where F : Monoid where M : Monad => new(ma.runValidation(F.Instance).Map(ma => ma.ToEither())); public static FinT ToFin(this ValidationT ma) where M : Monad => new(ma.runValidation(Monoid.instance()).Map(ma => ma.ToFin())); /// /// Monad bind operation /// /// Monadic bind function /// Projection function /// Intermediate bound value type /// Target bound value type /// `ValidationT` [Pure] public static ValidationT SelectMany( this K ma, Func, B>> bind, Func project) where M : Monad where L : Monoid => ValidationT.lift(ma).SelectMany(bind, project); /// /// Monad bind operation /// /// Monadic bind function /// Projection function /// Intermediate bound value type /// Target bound value type /// `ValidationT` [Pure] public static ValidationT SelectMany( this K ma, Func> bind, Func project) where M : Monad where L : Monoid => ValidationT.lift(ma).SelectMany(bind, project); } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/ValidationT/Extensions/ValidationT.Guard.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public static class ValidationTGuardExtensions { /// /// Natural transformation to `ValidationT` /// public static ValidationT ToValidationT(this Guard guard) where M : Monad where F : Monoid => guard.Flag ? ValidationT.Success(default) : ValidationT.Fail(guard.OnFalse()); /// /// Natural transformation to `ValidationT` /// internal static ValidationT ToValidationTI(this Guard guard) where M : Monad => guard.Flag ? ValidationT.SuccessI(default) : ValidationT.FailI(guard.OnFalse()); /// /// Monadic binding support for `ValidationT` /// public static ValidationT Bind( this Guard guard, Func> f) where M : Monad where F : Monoid => guard.Flag ? f(default).As() : ValidationT.Fail(guard.OnFalse()); /// /// Monadic binding support for `Validation` /// public static ValidationT SelectMany( this Guard guard, Func> bind, Func project) where M : Monad where F : Monoid => guard.Flag ? bind(default).As().Map(b => project(default, b)) : ValidationT.Fail(guard.OnFalse()); } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/ValidationT/Operators/ValidationT.Operators.Applicative.cs ================================================ using System; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; public static partial class ValidationTExtensions { extension(K, A> self) where M : Monad { /// /// Applicative sequence operator /// public static ValidationT operator >>> (K, A> ma, K, B> mb) => +ma.Action(mb); /// /// Applicative apply operator /// public static ValidationT operator * (K, Func> mf, K, A> ma) => +mf.Apply(ma); /// /// Applicative apply operator /// public static ValidationT operator * (K, A> ma, K, Func> mf) => +mf.Apply(ma); } extension(K, A> self) where M : Monad { /// /// Applicative apply operator /// public static ValidationT> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static ValidationT> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) where M : Monad { /// /// Applicative apply operator /// public static ValidationT>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static ValidationT>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) where M : Monad { /// /// Applicative apply operator /// public static ValidationT>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static ValidationT>>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) where M : Monad { /// /// Applicative apply operator /// public static ValidationT>>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static ValidationT>>>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) where M : Monad { /// /// Applicative apply operator /// public static ValidationT>>>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static ValidationT>>>>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) where M : Monad { /// /// Applicative apply operator /// public static ValidationT>>>>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static ValidationT>>>>>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) where M : Monad { /// /// Applicative apply operator /// public static ValidationT>>>>>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static ValidationT>>>>>>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) where M : Monad { /// /// Applicative apply operator /// public static ValidationT>>>>>>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static ValidationT>>>>>>>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) where M : Monad { /// /// Applicative apply operator /// public static ValidationT>>>>>>>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static ValidationT>>>>>>>>> operator *( K, A> ma, K, Func> mf) => curry * mf * ma; } } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/ValidationT/Operators/ValidationT.Operators.Choice.cs ================================================ using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt; public static partial class ValidationTExtensions { extension(K, A> self) where M : Monad { /// /// Choice operator. Usually means if the first argument succeeds, return it, otherwise return the second /// argument. /// /// Left hand side operand /// Right hand side operand /// public static ValidationT operator |(K, A> lhs, K, A> rhs) => +lhs.Choose(rhs); /// /// Choice operator. Usually means if the first argument succeeds, return it, otherwise return the second /// argument. /// /// Left hand side operand /// Right hand side operand /// public static ValidationT operator |(K, A> lhs, Pure rhs) => +lhs.Choose(ValidationT.SuccessI(rhs.Value)); } } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/ValidationT/Operators/ValidationT.Operators.Combine.cs ================================================ using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt; public static partial class ValidationTExtensions { extension(K, A> self) where M : Monad where FF : Semigroup { /// /// Semigroup combine operator: an associative binary operation. /// /// Left-hand side operand /// Right-hand side operand /// public static ValidationT operator +(K, A> lhs, K, A> rhs) => +lhs.Combine(rhs); /// /// Semigroup combine operator: an associative binary operation. /// /// Left-hand side operand /// Right-hand side operand /// public static ValidationT operator +(K, A> lhs, Pure rhs) => +lhs.Combine(ValidationT.SuccessI(rhs.Value)); /// /// Semigroup combine operator: an associative binary operation. /// /// Left-hand side operand /// Right-hand side operand /// public static ValidationT operator +(K, A> lhs, Fail rhs) => +lhs.Combine(ValidationT.FailI(rhs.Value)); /// /// Semigroup combine operator: an associative binary operation. /// /// Left-hand side operand /// Right-hand side operand /// public static ValidationT operator +(K, A> lhs, FF rhs) => +lhs.Combine(ValidationT.FailI(rhs)); } } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/ValidationT/Operators/ValidationT.Operators.Fallible.cs ================================================ using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt; public static partial class ValidationTExtensions { extension(K, A> self) where M : Monad { public static ValidationT operator |(K, A> lhs, CatchM, A> rhs) => +lhs.Catch(rhs); public static ValidationT operator |(K, A> lhs, Fail rhs) => +lhs.Catch(rhs); } } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/ValidationT/Operators/ValidationT.Operators.Functor.cs ================================================ using System; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; public static partial class ValidationTExtensions { extension(K, A> _) where M : Monad { /// /// Functor map operator /// public static ValidationT operator *(Func f, K, A> ma) => +ma.Map(f); /// /// Functor map operator /// public static ValidationT operator *(K, A> ma, Func f) => +ma.Map(f); } extension(K, A> _) where M : Monad { /// /// Functor map operator /// public static ValidationT> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static ValidationT> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) where M : Monad { /// /// Functor map operator /// public static ValidationT>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static ValidationT>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) where M : Monad { /// /// Functor map operator /// public static ValidationT>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static ValidationT>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) where M : Monad { /// /// Functor map operator /// public static ValidationT>>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static ValidationT>>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) where M : Monad { /// /// Functor map operator /// public static ValidationT>>>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static ValidationT>>>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) where M : Monad { /// /// Functor map operator /// public static ValidationT>>>>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static ValidationT>>>>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) where M : Monad { /// /// Functor map operator /// public static ValidationT>>>>>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static ValidationT>>>>>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) where M : Monad { /// /// Functor map operator /// public static ValidationT>>>>>>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static ValidationT>>>>>>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) where M : Monad { /// /// Functor map operator /// public static ValidationT>>>>>>>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static ValidationT>>>>>>>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/ValidationT/Operators/ValidationT.Operators.Monad.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public static partial class ValidationTExtensions { extension(K, A> self) where M : Monad { /// /// Monad bind operator /// /// Monad to bind /// Binding function /// Mapped monad public static ValidationT operator >> (K, A> ma, Func, B>> f) => +ma.Bind(f); /// /// Sequentially compose two actions, discarding any value produced by the first, like sequencing operators (such /// as the semicolon) in C#. /// /// First action to run /// Second action to run /// Result of the second action public static ValidationT operator >> (K, A> lhs, K, B> rhs) => lhs >> (_ => rhs); } extension(K, A> self) where M : Monad { /// /// Sequentially compose two actions. The second action is a unit-returning action, so the result of the /// first action is propagated. /// /// First action to run /// Second action to run /// Result of the first action public static ValidationT operator >> (K, A> lhs, K, Unit> rhs) => lhs >> (x => (_ => x) * rhs); } } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/ValidationT/Operators/ValidationT.Operators.cs ================================================ using LanguageExt.Traits; namespace LanguageExt; public static partial class ValidationTExtensions { extension(K, A> _) where M : Monad { /// /// Downcast operator /// public static ValidationT operator +(K, A> ma) => (ValidationT)ma; /// /// Downcast operator /// public static ValidationT operator >> (K, A> ma, Lower lower) => +ma; } } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/ValidationT/Prelude/ValidationT.Prelude.mapapply.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public static partial class Prelude { /// /// Functor map operation /// /// /// Unwraps the value within the functor, passes it to the map function `f` provided, and /// then takes the mapped value and wraps it back up into a new functor. /// /// Functor to map /// Mapping function /// Mapped functor public static ValidationT map(Func f, K, A> ma) where F : Monoid where M : Monad => Functor.map(f, ma).As(); /// /// Applicative action: runs the first applicative, ignores the result, and returns the second applicative /// public static ValidationT action(K, A> ma, K, B> mb) where F : Monoid where M : Monad => Applicative.action(ma, mb).As(); /// /// Applicative functor apply operation /// /// /// Unwraps the value within the `ma` applicative-functor, passes it to the unwrapped function(s) within `mf`, and /// then takes the resulting value and wraps it back up into a new applicative-functor. /// /// Value(s) applicative functor /// Mapping function(s) /// Mapped applicative functor public static ValidationT apply(K, Func> mf, K, A> ma) where F : Monoid where M : Monad => Applicative.apply(mf, ma).As(); } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/ValidationT/Trait/ValidationT.TraitImpl.2.cs ================================================ using System; using LanguageExt.Traits; using static LanguageExt.Prelude; using NSE = System.NotSupportedException; namespace LanguageExt; /// /// Trait implementation for `ValidationT` /// /// Given monad trait public partial class ValidationT : CoproductK>, Bimonad> where M : Monad { static K, F, A> CoproductCons>.Left(F value) => ValidationT.FailI(value); static K, F, A> CoproductCons>.Right(A value) => ValidationT.SuccessI(value); static K, F, B> CoproductK>.Match( Func Left, Func Right, K, F, A> fab) => new ValidationT(monoid => fab.As2().MatchI(Left, Right)(monoid).Map(Validation.SuccessI)); static K, F2, B> Bifunctor>.BiMap( Func first, Func second, K, F1, A> fab) => new ValidationT(_ => MonoidInstance.Instance switch { { IsSome: true, Value: { } t } => fab.As2() .Run(t) .Map(v => v switch { Validation.Fail(var e) => Validation.FailI(first(e)), Validation.Success(var x) => Validation.SuccessI(second(x)), _ => throw new NSE() }), _ => throw new NSE($"Type {typeof(F1).Name} is not a valid monoid") }); static K, F2, A> Bimonad>.BindFirst( K, F1, A> ma, Func, F2, A>> f) => new ValidationT(ty => MonoidInstance.Instance switch { { IsSome: true, Value: { } tx } => ma.As2() .Run(tx) .Bind(v => v switch { Validation.Fail (var e) => f(e).As2().Run(ty), Validation.Success (var x) => M.Pure(Validation.SuccessI(x)), _ => throw new NSE() }), _ => throw new NSE($"Type {typeof(F1).Name} is not a valid monoid") }); static K, F, B> Bimonad>.BindSecond( K, F, A> ma, Func, F, B>> f) => new ValidationT(ty => MonoidInstance.Instance switch { { IsSome: true, Value: { } tx } => ma.As2() .Run(tx) .Bind(v => v switch { Validation.Fail (var e) => M.Pure(Validation.FailI(e)), Validation.Success (var x) => f(x).As2().Run(ty), _ => throw new NSE() }), _ => throw new NSE($"Type {typeof(F).Name} is not a valid monoid") }); } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/ValidationT/Trait/ValidationT.TraitImpl.cs ================================================ using System; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; /// /// Trait implementation for `ValidationT` /// /// Given monad trait public partial class ValidationT : MonadT, M>, MonoidK>, Fallible>, Alternative>, MonadIO> where M : Monad { static K, B> Monad>.Bind( K, A> ma, Func, B>> f) => ma.As().Bind(f); static K, B> Monad>.Recur(A value, Func, Next>> f) => new ValidationT(semi => M.Recur>( value, a => f(a).As() .runValidation(semi) .Map(e => e switch { Validation>.Fail(var err) => Next.Done>(err), Validation>.Success({ IsDone: true } n) => Next.Done>(n.Done), Validation>.Success({ IsLoop: true } n) => Next.Loop>(n.Loop), _ => throw new NotSupportedException() }))); static K, B> Functor>.Map( Func f, K, A> ma) => new ValidationT(monoid => ma.As().Run(monoid).Map(fa => fa.Map(f))); static K, A> Applicative>.Pure(A value) => ValidationT.SuccessI(value); static K, B> Applicative>.Apply( K, Func> mf, K, A> ma) => new ValidationT(monoid => from ff in mf.As().Run(monoid) from fa in ma.As().Run(monoid) select ff.ApplyI(fa, monoid).As()); static K, B> Applicative>.Apply( K, Func> mf, Memo, A> ma) => new ValidationT(monoid => from ff in mf.As().Run(monoid) from fa in ma.Value.As().Run(monoid) select ff.ApplyI(fa, monoid).As()); static K, A> MonadT, M>.Lift(K ma) => ValidationT.liftI(ma); static K, A> MonadIO>.LiftIO(IO ma) => ValidationT.liftIOI(ma); static K, A> MonoidK>.Empty() => new ValidationT(monoid => M.Pure(Validation.FailI(monoid.Empty))); static K, A> Alternative>.Empty() => new ValidationT(monoid => M.Pure(Validation.FailI(monoid.Empty))); static K, A> SemigroupK>.Combine( K, A> ma, K, A> mb) => new ValidationT(monoid => M.Bind(ma.As().Run(monoid), ea => ea.IsSuccess ? M.Pure(ea) : M.Map(eb => ea.CombineFirst(eb.As(), monoid), mb.As().Run(monoid)))); static K, A> Choice>.Choose( K, A> ma, K, A> mb) => new ValidationT(monoid => M.Bind(ma.As().Run(monoid), ea => ea.IsSuccess ? M.Pure(ea) : mb.As().Run(monoid))); static K, A> Choice>.Choose( K, A> ma, Memo, A> mb) => new ValidationT(monoid => M.Bind(ma.As().Run(monoid), ea => ea.IsSuccess ? M.Pure(ea) : mb.Value.As().Run(monoid))); static K, A> Fallible>.Fail(F error) => ValidationT.FailI(error); static K, A> Fallible>.Catch( K, A> fa, Func Predicate, Func, A>> Fail) => fa.As().BindFail(e => Predicate(e) ? Fail(e).As() : ValidationT.FailI(e)); } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/ValidationT/ValidationT.Module.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public partial class ValidationT { public static ValidationT Success(A value) where L : Monoid where M : Monad => new (_ => M.Pure(Validation.Success(value))); public static ValidationT Fail(L value) where L : Monoid where M : Monad => new (_ => M.Pure(Validation.Fail(value))); public static ValidationT lift(Validation ma) where L : Monoid where M : Monad => new (_ => M.Pure(ma)); public static ValidationT lift(K ma) where L : Monoid where M : Monad => new (_ => ma.Map(Validation.Success)); public static ValidationT lift(Pure ma) where L : Monoid where M : Monad => new (_ => M.Pure(Validation.Success(ma.Value))); public static ValidationT lift(Fail ma) where L : Monoid where M : Monad => new (_ => M.Pure(Validation.Fail(ma.Value))); public static ValidationT liftIO(IO ma) where L : Monoid where M : MonadIO => new (_ => M.LiftIO(ma).Map(Validation.Success)); public static K match(K, A> ma, Func Fail, Func Succ) where F : Monoid where M : Monad => ma.Match(Fail, Succ); internal static ValidationT liftI(Validation ma) where M : Monad => new (_ => M.Pure(ma)); internal static ValidationT liftI(K ma) where M : Monad => new (_ => ma.Map(Validation.SuccessI)); internal static ValidationT liftIOI(IO ma) where M : Monad => new (_ => M.LiftIOMaybe(ma).Map(Validation.SuccessI)); internal static ValidationT SuccessI(A value) where M : Monad => new (_ => M.Pure(Validation.SuccessI(value))); internal static ValidationT FailI(L value) where M : Monad => new (_ => M.Pure(Validation.FailI(value))); } ================================================ FILE: LanguageExt.Core/Monads/Alternative Monads/ValidationT/ValidationT.cs ================================================ using System; using System.Diagnostics.Contracts; using System.Runtime.CompilerServices; using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt; /// /// `ValidationT` monad transformer, which allows for an optional result. /// /// Given monad trait /// Left value type /// Bound value type public record ValidationT(Func, K>> runValidation) : K, A>, K, F, A> where M : Monad { //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Match // internal Func, K> MatchI( Func Fail, Func Succ) => monoid => M.Map(mx => mx.Match(Fail, Succ), Run(monoid)); public K> Run(MonoidInstance monoid) => runValidation(monoid); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Map // /// /// Maps the bound monad /// /// Mapping function /// Target monad type /// Target bound value type /// Mapped monad public ValidationT MapT(Func>, K>> f) where M1 : Monad => new(monoid => f(runValidation(monoid))); /// /// Maps the given monad /// /// Mapping function public ValidationT MapM(Func, K> f) => new(monoid => runValidation(monoid) .Bind(fv => fv switch { Validation.Success (var v) => f(M.Pure(v)).Map(Validation.SuccessI), Validation.Fail (var e) => M.Pure>(e), _ => throw new NotSupportedException() })); /// /// Maps the bound value /// /// Mapping function /// Target bound value type /// `ValidationT` public ValidationT Map(Func f) => new(monoid => M.Map(mx => mx.Map(f), runValidation(monoid))); /// /// Maps the bound value /// /// Mapping transducer /// Target bound value type /// `ValidationT` public ValidationT Select(Func f) => new(monoid => M.Map(mx => mx.Map(f), runValidation(monoid))); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Bind // /// /// Monad bind operation /// /// Mapping function /// Target bound value type /// `ValidationT` public ValidationT Bind(Func, B>> f) => Bind(x => f(x).As()); /// /// Monad bind operation /// /// Mapping function /// Target bound value type /// `ValidationT` public ValidationT Bind(Func> f) => new(monoid => M.Bind(runValidation(monoid), ea => ea.IsSuccess switch { true => f(ea.SuccessValue).runValidation(monoid), false => M.Pure(Validation.FailI(ea.FailValue)) })); /// /// Monad bind operation /// /// Success mapping function /// Failure mapping function /// Target bound value type /// `ValidationT` public ValidationT BiBind(Func> Succ, Func> Fail) => new(monoid => M.Bind(runValidation(monoid), ea => ea.IsSuccess switch { true => Succ(ea.SuccessValue).runValidation(monoid), false => Fail(ea.FailValue).runValidation(monoid) })); /// /// Failure bind operation /// /// Failure mapping function /// Target bound value type /// `ValidationT` public ValidationT BindFail(Func> Fail) => BiBind(ValidationT.SuccessI, Fail); /// /// Monad bind operation /// /// Mapping function /// Target bound value type /// `ValidationT` public ValidationT Bind(Func> f) => Bind(a => ValidationT.liftIOI(f(a))); /// /// Monad bind operation /// /// Mapping function /// Target bound value type /// `ValidationT` public ValidationT Bind(Func> f) => Map(a => f(a).Value); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // SelectMany // /// /// Monad bind operation /// /// Monadic bind function /// Projection function /// Intermediate bound value type /// Target bound value type /// `ValidationT` public ValidationT SelectMany(Func, B>> bind, Func project) => SelectMany(x => bind(x).As(), project); /// /// Monad bind operation /// /// Monadic bind function /// Projection function /// Intermediate bound value type /// Target bound value type /// `ValidationT` public ValidationT SelectMany(Func> bind, Func project) => Bind(x => bind(x).Map(y => project(x, y))); /// /// Monad bind operation /// /// Monadic bind function /// Projection function /// Intermediate bound value type /// Target bound value type /// `ValidationT` public ValidationT SelectMany(Func> bind, Func project) => SelectMany(x => ValidationT.liftI(bind(x)), project); /// /// Monad bind operation /// /// Monadic bind function /// Projection function /// Intermediate bound value type /// Target bound value type /// `ValidationT` public ValidationT SelectMany(Func> bind, Func project) => SelectMany(x => ValidationT.liftI(bind(x)), project); /// /// Monad bind operation /// /// Monadic bind function /// Projection function /// Intermediate bound value type /// Target bound value type /// `ValidationT` public ValidationT SelectMany(Func> bind, Func project) => Map(x => project(x, bind(x).Value)); /// /// Monad bind operation /// /// Monadic bind function /// Projection function /// Intermediate bound value type /// Target bound value type /// `ValidationT` public ValidationT SelectMany(Func> bind, Func project) => SelectMany(x => M.LiftIOMaybe(bind(x)), project); /// /// Monad bind operation /// /// Monadic bind function /// Projection function /// Intermediate bound value type /// Target bound value type /// `ValidationT` public ValidationT SelectMany(Func> bind, Func project) => Bind(x => bind(x).ToValidationTI().Map(_ => project(x, default))); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Operators // public static implicit operator ValidationT(Pure ma) => ValidationT.SuccessI(ma.Value); public static implicit operator ValidationT(Fail ma) => ValidationT.FailI(ma.Value); public static implicit operator ValidationT(F fail) => ValidationT.FailI(fail); public static implicit operator ValidationT(IO ma) => ValidationT.liftIOI(ma); public static ValidationT> operator &(ValidationT ma, ValidationT mb) => new(monoid => M.Bind(ma.runValidation(monoid), ea => M.Map(eb => ea & eb, mb.runValidation(monoid)))); } ================================================ FILE: LanguageExt.Core/Monads/ContT/ContT.Module.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt.ContT; public class ContT { /// /// Construct the terminal condition for the monad transformer /// /// Pure value to terminate with /// `ContT` public static ContT Pure(A value) where M : Applicative => new(f => f(value)); public static ContT lift(Func>, K> f) where M : Applicative => new(f); } ================================================ FILE: LanguageExt.Core/Monads/ContT/ContT.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt.ContT; /// /// The continuation monad transformer. /// /// /// Can be used to add continuation handling to any type constructor: /// the `Monad` trait-implementation and most of the operations do not /// require `M` to be a monad. /// /// The continuation /// /// /// public record ContT(Func>, K> runCont) where M : Applicative { /// /// Monadic bind operation /// /// Bind function public ContT Map(Func f) => new(c => runCont(x => c(f(x)))); /// /// Monadic bind operation /// /// Bind function public ContT Bind(Func> f) => new(c => runCont(x => f(x).runCont(c))); /// /// Run the continuation, passing the final continuation /// /// /// public K Run(Func f) => runCont(x => M.Pure(f(x))); } ================================================ FILE: LanguageExt.Core/Monads/ContT/README.md ================================================ ## This type is currently work-in-progress. Not ready for use. ================================================ FILE: LanguageExt.Core/Monads/Free/Extensions/Free.Extensions.MapApply.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public static partial class FreeExtensions { /// /// Functor map operation /// /// /// Unwraps the value within the functor, passes it to the map function `f` provided, and /// then takes the mapped value and wraps it back up into a new functor. /// /// Functor to map /// Mapping function /// Mapped functor public static Free Map(this Func f, K, A> ma) where F : Functor => Functor.map(f, ma).As(); /// /// Functor map operation /// /// /// Unwraps the value within the functor, passes it to the map function `f` provided, and /// then takes the mapped value and wraps it back up into a new functor. /// /// Functor to map /// Mapping function /// Mapped functor public static Free Map(this Func f, Free ma) where F : Functor => Functor.map(f, ma).As(); /// /// Applicative action: runs the first applicative, ignores the result, and returns the second applicative /// public static Free Action(this Free ma, K, B> mb) where F : Functor => Applicative.action(ma, mb).As(); /// /// Applicative action: runs the first applicative, ignores the result, and returns the second applicative /// public static Free Action(this K, A> ma, K, B> mb) where F : Functor => Applicative.action(ma, mb).As(); /// /// Applicative functor apply operation /// /// /// Unwraps the value within the `ma` applicative-functor, passes it to the unwrapped function(s) within `mf`, and /// then takes the resulting value and wraps it back up into a new applicative-functor. /// /// Value(s) applicative functor /// Mapping function(s) /// Mapped applicative functor public static Free Apply(this Free> mf, K, A> ma) where F : Functor => Applicative.apply(mf, ma).As(); /// /// Applicative functor apply operation /// /// /// Unwraps the value within the `ma` applicative-functor, passes it to the unwrapped function(s) within `mf`, and /// then takes the resulting value and wraps it back up into a new applicative-functor. /// /// Value(s) applicative functor /// Mapping function(s) /// Mapped applicative functor public static Free Apply(this K, Func> mf, K, A> ma) where F : Functor => Applicative.apply(mf, ma).As(); } ================================================ FILE: LanguageExt.Core/Monads/Free/Extensions/Free.Extensions.cs ================================================ using LanguageExt.Traits; namespace LanguageExt; public static partial class FreeExtensions { public static Free As(this K, A> ma) where Fnctr : Functor => (Free)ma; } ================================================ FILE: LanguageExt.Core/Monads/Free/Free.Module.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public static class Free { /// /// Terminal case for the free monad /// /// Terminal value /// Functor type /// Bound value type public static Free pure(A value) where F : Functor => new Pure(value); /// /// Lift the functor into the free monad /// /// Functor that yields a `Free` monad /// Functor type /// Bound value type public static Free lift(K value) where F : Functor => new Bind(value.Map(pure)); /// /// Monadic bind case for the free monad /// /// Functor that yields a `Free` monad /// Functor type /// Bound value type public static Free bind(K> value) where F : Functor => new Bind(value); /// /// Lift a natural transformation from `F` to `G` into a natural-transformation from /// `Free F` to `Free G`. /// /// Free monad in F /// Natural transformation /// Functor /// Functor /// Bound value type /// Free monad in G public static Free hoist(Free fb) where F : Functor, Natural where G : Functor => fb switch { Pure(var x) => pure(x), Bind(var xs) => bind(F.Transform(xs).Map(hoist)), _ => throw new NotSupportedException() }; } ================================================ FILE: LanguageExt.Core/Monads/Free/Free.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; /// /// Free monad makes any functor into a monad /// /// Functor type /// Bound value type public abstract record Free : K, A> where F : Functor { public Free Map(Func f) => this.Kind().Map(f).As(); public Free Select(Func f) => this.Kind().Map(f).As(); public Free Bind(Func> f) => this.Kind().Bind(f).As(); public Free Bind(Func, B>> f) => this.Kind().Bind(f).As(); public Free SelectMany(Func, B>> bind, Func project) => Bind(x => bind(x).Map(y => project(x, y))); } /// /// Terminal case for the free monad /// /// Terminal value /// Functor type /// Bound value type public sealed record Pure(A Value) : Free where F : Functor; /// /// Monadic bind case for the free monad /// /// Functor that yields a `Free` monad /// Functor type /// Bound value type public sealed record Bind(K> Value) : Free where F : Functor; ================================================ FILE: LanguageExt.Core/Monads/Free/Operators/Free.Operators.Applicative.cs ================================================ using System; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; public static partial class FreeExtensions { extension(K, A> self) where Fun : Functor { /// /// Applicative sequence operator /// public static Free operator >>> (K, A> ma, K, B> mb) => +ma.Action(mb); /// /// Applicative apply operator /// public static Free operator * (K, Func> mf, K, A> ma) => mf.Apply(ma); /// /// Applicative apply operator /// public static Free operator * (K, A> ma, K, Func> mf) => mf.Apply(ma); } extension(K, A> self) where Fun : Functor { /// /// Applicative apply operator /// public static Free> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static Free> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) where Fun : Functor { /// /// Applicative apply operator /// public static Free>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static Free>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) where Fun : Functor { /// /// Applicative apply operator /// public static Free>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static Free>>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) where Fun : Functor { /// /// Applicative apply operator /// public static Free>>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static Free>>>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) where Fun : Functor { /// /// Applicative apply operator /// public static Free>>>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static Free>>>>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) where Fun : Functor { /// /// Applicative apply operator /// public static Free>>>>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static Free>>>>>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) where Fun : Functor { /// /// Applicative apply operator /// public static Free>>>>>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static Free>>>>>>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) where Fun : Functor { /// /// Applicative apply operator /// public static Free>>>>>>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static Free>>>>>>>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) where Fun : Functor { /// /// Applicative apply operator /// public static Free>>>>>>>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static Free>>>>>>>>> operator *( K, A> ma, K, Func> mf) => curry * mf * ma; } } ================================================ FILE: LanguageExt.Core/Monads/Free/Operators/Free.Operators.Functor.cs ================================================ using System; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; public static partial class FreeExtensions { extension(K, A> _) where Fun : Functor { /// /// Functor map operator /// public static Free operator *(Func f, K, A> ma) => +ma.Map(f); /// /// Functor map operator /// public static Free operator *(K, A> ma, Func f) => +ma.Map(f); } extension(K, A> _) where Fun : Functor { /// /// Functor map operator /// public static Free> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static Free> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) where Fun : Functor { /// /// Functor map operator /// public static Free>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static Free>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) where Fun : Functor { /// /// Functor map operator /// public static Free>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static Free>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) where Fun : Functor { /// /// Functor map operator /// public static Free>>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static Free>>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) where Fun : Functor { /// /// Functor map operator /// public static Free>>>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static Free>>>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) where Fun : Functor { /// /// Functor map operator /// public static Free>>>>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static Free>>>>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) where Fun : Functor { /// /// Functor map operator /// public static Free>>>>>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static Free>>>>>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) where Fun : Functor { /// /// Functor map operator /// public static Free>>>>>>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static Free>>>>>>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) where Fun : Functor { /// /// Functor map operator /// public static Free>>>>>>>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static Free>>>>>>>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } } ================================================ FILE: LanguageExt.Core/Monads/Free/Operators/Free.Operators.Monad.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public static partial class FreeExtensions { extension(K, A> self) where F : Functor { /// /// Monad bind operator /// /// Monad to bind /// Binding function /// Mapped monad public static Free operator >> (K, A> ma, Func, B>> f) => +ma.Bind(f); /// /// Sequentially compose two actions, discarding any value produced by the first, like sequencing operators (such /// as the semicolon) in C#. /// /// First action to run /// Second action to run /// Result of the second action public static Free operator >> (K, A> lhs, K, B> rhs) => lhs >> (_ => rhs); } extension(K, A> self) where F : Functor { /// /// Sequentially compose two actions. The second action is a unit-returning action, so the result of the /// first action is propagated. /// /// First action to run /// Second action to run /// Result of the first action public static Free operator >> (K, A> lhs, K, Unit> rhs) => lhs >> (x => (_ => x) * rhs); } } ================================================ FILE: LanguageExt.Core/Monads/Free/Operators/Free.Operators.cs ================================================ using LanguageExt.Traits; namespace LanguageExt; public static partial class IOExtensions { extension(K, A> _) where F : Functor { /// /// Downcast operator /// public static Free operator +(K, A> ma) => (Free)ma; /// /// Downcast operator /// public static Free operator >> (K, A> ma, Lower lower) => +ma; } } ================================================ FILE: LanguageExt.Core/Monads/Free/Prelude/Free.Prelude.mapapply.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public static partial class Prelude { /// /// Functor map operation /// /// /// Unwraps the value within the functor, passes it to the map function `f` provided, and /// then takes the mapped value and wraps it back up into a new functor. /// /// Functor to map /// Mapping function /// Mapped functor public static Free map(Func f, K, A> ma) where F : Functor => Functor.map(f, ma).As(); /// /// Applicative action: runs the first applicative, ignores the result, and returns the second applicative /// public static Free action(K, A> ma, K, B> mb) where F : Functor => Applicative.action(ma, mb).As(); /// /// Applicative functor apply operation /// /// /// Unwraps the value within the `ma` applicative-functor, passes it to the unwrapped function(s) within `mf`, and /// then takes the resulting value and wraps it back up into a new applicative-functor. /// /// Value(s) applicative functor /// Mapping function(s) /// Mapped applicative functor public static Free apply(K, Func> mf, K, A> ma) where F : Functor => Applicative.apply(mf, ma).As(); } ================================================ FILE: LanguageExt.Core/Monads/Free/Trait/Free.TraitImpl.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; /// /// Free monad makes any functor into a monad /// public class Free : Monad> where F : Functor { static K, B> Functor>.Map(Func f, K, A> ma) { return go(ma.As()); Free go(Free mx) => mx switch { Pure (var a) => new Pure(f(a)), Bind (var fa) => new Bind(F.Map(go, fa)), _ => throw new InvalidOperationException() }; } static K, B> Monad>.Bind(K, A> ma, Func, B>> f) => ma switch { Pure (var a) => f(a), Bind (var m) => new Bind(m.Map(mx => mx.Bind(f).As())), _ => throw new InvalidOperationException() }; static K, B> Monad>.Recur(A value, Func, Next>> f) => Monad.unsafeRecur(value, f); static K, A> Applicative>.Pure(A value) => new Pure(value); static K, B> Applicative>.Apply(K, Func> mf, K, A> ma) => (mf, ma) switch { (Pure> (var f), Pure (var a)) => new Pure(f(a)), (Pure> (var f), Bind (var a)) => new Bind(a.Map(x => x.Map(f).As())), (Bind> (var f), Bind a) => new Bind(f.Map(x => x.Apply(a).As())), _ => throw new InvalidOperationException() }; static K, B> Applicative>.Apply(K, Func> mf, Memo, A> ma) => (mf, ma.Value) switch { (Pure> (var f), Pure (var a)) => new Pure(f(a)), (Pure> (var f), Bind (var a)) => new Bind(a.Map(x => x.Map(f).As())), (Bind> (var f), Bind a) => new Bind(f.Map(x => x.Apply(a).As())), _ => throw new InvalidOperationException() }; } ================================================ FILE: LanguageExt.Core/Monads/Identity/Extensions/Identity.Extensions.cs ================================================ using LanguageExt.Traits; namespace LanguageExt; public static partial class IdentityExtensions { extension(K ma) { public Identity As() => (Identity)ma; public A Run() => ((Identity)ma).Value; } } ================================================ FILE: LanguageExt.Core/Monads/Identity/Identity.Module.cs ================================================ using LanguageExt.Traits; namespace LanguageExt; public partial class Identity { public static Identity Pure(A value) => new (value); static K PureK(A value) => Pure(value); } ================================================ FILE: LanguageExt.Core/Monads/Identity/Identity.cs ================================================ using System; using System.Diagnostics.Contracts; using LanguageExt.ClassInstances; using LanguageExt.Traits; namespace LanguageExt; /// /// Identity monad /// /// /// Simply carries the bound value through its bind expressions without imparting any additional behaviours. It can /// be constructed using: /// /// Identity〈int〉 ma = Id(123); /// /// /// Bound value type public record Identity(A Value) : K, IComparable> { /// /// Map each element of a structure to an action, evaluate these actions from /// left to right, and collect the results. /// /// /// Traversable structure /// Applicative functor trait /// Bound value (output) [Pure] public K> Traverse(Func> f) where F : Applicative => F.Map(x => x.As(), Traversable.traverse(f, this)); /// /// Map each element of a structure to an action, evaluate these actions from /// left to right, and collect the results. /// /// /// Traversable structure /// Monad trait /// Bound value (output) [Pure] public K> TraverseM(Func> f) where M : Monad => M.Map(x => x.As(), Traversable.traverseM(f, this)); [Pure] public virtual bool Equals(Identity? other) => other is not null && EqDefault.Equals(Value, other.Value); [Pure] public override int GetHashCode() => HashableDefault.GetHashCode(Value); [Pure] public Identity Map(Func f) => new(f(Value)); [Pure] public Identity Select(Func f) => new(f(Value)); [Pure] public Identity Bind(Func> f) => f(Value); [Pure] public Identity Bind(Func> f) => f(Value).As(); [Pure] public Identity SelectMany(Func> bind, Func project) => Bind(x => bind(x).Map(y => project(x, y))); [Pure] public Identity SelectMany(Func> bind, Func project) => Bind(x => bind(x).As().Map(y => project(x, y))); [Pure] public int CompareTo(Identity? other) => other is { } rhs ? OrdDefault.Compare(Value, rhs.Value) : 1; } ================================================ FILE: LanguageExt.Core/Monads/Identity/Operators/Identity.Operators.cs ================================================ using LanguageExt.Traits; namespace LanguageExt; public static partial class IdentityExtensions { extension(K _) { /// /// Downcast operator /// public static Identity operator +(K ma) => (Identity)ma; /// /// Downcast operator /// public static Identity operator >> (K ma, Lower lower) => +ma; } } ================================================ FILE: LanguageExt.Core/Monads/Identity/Trait/Identity.TraitImpl.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; /// /// Identity trait implementation /// public partial class Identity : Monad, Traversable { //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Monad // static K Monad.Bind(K ma, Func> f) => ma.As().Bind(f); static K Monad.Recur(A value, Func>> f) { while (true) { var mr = +f(value); if (mr.Value.IsDone) return new Identity(mr.Value.Done); value = mr.Value.Loop; } } static K Functor.Map(Func f, K ma) => ma.As().Map(f); static K Applicative.Pure(A value) => new Identity(value); static K Applicative.Apply(K> mf, K ma) => mf.As().Bind(f => ma.As().Map(f)); static K Applicative.Apply(K> mf, Memo ma) => mf.As().Bind(f => ma.Value.As().Map(f)); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Foldable // static S Foldable.FoldWhile( Func> f, Func<(S State, A Value), bool> predicate, S initialState, K ta) { var id = ta.As(); if (!predicate((initialState, id.Value))) return initialState; return f(id.Value)(initialState); } static S Foldable.FoldBackWhile( Func> f, Func<(S State, A Value), bool> predicate, S initialState, K ta) { var id = ta.As(); if (!predicate((initialState, id.Value))) return initialState; return f(initialState)(id.Value); } static Fold Foldable.FoldStep(K ta, S initialState) => Fold.Loop(initialState, ta.As().Value, Fold.Done); static Fold Foldable.FoldStepBack(K ta, S initialState) => ta.FoldStep(initialState); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Traversable // static K> Traversable.Traverse(Func> f, K ta) => F.Map(PureK, f(ta.As().Value)); } ================================================ FILE: LanguageExt.Core/Monads/IdentityT/IdentityT.Extensions.cs ================================================ using LanguageExt.Traits; namespace LanguageExt; public static class IdentityTExt { extension(K, A> ma) where M : Monad { public IdentityT As() => (IdentityT)ma; public K Run() => ((IdentityT)ma).Value; } } ================================================ FILE: LanguageExt.Core/Monads/IdentityT/IdentityT.Module.cs ================================================ using LanguageExt.Traits; namespace LanguageExt; public class IdentityT { public static IdentityT Pure(A value) where M : Monad => new (M.Pure(value)); public static IdentityT lift(K value) where M : Monad => new (value); } ================================================ FILE: LanguageExt.Core/Monads/IdentityT/IdentityT.Monad.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; /// /// Identity module /// public class IdentityT : MonadT, M>, MonadUnliftIO> where M : Monad { //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Monad // static K, B> Monad>.Bind(K, A> ma, Func, B>> f) => ma.As().Bind(f); static K, B> Monad>.Recur(A value, Func, Next>> f) => // TODO: Make a stack-safe version of this Monad.unsafeRecur(value, f); static K, B> Functor>.Map(Func f, K, A> ma) => ma.As().Map(f); static K, A> Applicative>.Pure(A value) => IdentityT.Pure(value); static K, B> Applicative>.Apply(K, Func> mf, K, A> ma) => mf.As().Bind(f => ma.As().Map(f)); static K, B> Applicative>.Apply(K, Func> mf, Memo, A> ma) => mf.As().Bind(f => ma.Value.As().Map(f)); static K, A> MonadT, M>.Lift(K ma) => IdentityT.lift(ma); static K, A> MonadIO>.LiftIO(IO ma) => IdentityT.lift(M.LiftIOMaybe(ma)); static K, IO> MonadUnliftIO>.ToIO(K, A> ma) => new IdentityT>(M.ToIOMaybe(ma.As().Value)); static K, B> MonadUnliftIO>.MapIO(K, A> ma, Func, IO> f) => new IdentityT(M.MapIOMaybe(ma.As().Value, f)); } ================================================ FILE: LanguageExt.Core/Monads/IdentityT/IdentityT.cs ================================================ using System; using System.Diagnostics.Contracts; using LanguageExt.Traits; namespace LanguageExt; /// /// IdentityT monad /// /// Bound value type public record IdentityT(K Value) : K, A> where M : Monad { [Pure] public IdentityT Map(Func f) => new(M.Map(f, Value)); /// /// Maps the given monad /// /// Mapping function public IdentityT MapM(Func, K> f) => new(f(Value)); [Pure] public IdentityT Select(Func f) => new(M.Map(f, Value)); [Pure] public IdentityT Bind(Func> f) => new(M.Bind(Value, x => f(x).Value)); [Pure] public IdentityT Bind(Func, B>> f) => new(M.Bind(Value, x => f(x).As().Value)); [Pure] public IdentityT Bind(Func> f) => new(M.Map(x => f(x).Value, Value)); [Pure] public IdentityT Bind(Func> f) => new(M.Bind(Value, x => M.LiftIOMaybe(f(x)))); [Pure] public IdentityT SelectMany(Func> bind, Func project) => Bind(x => bind(x).Map(y => project(x, y))); [Pure] public IdentityT SelectMany(Func, B>> bind, Func project) => Bind(x => bind(x).As().Map(y => project(x, y))); [Pure] public IdentityT SelectMany(Func> bind, Func project) => Bind(x => bind(x).Map(y => project(x, y))); [Pure] public IdentityT SelectMany(Func> bind, Func project) => Bind(x => bind(x).Map(y => project(x, y))); /// /// Sequentially compose two actions, discarding any value produced by the first, like sequencing operators (such /// as the semicolon) in C#. /// /// First action to run /// Second action to run /// Result of the second action public static IdentityT operator >> (IdentityT lhs, IdentityT rhs) => lhs.Bind(_ => rhs); /// /// Sequentially compose two actions, discarding any value produced by the first, like sequencing operators (such /// as the semicolon) in C#. /// /// First action to run /// Second action to run /// Result of the second action public static IdentityT operator >> (IdentityT lhs, K, A> rhs) => lhs.Bind(_ => rhs); /// /// Sequentially compose two actions. The second action is a unit returning action, so the result of the /// first action is propagated. /// /// First action to run /// Second action to run /// Result of the first action public static IdentityT operator >> (IdentityT lhs, IdentityT rhs) => lhs.Bind(x => rhs.Map(_ => x)); /// /// Sequentially compose two actions. The second action is a unit returning action, so the result of the /// first action is propagated. /// /// First action to run /// Second action to run /// Result of the first action public static IdentityT operator >> (IdentityT lhs, K, Unit> rhs) => lhs.Bind(x => rhs.Map(_ => x)); } ================================================ FILE: LanguageExt.Core/Monads/Lifting/Lift.Extensions.cs ================================================ using System.Threading.Tasks; namespace LanguageExt; public static class LiftExtensions { public static IO ToIO(this Lift f) => IO.lift(f.Function); public static IO ToIO(this Lift> f) => IO.liftAsync(f.Function); public static IO ToIO(this Lift> f) => IO.liftAsync(f.Function); } ================================================ FILE: LanguageExt.Core/Monads/Lifting/Lift.Prelude.cs ================================================ using System; using System.Collections.Generic; using System.Threading.Tasks; using LanguageExt.Traits; namespace LanguageExt; public static partial class Prelude { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Asynchronous IO lifting // /// /// Lift an action /// /// Function /// Value that can be used with monadic types in LINQ expressions public static Lift lift(Action action) => lift(() => { action(); return default; }); /// /// Lift a function /// /// Function /// Value that can be used with monadic types in LINQ expressions public static Lift lift(Func function) => new(function); /// /// Lift a function /// /// Function /// Value that can be used with monadic types in LINQ expressions public static Lift lift(Func function) => new(function); ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Asynchronous IO lifting // /// /// Lift a asynchronous IO action /// /// Function /// Value that can be used with monadic types in LINQ expressions public static IO liftIO(Func action) => IO.liftAsync(async _ => { await action().ConfigureAwait(false); return unit; }); /// /// Lift a asynchronous IO action /// /// Action /// Value that can be used with monadic types in LINQ expressions public static IO liftIO(Func action) => IO.liftAsync(async env => { await action(env).ConfigureAwait(false); return unit; }); /// /// Lift an asynchronous IO function /// /// Function /// Value that can be used with monadic types in LINQ expressions public static IO liftIO(Func> function) => IO.liftAsync(async () => await function().ConfigureAwait(false)); /// /// Lift an asynchronous IO function /// /// Function /// Value that can be used with monadic types in LINQ expressions public static IO liftIO(Func> function) => IO.liftAsync(async e => await function(e).ConfigureAwait(false)); /// /// Lift an option into an `OptionT IO` /// public static OptionT liftIO(Option ma) => OptionT.lift(ma); /// /// Lift an Option into an `OptionT IO` /// public static OptionT liftIO(Task> ma) => new(IO.liftAsync(async () => await ma.ConfigureAwait(false))); /// /// Lift an `Either` into a `EitherT IO` /// public static EitherT liftIO(Either ma) => EitherT.lift(ma); /// /// Lift an `Either` into a `EitherT IO` /// public static EitherT liftIO(Task> ma) => new(IO.liftAsync(async () => await ma.ConfigureAwait(false))); /// /// Lift an `Either` into a `EitherT IO` /// public static FinT liftIO(Task> ma) => new(IO.liftAsync(async () => await ma.ConfigureAwait(false))); /// /// Lift a `Validation` into a `ValidationT IO` /// public static ValidationT liftIO(Validation ma) where L : Monoid => ValidationT.lift(ma); /// /// Lift a `Validation` into a `ValidationT IO` /// public static ValidationT liftIO(Task> ma) where L : Monoid => new(_ => IO.liftAsync(async () => await ma.ConfigureAwait(false))); /// /// Lift the IO monad into a transformer-stack with an IO as its innermost monad. /// public static K liftIO(IO ma) where T : MonadT where M : Monad => T.Lift(M.LiftIOMaybe(ma)); } ================================================ FILE: LanguageExt.Core/Monads/Lifting/Lift.cs ================================================ using System; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; /// /// Lifts a function into a type that can be used with other /// monadic types (in LINQ expressions, for example) and with implicit /// conversions. /// public record Lift(Func Function) { public K ToApplicative() where F : Applicative => F.Pure(unit).Map(_ => Function()); public IO ToIO() => IO.lift(_ => Function()); public Eff ToEff() => Eff.Lift(Function); public Eff ToEff() => Eff.Lift(Function); public Lift Map(Func f) => new (() => f(Function())); public Lift Bind(Func> f) => new(() => f(Function()).Function()); public Lift Bind(Func> f) => new (() => f(Function()).Value); public K Bind(Func> f) where M : Monad => M.Pure(unit).Map(_ => Function()).Bind(f); public IO Bind(Func> f) => ToIO().Bind(f); public Lift Select(Func f) => Map(f); public IO SelectMany(Func> bind, Func project) => ToIO().SelectMany(bind, project); public K SelectMany(Func> bind, Func project) where M : Monad => Bind(x => bind(x).Map(y => project(x, y))); public Lift SelectMany(Func> bind, Func project) => Bind(x => bind(x).Map(y => project(x, y))); public Lift SelectMany(Func> bind, Func project) => Bind(x => bind(x).Map(y => project(x, y))); } /// /// Lifts a function into a type that can be used with other /// monadic types (in LINQ expressions, for example) and with implicit /// conversions. /// public record Lift(Func Function) { public Lift Map(Func f) => new (x => f(Function(x))); public Lift Bind(Func> f) => new(x => f(Function(x)).Function(x)); public Lift Bind(Func> f) => new (x => f(Function(x)).Value); public Lift Select(Func f) => Map(f); public Lift SelectMany(Func> bind, Func project) => Bind(x => bind(x).Map(y => project(x, y))); } ================================================ FILE: LanguageExt.Core/Monads/Monadic conditionals/Prelude.guard.cs ================================================ using System; using System.Diagnostics.Contracts; using System.Runtime.CompilerServices; using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt; public static partial class Prelude { /// /// Guard against continuing an applicative expression /// /// Flag for continuing /// Applicative that yields `()` if `flag` is `true`; otherwise it yields `Applicative.Empty` - /// shortcutting the expression [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static K guard(bool flag) where F : MonoidK, Applicative => flag ? F.Pure(unit) : F.Empty(); /// /// Guard against continuing an applicative expression /// /// Flag for continuing /// Applicative that yields `()` if `flag` is `true`; otherwise it yields `Applicative.Empty` - /// shortcutting the expression [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static IO guardIO(bool flag) => flag ? IO.pure(unit) : IO.empty(); /// /// Guard against continuing a monadic expression /// /// Flag for continuing /// If the flag is false, this provides the error /// Guard [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Guard guard(bool flag, Func False) => new (flag, False); /// /// Guard against continuing a monadic expression /// /// Flag for continuing /// If the flag is false, this provides the error /// Guard [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Guard guard(bool flag, E False) => new (flag, False); /// /// Guard against continuing a monadic expression /// /// Flag for continuing /// If the flag is false, this provides the error /// Guard [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Guard guard(bool flag, Func False) => new (flag, False); /// /// Guard against continuing a monadic expression /// /// Flag for continuing /// If the flag is false, this provides the error /// Guard [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Guard guard(bool flag, Error False) => new (flag, False); } ================================================ FILE: LanguageExt.Core/Monads/Monadic conditionals/Prelude.guardnot.cs ================================================ using System; using System.Diagnostics.Contracts; using System.Runtime.CompilerServices; using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt; public static partial class Prelude { /// /// Guard against continuing a monadic expression /// /// Flag for continuing /// If the flag is false, this provides the error /// Guard [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Guard guardnot(bool flag, Func True) => new (!flag, True); /// /// Guard against continuing a monadic expression /// /// Flag for continuing /// If the flag is false, this provides the error /// Guard [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Guard guardnot(bool flag, E True) => new (!flag, True); /// /// Guard against continuing a monadic expression /// /// Flag for continuing /// If the flag is false, this provides the error /// Guard [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Guard guardnot(bool flag, Func True) => new (!flag, True); /// /// Guard against continuing a monadic expression /// /// Flag for continuing /// If the flag is false, this provides the error /// Guard [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Guard guardnot(bool flag, Error True) => new (!flag, True); } ================================================ FILE: LanguageExt.Core/Monads/Monadic conditionals/Prelude.iff.cs ================================================ using System.Diagnostics.Contracts; using LanguageExt.Traits; namespace LanguageExt; public static partial class Prelude { /// /// Compute the predicate and depending on its state compute `Then` or `Else` /// /// Predicate /// Computation /// Computation /// Monad /// Unit monad [Pure] public static K iff(K Pred, K Then, K Else) where M : Monad => Pred.Bind(f => f ? Then : Else); /// /// Compute the predicate and depending on its state compute `Then` or `Else` /// /// Predicate /// Computation /// Computation /// Monad /// Unit monad [Pure] public static K iff(K Pred, K Then, K Else) where M : Monad => Pred.Bind(f => f ? Then : M.LiftIOMaybe(Else)); /// /// Compute the predicate and depending on its state compute `Then` or `Else` /// /// Predicate /// Computation /// Computation /// Monad /// Unit monad [Pure] public static K iff(K Pred, K Then, K Else) where M : Monad => Pred.Bind(f => f ? M.LiftIOMaybe(Then) : Else); /// /// Compute the predicate and depending on its state compute `Then` or `Else` /// /// Predicate /// Computation /// Computation /// Monad /// Unit monad [Pure] public static K iff(K Pred, K Then, K Else) where M : Monad => Pred.Bind(f => f ? M.LiftIOMaybe(Then) : M.LiftIOMaybe(Else)); /// /// Compute the predicate and depending on its state compute `Then` or `Else` /// /// Predicate /// Computation /// Computation /// Monad /// Unit monad [Pure] public static K iff(K Pred, K Then, Pure Else) where M : Monad => Pred.Bind(f => f ? Then : M.Pure(Else.Value)); /// /// Compute the predicate and depending on its state compute `Then` or `Else` /// /// Predicate /// Computation /// Computation /// Monad /// Unit monad [Pure] public static K iff(K Pred, Pure Then, K Else) where M : Monad => Pred.Bind(f => f ? M.Pure(Then.Value) : Else); /// /// Compute the predicate and depending on its state compute `Then` or `Else` /// /// Predicate /// Computation /// Computation /// Monad /// Unit monad [Pure] public static K iff(K Pred, Pure Then, Pure Else) where M : Monad => Pred.Bind(f => f ? M.Pure(Then.Value) : M.Pure(Else.Value)); /// /// Compute the predicate and depending on its state compute `Then` or `Else` /// /// Predicate /// Computation /// Computation /// Monad /// Unit monad [Pure] public static K iff(K Pred, Pure Then, K Else) where M : Monad => Pred.Bind(f => f ? M.Pure(Then.Value) : M.LiftIOMaybe(Else)); /// /// Compute the predicate and depending on its state compute `Then` or `Else` /// /// Predicate /// Computation /// Computation /// Monad /// Unit monad [Pure] public static K iff(K Pred, K Then, Pure Else) where M : Monad => Pred.Bind(f => f ? M.LiftIOMaybe(Then) : M.Pure(Else.Value)); /// /// If this then that for higher-kinds /// /// Boolean flag to be computed /// Then branch if the flag computes to `true` /// Else branch if the flag computes to `false` /// Trait type /// Bound return value /// Returns either the `then` or `else` branches depending on the computed `flag` public static K iff(bool Pred, K Then, K Else) => Pred ? Then : Else; } ================================================ FILE: LanguageExt.Core/Monads/Monadic conditionals/Prelude.unless.cs ================================================ using System; using System.Diagnostics.Contracts; using System.Runtime.CompilerServices; using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt; public static partial class Prelude { /// /// Conditional execution of `Applicative` expressions /// /// Run the `alternative` when the `flag` is `false`, return `pure ()` when `true` /// /// If `false` the `alternative` is run /// Computation to run if the flag is `false` /// Either the result of the `alternative` computation if the `flag` is `false` or `Unit` /// /// /// from x in ma /// from _ in unless(x == 100, Console.writeLine〈RT〉("x should be 100!")) /// select x; /// /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static K unless(bool flag, K alternative) where F : Applicative => Applicative.unless(flag, alternative); /// /// When the predicate evaluates to `false`, compute `Then` /// /// Predicate /// Computation /// Monad /// Unit monad [Pure] public static K unless(K Pred, K Then) where M : Monad => Pred.Bind(f => Applicative.unless(f, Then)); /// /// When the predicate evaluates to `false`, compute `Then` /// /// Predicate /// Computation /// Monad /// Unit monad [Pure] public static K unless(K Pred, K Then) where M : MonadIO => Pred.Bind(f => Applicative.unless(f, Then).As()); /// /// When the predicate evaluates to `false`, compute `Then` /// /// Predicate /// Computation /// Monad /// Unit monad [Pure] public static K unless(K Pred, Pure Then) where M : Monad => Pred.Bind(f => Applicative.unless(f, M.Pure(unit))); } ================================================ FILE: LanguageExt.Core/Monads/Monadic conditionals/Prelude.when.cs ================================================ using System.Diagnostics.Contracts; using System.Runtime.CompilerServices; using LanguageExt.Traits; namespace LanguageExt; public static partial class Prelude { /// /// Conditional execution of `Applicative` expressions /// /// Run the `alternative` when the `flag` is `true`, return `pure ()` when `false` /// /// If `true` the `alternative` is run /// Computation to run if the `flag` is `true` /// Either the result of the `alternative` computation if the `flag` is `true` or `Unit` /// /// /// from x in ma /// from _ in when(x == 100, Console.writeLine〈RT〉("x is 100, finally!")) /// select x; /// /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static K when(bool flag, K alternative) where F : Applicative => Applicative.when(flag, alternative); /// /// When the predicate evaluates to `true`, compute `Then` /// /// Predicate /// Computation /// Monad /// Unit monad [Pure] public static K when(K Pred, K Then) where M : Monad => Pred.Bind(f => Applicative.when(f, Then)); /// /// When the predicate evaluates to `true`, compute `Then` /// /// Predicate /// Computation /// Monad /// Unit monad [Pure] public static K when(K Pred, K Then) where M : MonadIO => Pred.Bind(f => Applicative.when(f, Then).As()); /// /// When the predicate evaluates to `true`, compute `Then` /// /// Predicate /// Computation /// Monad /// Unit monad [Pure] public static K when(K Pred, Pure Then) where M : Monad => Pred.Bind(f => Applicative.when(f, M.Pure(unit))); } ================================================ FILE: LanguageExt.Core/Monads/Monadic conditionals/README.md ================================================ `guard`, `when`, and `unless` allow for conditional operations and short-cutting in monadic expressions. ## Guards Guards are used to stop the monadic expression continuing if a flag is `true` (for `guard`) or `false` (for `guardnot`). They only work with monads that have an _'alternative value'_ (which is usually used as the error condition: `Left` in `Either` for example). An alternative value is provided when the guard triggers: from x in ma from _ in guard(x == 100, Error.New("x should be 100")) select x; Supported monads are: Either EitherUnsafe EitherAsync Fin Validation Aff Eff ## When and Unless `when` and `unless` are similar to guards, but instead of providing _the_ alternative value, you provide an alternative monad to run. This monad could be in a failed state, or it could run a successful _side effect_ (an `Eff` calling `Console.writeLine()` for example). from x in ma from _ in when(x == 100, Console.writeLine("x is 100, finally!")) select x; ================================================ FILE: LanguageExt.Core/Monads/Prelude.cs ================================================ using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt; public static partial class Prelude { /// /// Construct identity monad /// public static Identity Id(A value) => Identity.Pure(value); /// /// Construct identity monad /// public static IdentityT Id(A value) where M : Monad, Choice => IdentityT.Pure(value); /// /// Create a new Pure monad. This monad doesn't do much, but when combined with /// other monads, it allows for easier construction of pure lifted values. /// /// There are various bind operators that make it work with these types: /// /// * Option /// * Eff /// * Either /// * Fin /// * IO /// * Validation /// /// /// Value to lift /// Bound value type /// Pure monad public static Pure Pure(A value) => new(value); /// /// Create a new Fail monad: the monad that always fails. This monad doesn't do much, /// but when combined with other monads, it allows for easier construction of lifted /// failure values. /// /// There are various bind operators that make it work with these types: /// /// * Option /// * Eff /// * Either /// * Fin /// * IO /// * Validation /// /// /// Value to lift /// Bound value type /// Pure monad public static Fail Fail(E error) => new(error); /// /// Extractor for types that support lowering via operators /// /// /// For example: /// /// Pure(123) >> lower == 123 /// public static readonly Lower lower = default; } ================================================ FILE: LanguageExt.Core/Monads/README.md ================================================ Not all of the monadic types are in this section, but most of them are. The other monadic types are [the collections](https://louthy.github.io/language-ext/LanguageExt.Core/Immutable%20Collections/index.html), and the [effect-system monads of `IO`, `Eff`, `StreamT`](https://louthy.github.io/language-ext/LanguageExt.Core/Effects/index.html), and [`Pipes`](https://louthy.github.io/language-ext/LanguageExt.Pipes/index.html) ================================================ FILE: LanguageExt.Core/Monads/State and Environment Monads/RWS/RWST/Extensions/RWST.Extensions.cs ================================================ using System; using System.Diagnostics.Contracts; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; public static partial class RWSTExtensions { public static RWST As(this K, A> ma) where M : Monad where W : Monoid => (RWST)ma; public static K Run( this K, A> ma, R env, W output, S state) where M : Monad where W : Monoid => ma.As().runRWST((env, output, state)); public static K Run( this K, A> ma, R env, S state) where M : Monad where W : Monoid => ma.As().runRWST((env, W.Empty, state)); /// /// Monadic join /// [Pure] public static RWST Flatten(this RWST> mma) where W : Monoid where M : Monad => mma.Bind(x => x); /// /// Monadic join /// [Pure] public static RWST Flatten(this RWST, A>> mma) where W : Monoid where M : Monad => mma.Bind(x => x); /// /// Monad bind operation /// /// Monadic bind function /// Projection function /// Intermediate bound value type /// Target bound value type /// `ReaderT` public static RWST SelectMany( this K ma, Func, B>> bind, Func project) where W : Monoid where M : Monad => RWST.Lift(ma).SelectMany(bind, project); /// /// Monad bind operation /// /// Monadic bind function /// Projection function /// Intermediate bound value type /// Target bound value type /// `ReaderT` public static RWST SelectMany( this K ma, Func> bind, Func project) where W : Monoid where M : Monad => RWST.Lift(ma).SelectMany(bind, project); /// /// Monad bind operation /// /// Monadic bind function /// Projection function /// Intermediate bound value type /// Target bound value type /// `ReaderT` public static RWST SelectMany( this K ma, Func, B>> bind, Func project) where W : Monoid where M : Monad => RWST.LiftIO(ma).SelectMany(bind, project); /// /// Monad bind operation /// /// Monadic bind function /// Projection function /// Intermediate bound value type /// Target bound value type /// `ReaderT` public static RWST SelectMany( this K ma, Func> bind, Func project) where W : Monoid where M : Monad => RWST.LiftIO(ma).SelectMany(bind, project); } ================================================ FILE: LanguageExt.Core/Monads/State and Environment Monads/RWS/RWST/Operators/RWST.Operators.Applicative.cs ================================================ using System; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; public static partial class RWSTExtensions { extension(K, A> self) where M : Monad where W : Monoid { /// /// Applicative sequence operator /// public static RWST operator >>> (K, A> ma, K, B> mb) => +ma.Action(mb); /// /// Applicative apply operator /// public static RWST operator * (K, Func> mf, K, A> ma) => +mf.Apply(ma); /// /// Applicative apply operator /// public static RWST operator * (K, A> ma, K, Func> mf) => +mf.Apply(ma); } extension(K, A> self) where M : Monad where W : Monoid { /// /// Applicative apply operator /// public static RWST> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static RWST> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) where M : Monad where W : Monoid { /// /// Applicative apply operator /// public static RWST>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static RWST>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) where M : Monad where W : Monoid { /// /// Applicative apply operator /// public static RWST>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static RWST>>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) where M : Monad where W : Monoid { /// /// Applicative apply operator /// public static RWST>>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static RWST>>>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) where M : Monad where W : Monoid { /// /// Applicative apply operator /// public static RWST>>>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static RWST>>>>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) where M : Monad where W : Monoid { /// /// Applicative apply operator /// public static RWST>>>>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static RWST>>>>>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) where M : Monad where W : Monoid { /// /// Applicative apply operator /// public static RWST>>>>>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static RWST>>>>>>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) where M : Monad where W : Monoid { /// /// Applicative apply operator /// public static RWST>>>>>>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static RWST>>>>>>>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) where M : Monad where W : Monoid { /// /// Applicative apply operator /// public static RWST>>>>>>>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static RWST>>>>>>>>> operator *( K, A> ma, K, Func> mf) => curry * mf * ma; } } ================================================ FILE: LanguageExt.Core/Monads/State and Environment Monads/RWS/RWST/Operators/RWST.Operators.Choice.cs ================================================ using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt; public static partial class RWSTExtensions { extension(K, A> self) where M : Monad, Choice where W : Monoid { /// /// Choice operator. Usually means if the first argument succeeds, return it, otherwise return the second /// argument. /// /// Left hand side operand /// Right hand side operand /// public static RWST operator |(K, A> lhs, K, A> rhs) => new (env => lhs.As().runRWST(env) | rhs.As().runRWST(env)); /// /// Choice operator. Usually means if the first argument succeeds, return it, otherwise return the second /// argument. /// /// Left hand side operand /// Right hand side operand /// public static RWST operator |(K, A> lhs, Pure rhs) => new (env => lhs.As().runRWST(env) | rhs.Map(x => (x, env.Output, env.State))); } } ================================================ FILE: LanguageExt.Core/Monads/State and Environment Monads/RWS/RWST/Operators/RWST.Operators.Fallible.cs ================================================ using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt; public static partial class RWSTExtensions { extension(K, A> self) where M : Monad, Fallible where W : Monoid { public static RWST operator |(K, A> lhs, CatchM rhs) => new(env => lhs.As().runRWST(env) | rhs.Map(a => (a, env.Output, env.State))); public static RWST operator |(K, A> lhs, Fail rhs) => new(env => lhs.As().runRWST(env) | rhs); } } ================================================ FILE: LanguageExt.Core/Monads/State and Environment Monads/RWS/RWST/Operators/RWST.Operators.Final.cs ================================================ namespace LanguageExt.Traits; public static class RWSTExtensions { extension(K, A> _) where M : Monad, Final where W : Monoid { /// /// Run a `finally` operation after the main operation regardless of whether it succeeds or not. /// /// Primary operation /// Finally operation /// Result of primary operation public static RWST operator |(K, A> lhs, Finally rhs) => new(env => lhs.As().runRWST(env) | rhs); } } ================================================ FILE: LanguageExt.Core/Monads/State and Environment Monads/RWS/RWST/Operators/RWST.Operators.Functor.cs ================================================ using System; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; public static partial class RWSTExtensions { extension(K, A> _) where M : Monad where W : Monoid { /// /// Functor map operator /// public static RWST operator *(Func f, K, A> ma) => +ma.Map(f); /// /// Functor map operator /// public static RWST operator *(K, A> ma, Func f) => +ma.Map(f); } extension(K, A> _) where M : Monad where W : Monoid { /// /// Functor map operator /// public static RWST> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static RWST> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) where M : Monad where W : Monoid { /// /// Functor map operator /// public static RWST>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static RWST>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) where M : Monad where W : Monoid { /// /// Functor map operator /// public static RWST>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static RWST>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) where M : Monad where W : Monoid { /// /// Functor map operator /// public static RWST>>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static RWST>>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) where M : Monad where W : Monoid { /// /// Functor map operator /// public static RWST>>>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static RWST>>>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) where W : Monoid where M : Monad { /// /// Functor map operator /// public static RWST>>>>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static RWST>>>>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) where M : Monad where W : Monoid { /// /// Functor map operator /// public static RWST>>>>>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static RWST>>>>>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) where M : Monad where W : Monoid { /// /// Functor map operator /// public static RWST>>>>>>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static RWST>>>>>>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) where M : Monad where W : Monoid { /// /// Functor map operator /// public static RWST>>>>>>>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static RWST>>>>>>>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } } ================================================ FILE: LanguageExt.Core/Monads/State and Environment Monads/RWS/RWST/Operators/RWST.Operators.Monad.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public static partial class RWSTExtensions { extension(K, A> self) where M : Monad where W : Monoid { /// /// Monad bind operator /// /// Monad to bind /// Binding function /// Mapped monad public static RWST operator >> (K, A> ma, Func, B>> f) => +ma.Bind(f); /// /// Sequentially compose two actions, discarding any value produced by the first, like sequencing operators (such /// as the semicolon) in C#. /// /// First action to run /// Second action to run /// Result of the second action public static RWST operator >> (K, A> lhs, K, B> rhs) => lhs >> (_ => rhs); } extension(K, A> self) where M : Monad where W : Monoid { /// /// Sequentially compose two actions. The second action is a unit-returning action, so the result of the /// first action is propagated. /// /// First action to run /// Second action to run /// Result of the first action public static RWST operator >> (K, A> lhs, K, Unit> rhs) => lhs >> (x => (_ => x) * rhs); } } ================================================ FILE: LanguageExt.Core/Monads/State and Environment Monads/RWS/RWST/Operators/RWST.Operators.SemigroupK.cs ================================================ using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt; public static partial class RWSTExtensions { extension(K, A> self) where M : Monad, SemigroupK where W : Monoid { /// /// Semigroup combine operator: an associative binary operation. /// /// Left-hand side operand /// Right-hand side operand /// public static RWST operator +(K, A> lhs, K, A> rhs) => new(env => lhs.As().runRWST(env) + rhs.As().runRWST(env)); /// /// Semigroup combine operator: an associative binary operation. /// /// Left-hand side operand /// Right-hand side operand /// public static RWST operator +(K, A> lhs, Pure rhs) => new(env => lhs.As().runRWST(env) + M.Pure((rhs.Value, env.Output, env.State))); } extension(K, A> self) where M : Monad, SemigroupK, Fallible where W : Monoid { /// /// Semigroup combine operator: an associative binary operation. /// /// Left-hand side operand /// Right-hand side operand /// public static RWST operator +(K, A> lhs, Fail rhs) => new(env => lhs.As().runRWST(env) + M.Fail<(A, W, S)>(rhs.Value)); } } ================================================ FILE: LanguageExt.Core/Monads/State and Environment Monads/RWS/RWST/Operators/RWST.Operators.cs ================================================ using LanguageExt.Traits; namespace LanguageExt; public static partial class RWSTExtensions { extension(K, A> _) where M : Monad where W : Monoid { /// /// Downcast operator /// public static RWST operator +(K, A> ma) => (RWST)ma; /// /// Downcast operator /// public static RWST operator >> (K, A> ma, Lower lower) => +ma; } } ================================================ FILE: LanguageExt.Core/Monads/State and Environment Monads/RWS/RWST/RWST.Module.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public static class RWST { public static RWST pure(A value) where W : Monoid where M : Monad => RWST.Pure(value); public static RWST lift(K ma) where W : Monoid where M : Monad => RWST.Lift(ma); /// /// Lifts a given monad into the transformer /// /// Monad to lift /// `ReaderT` public static RWST liftIO(IO effect) where W : Monoid where M : Monad => RWST.LiftIO(effect); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Reader behaviours // public static RWST ask() where W : Monoid where M : Monad => Readable.ask, R>().As(); public static RWST asks(Func f) where W : Monoid where M : Monad => Readable.asks, R, A>(f).As(); public static RWST asksM(Func> f) where W : Monoid where M : Monad => RWST.AsksM(f); public static RWST asksM(Func, A>> f) where W : Monoid where M : Monad => Readable.asksM(f).As(); public static RWST local(Func f, K, A> ma) where W : Monoid where M : Monad => Readable.local(f, ma).As(); public static RWST with(Func f, K, A> ma) where W : Monoid where M : Monad => ma.As().With(f); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Writer behaviours // /// /// Tell is an action that produces the writer output /// /// Item to tell /// Writer type /// Structure with the told item public static RWST tell(W item) where M : Monad where W : Monoid => Writable.tell, W>(item).As(); /// /// Writes an item and returns a value at the same time /// public static RWST write((A, W) item) where M : Monad where W : Monoid => Writable.write, A>(item).As(); /// /// Writes an item and returns a value at the same time /// public static RWST write(A value, W item) where M : Monad where W : Monoid => Writable.write, A>(value, item).As(); /// /// `pass` is an action that executes the `action`, which returns a value and a /// function; it then returns the value with the output having been applied to /// the function. /// public static RWST pass(RWST Function)> action) where M : Monad where W : Monoid => Writable.pass(action).As(); /// /// `listen` executes the action `ma` and adds the result of applying `f` to the /// output to the value of the computation. /// public static RWST listen(RWST ma) where M : Monad where W : Monoid => Writable.listen, A>(ma).As(); /// /// `listens` executes the action `ma` and adds the result of applying `f` to the /// output to the value of the computation. /// public static RWST listens(Func f, RWST ma) where M : Monad where W : Monoid => Writable.listens(f, ma).As(); /// /// `censor` executes the action `ma` and applies the function `f` to its output, /// leaving the return value unchanged. /// public static RWST censor(Func f, RWST ma) where M : Monad where W : Monoid => Writable.censor(f, ma).As(); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // State behaviours // public static RWST get() where W : Monoid where M : Monad => Stateful.get, S>().As(); public static RWST gets(Func f) where W : Monoid where M : Monad => Stateful.gets, S, A>(f).As(); public static RWST getsM(Func> f) where W : Monoid where M : Monad => RWST.GetsM(f); public static RWST getsM(Func, A>> f) where W : Monoid where M : Monad => Stateful.getsM(f).As(); public static RWST put(S state) where W : Monoid where M : Monad => Stateful.put, S>(state).As(); public static RWST modify(Func f) where W : Monoid where M : Monad => Stateful.modify, S>(f).As(); public static RWST modifyM(Func> f) where W : Monoid where M : Monad => RWST.ModifyM(f); public static RWST modifyM(Func, S>> f) where W : Monoid where M : Monad => Stateful.modifyM(f).As(); } ================================================ FILE: LanguageExt.Core/Monads/State and Environment Monads/RWS/RWST/RWST.cs ================================================ using System; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; /// /// Reader / Write / State monad-transformer /// /// Reader environment type /// Writer output type /// State type /// Lifted monad type /// Bound value type public record RWST(Func<(R Env, W Output, S State), K> runRWST): K, A> where M : Monad where W : Monoid { public static RWST Pure(A value) => new (input => M.Pure((value, input.Output, input.State))); public static RWST Lift(K ma) => new (input => ma.Map(a => (a, input.Output, input.State))); public static RWST Lift(Pure ma) => new (input => M.Pure((ma.Value, input.Output, input.State))); public static RWST LiftIO(K ma) => new (input => M.LiftIOMaybe(ma).Map(a => (a, input.Output, input.State))); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Reader behaviours // /// /// Extracts the environment value and maps it to the bound value /// /// Environment mapping function /// `RWST` public static RWST Asks(Func f) => new(input => M.Pure((f(input.Env), input.Output, input.State))); /// /// Extracts the environment value and maps it to the bound value /// /// Environment mapping function /// `RWST` public static RWST AsksM(Func> f) => new(input => f(input.Env).Map(a => (a, input.Output, input.State))); /// /// Maps the Reader's environment value /// /// Mapping function /// `RWST` public RWST With(Func f) => new(input => runRWST((f(input.Env), input.Output, input.State))); /// /// Maps the Reader's environment value /// /// Mapping function /// `RWST` public RWST Local(Func f) => new(input => runRWST((f(input.Env), input.Output, input.State))); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Writer behaviours // /// /// Construct a writer computation from a (result, output) pair. /// /// /// The inverse of `Run()` /// /// Result / Output pair public static RWST Write((A Value, W Output) result) => Writable.write, A>(result).As(); /// /// Construct a writer computation from a (result, output) pair. /// /// /// The inverse of `Run()` /// /// Result / Output pair public static RWST Write(A value, W output) => Writable.write, A>(value, output).As(); /// /// Writes an item and returns a value at the same time /// public RWST Listen => Writable.listen, A>(this).As(); /// /// `Listens` executes the action and adds the result of applying `f` to the /// output to the value of the computation. /// public RWST Listens(Func f) => Writable.listens(f, this).As(); /// /// `Censor` executes the action and applies the function `f` to its output, /// leaving the return value unchanged. /// public RWST Censor(Func f) => Writable.censor(f, this).As(); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // State behaviours // /// /// Extracts the state value, maps it, and then puts it back into /// the monadic state. /// /// State mapping function /// `RWST` public static RWST Modify(Func f) => new(input => M.Pure((unit, input.Output, f(input.State)))); /// /// Extracts the state value, maps it, and then puts it back into /// the monadic state. /// /// State mapping function /// `RWST` public static RWST ModifyM(Func> f) => new(input => f(input.State).Map(s => (unit, input.Output, s))); /// /// Writes the value into the monadic state /// /// `RWST` public static RWST Put(S value) => new(input => M.Pure((unit, input.Output, value))); /// /// Writes a value and state into the monad /// /// `RWST` public static RWST State(A value, S state) => new(input => M.Pure((value, input.Output, state))); /// /// Writes a value and state into the monad /// /// `RWST` public static RWST State((A Value, S State) ma) => new(input => M.Pure((ma.Value, input.Output, ma.State))); /// /// Extracts the state value and returns it as the bound value /// /// `RWST` public static RWST Get { get; } = new(input => M.Pure((input.State, input.Output, input.State))); /// /// Extracts the state value and maps it to the bound value /// /// State mapping function /// `RWST` public static RWST Gets(Func f) => new(input => M.Pure((f(input.State), input.Output, input.State))); /// /// Extracts the state value and maps it to the bound value /// /// State mapping function /// `RWST` public static RWST GetsM(Func> f) => new(input => M.Map(v => (v, input.Output, input.State), f(input.State))); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Map // /// /// Maps the bound value /// /// Mapping function /// Target bound value type /// `RWST` public RWST Map(Func f) => Functor.map(f, this).As(); /// /// Maps the bound value /// /// Mapping transducer /// Target bound value type /// `RWST` public RWST Select(Func f) => Functor.map(f, this).As(); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Bind // /// /// Monad bind operation /// /// Mapping function /// Target bound value type /// `RWST` public RWST Bind(Func, B>> f) => Monad.bind(this, f).As(); /// /// Monad bind operation /// /// Mapping function /// Target bound value type /// `RWST` public RWST Bind(Func> f) => Monad.bind(this, f).As(); /// /// Monad bind operation /// /// Mapping function /// Target bound value type /// `RWST` public RWST Bind(Func> f) => Bind(x => (RWST)f(x)); /// /// Monad bind operation /// /// Mapping function /// Target bound value type /// `RWST` public RWST Bind(Func> f) => Bind(x => RWST.LiftIO(f(x))); /// /// Monad bind operation /// /// Mapping function /// Target bound value type /// `RWST` public RWST Bind(Func> f) => Bind(x => RWST.LiftIO(f(x))); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // SelectMany // /// /// Monad bind operation /// /// Monadic bind function /// Projection function /// Intermediate bound value type /// Target bound value type /// `RWST` public RWST SelectMany(Func> bind, Func project) => new(input => runRWST(input).Bind( output1 => bind(output1.Value) .runRWST((input.Env, output1.Output, output1.State)) .Map(output2 => (project(output1.Value, output2.Value), output2.Output, output2.State)))); /// /// Monad bind operation /// /// Monadic bind function /// Projection function /// Intermediate bound value type /// Target bound value type /// `RWST` public RWST SelectMany(Func, B>> bind, Func project) => SelectMany(x => bind(x).As(), project); /// /// Monad bind operation /// /// Monadic bind function /// Projection function /// Intermediate bound value type /// Target bound value type /// `RWST` public RWST SelectMany(Func> bind, Func project) => SelectMany(x => RWST.Lift(bind(x)), project); /// /// Monad bind operation /// /// Monadic bind function /// Projection function /// Intermediate bound value type /// Target bound value type /// `RWST` public RWST SelectMany(Func> bind, Func project) => Map(x => project(x, bind(x).Value)); /// /// Monad bind operation /// /// Monadic bind function /// Projection function /// Intermediate bound value type /// Target bound value type /// `RWST` public RWST SelectMany(Func> bind, Func project) => SelectMany(x => RWST.LiftIO(bind(x)), project); /// /// Monad bind operation /// /// Monadic bind function /// Projection function /// Intermediate bound value type /// Target bound value type /// `RWST` public RWST SelectMany(Func> bind, Func project) => SelectMany(x => RWST.LiftIO(bind(x)), project); /// /// Monad bind operation /// /// Monadic bind function /// Projection function /// Intermediate bound value type /// Target bound value type /// `RWST` public RWST SelectMany(Func> bind, Func project) => SelectMany(x => bind(x).ToReadable>(), project); /// /// Monad bind operation /// /// Monadic bind function /// Projection function /// Intermediate bound value type /// Target bound value type /// `RWST` public RWST SelectMany(Func> bind, Func project) => SelectMany(x => bind(x).ToStateful>(), project); /// /// Monad bind operation /// /// Monadic bind function /// Projection function /// Intermediate bound value type /// Target bound value type /// `RWST` public RWST SelectMany(Func> bind, Func project) => SelectMany(x => bind(x).ToStateful>(), project); /// /// Monad bind operation /// /// Monadic bind function /// Projection function /// Intermediate bound value type /// Target bound value type /// `RWST` public RWST SelectMany(Func> bind, Func project) => SelectMany(x => bind(x).ToStateful>(), project); /// /// Monad bind operation /// /// Monadic bind function /// Projection function /// Intermediate bound value type /// Target bound value type /// `RWST` public RWST SelectMany(Func> bind, Func project) => SelectMany(x => bind(x).ToWritable>(), project); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Operators // public static implicit operator RWST(Pure ma) => Pure(ma.Value); public static implicit operator RWST(Ask ma) => Asks(ma.F); public static implicit operator RWST(IO ma) => LiftIO(ma); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Run the RWST // /// /// Run the monad /// public K Run(R env, W output, S state) => runRWST((env, output, state)); /// /// Run the monad /// public K Run(R env, S state) => runRWST((env, W.Empty, state)); } ================================================ FILE: LanguageExt.Core/Monads/State and Environment Monads/RWS/RWST/Trait/RWST.TraitImpl.cs ================================================ using System; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; /// /// Reader / Write / State monad-transformer trait implementations /// /// Reader environment type /// Writer output type /// State type /// Lifted monad type public class RWST : MonadT, M>, Readable, R>, Writable, W>, Stateful, S> where M : Monad where W : Monoid { static K, B> Monad>.Bind( K, A> ma, Func, B>> f) => new RWST( input => ma.As().runRWST(input) .Bind(x => f(x.Value).As().runRWST((input.Env, x.Output, x.State)))); static K, B> Monad>.Recur(A value, Func, Next>> f) => new RWST( input => { return M.Recur(f(value).As().runRWST(input), go); K, W, S)>, (B, W, S)>> go(K Next, W Output, S State)> ma) => ma >> (n => n.Next switch { { IsDone: true, Done: var x } => M.Pure(Next.Done, W, S)>, (B, W, S)>((x, n.Output, n.State))), { IsLoop: true, Loop: var x } => M.Pure(Next.Loop, W, S)>, (B, W, S)>(f(x).As().Run(input.Env, n.Output, n.State))), _ => throw new NotSupportedException() }); }); static K, B> Functor>.Map( Func f, K, A> ma) => new RWST( input => ma.As().runRWST(input) .Map(x => (f(x.Value), x.Output, x.State))); static K, A> Applicative>.Pure(A value) => new RWST(input => M.Pure((value, input.Output, input.State))); static K, B> Applicative>.Apply( K, Func> mf, K, A> ma) => new RWST( input => from rf in mf.As().runRWST(input) from ra in ma.As().runRWST((input.Env, rf.Output, rf.State)) select (rf.Value(ra.Value), ra.Output, ra.State)); static K, B> Applicative>.Apply( K, Func> mf, Memo, A> ma) => new RWST( input => from rf in mf.As().runRWST(input) from ra in ma.Value.As().runRWST((input.Env, rf.Output, rf.State)) select (rf.Value(ra.Value), ra.Output, ra.State)); static K, A> MonadT, M>.Lift(K ma) => new RWST(input => ma.Map(value => (value, input.Output, input.State))); static K, A> Readable, R>.Asks(Func f) => new RWST(input => M.Pure((f(input.Env), input.Output, input.State))); static K, A> Readable, R>.Local( Func f, K, A> ma) => new RWST( input => ma.As().runRWST((f(input.Env), input.Output, input.State))); static K, Unit> Writable, W>.Tell(W item) => new RWST( input => M.Pure((unit, input.Output.Combine(item), input.State))); static K, (A Value, W Output)> Writable, W>.Listen( K, A> ma) => new RWST( input => ma.As() .runRWST(input) .Map(output => ((output.Value, output.Output), output.Output, output.State))); static K, A> Writable, W>.Pass( K, (A Value, Func Function)> action) => new RWST( input => action.As() .runRWST(input) .Map(output => (output.Value.Value, output.Output + output.Value.Function(output.Output), output.State))); static K, Unit> Stateful, S>.Put(S value) => new RWST(input => M.Pure((unit, input.Output, value))); static K, Unit> Stateful, S>.Modify(Func f) => new RWST(input => M.Pure((unit, input.Output, f(input.State)))); static K, A> Stateful, S>.Gets(Func f) => new RWST(input => M.Pure((f(input.State), input.Output, input.State))); static K, A> Maybe.MonadIO>.LiftIOMaybe(K ma) => new RWST(input => M.LiftIOMaybe(ma).Map(a => (a, input.Output, input.State))); static K, A> Maybe.MonadIO>.LiftIOMaybe(IO ma) => new RWST(input => M.LiftIOMaybe(ma).Map(a => (a, input.Output, input.State))); } ================================================ FILE: LanguageExt.Core/Monads/State and Environment Monads/Reader/Ask.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; /// /// Reader ask /// /// /// This is a convenience type that is created by the Prelude `ask` function. It avoids /// the need for lots of generic parameters when used in `ReaderT` and `Reader` based /// monads. /// /// Mapping from the environment /// Reader environment type /// Type to map to public readonly record struct Ask(Func F) { /// /// Use a `Readable` trait to convert to an `M〈A〉` /// public K ToReadable() where M : Readable => Readable.asks(F); /// /// Convert to a `Reader` /// public Reader ToReader() => ToReadable>().As(); /// /// Convert to a `ReaderT` /// public ReaderT ToReaderT() where M : Monad => ToReadable>().As(); /// /// Convert to a `RWS` /// //public RWS ToRWS() => // ToReadable>().As(); /// /// Convert to a `RWST` /// public RWST ToRWST() where W : Monoid where M : Monad, Choice => ToReadable>().As(); /// /// Monadic bind with any `Reader` /// public K SelectMany(Func> bind, Func project) where M : Monad, Readable => M.Bind(M.Asks(F), x => M.Map(y => project(x, y), bind(x))); /// /// Monadic bind with `ReaderT` /// public ReaderT SelectMany(Func> bind, Func project) where M : Monad => ToReaderT().SelectMany(bind, project); /// /// Monadic bind with `ReaderT` /// public ReaderT SelectMany(Func, B>> bind, Func project) where M : Monad => ToReaderT().SelectMany(bind, project); /// /// Monadic bind with `Reader` /// public Reader SelectMany(Func> bind, Func project) => ToReader().SelectMany(bind, project).As(); /// /// Monadic bind with `Reader` /// public Reader SelectMany(Func, B>> bind, Func project) => ToReader().SelectMany(bind, project).As(); /// /// Monad bind operation /// /// Monadic bind function /// Projection function /// Intermediate bound value type /// Target bound value type /// `ReaderT` public RWST SelectMany( Func> bind, Func project) where W : Monoid where M : Monad, Choice => RWST.Asks(F).SelectMany(bind, project); /// /// Monad bind operation /// /// Monadic bind function /// Projection function /// Intermediate bound value type /// Target bound value type /// `ReaderT` public RWST SelectMany( Func, B>> bind, Func project) where W : Monoid where M : Monad, Choice => RWST.Asks(F).SelectMany(bind, project); } public static class AskExtensions { /// /// Monadic bind with any `Reader` /// public static K SelectMany( this K ma, Func> bind, Func project) where M : Monad, Readable => M.Bind(ma, a => M.Map(b => project(a, b), M.Asks(bind(a).F))); } ================================================ FILE: LanguageExt.Core/Monads/State and Environment Monads/Reader/Reader/Extensions/Reader.Extensions.cs ================================================ using static LanguageExt.Prelude; using System.Diagnostics.Contracts; using LanguageExt.Traits; namespace LanguageExt; /// /// Reader monad extensions /// public static partial class ReaderExtensions { public static Reader As(this K, A> ma) => (Reader)ma; /// /// Run the reader monad /// /// Input environment public static A Run(this K, A> ma, Env env) => ((Reader)ma).runReader(env); /// /// Monadic join /// [Pure] public static Reader Flatten(this Reader> mma) => mma.Bind(identity); } ================================================ FILE: LanguageExt.Core/Monads/State and Environment Monads/Reader/Reader/Operators/Reader.Operators.Applicative.cs ================================================ using System; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; public static partial class ReaderExtensions { extension(K, A> self) { /// /// Applicative sequence operator /// public static Reader operator >>> (K, A> ma, K, B> mb) => +ma.Action(mb); /// /// Applicative apply operator /// public static Reader operator * (K, Func> mf, K, A> ma) => +mf.Apply(ma); /// /// Applicative apply operator /// public static Reader operator * (K, A> ma, K, Func> mf) => +mf.Apply(ma); } extension(K, A> self) { /// /// Applicative apply operator /// public static Reader> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static Reader> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) { /// /// Applicative apply operator /// public static Reader>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static Reader>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) { /// /// Applicative apply operator /// public static Reader>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static Reader>>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) { /// /// Applicative apply operator /// public static Reader>>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static Reader>>>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) { /// /// Applicative apply operator /// public static Reader>>>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static Reader>>>>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) { /// /// Applicative apply operator /// public static Reader>>>>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static Reader>>>>>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) { /// /// Applicative apply operator /// public static Reader>>>>>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static Reader>>>>>>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) { /// /// Applicative apply operator /// public static Reader>>>>>>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static Reader>>>>>>>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) { /// /// Applicative apply operator /// public static Reader>>>>>>>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static Reader>>>>>>>>> operator *( K, A> ma, K, Func> mf) => curry * mf * ma; } } ================================================ FILE: LanguageExt.Core/Monads/State and Environment Monads/Reader/Reader/Operators/Reader.Operators.Functor.cs ================================================ using System; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; public static partial class ReaderExtensions { extension(K, A> _) { /// /// Functor map operator /// public static Reader operator *(Func f, K, A> ma) => +ma.Map(f); /// /// Functor map operator /// public static Reader operator *(K, A> ma, Func f) => +ma.Map(f); } extension(K, A> _) { /// /// Functor map operator /// public static Reader> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static Reader> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) { /// /// Functor map operator /// public static Reader>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static Reader>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) { /// /// Functor map operator /// public static Reader>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static Reader>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) { /// /// Functor map operator /// public static Reader>>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static Reader>>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) { /// /// Functor map operator /// public static Reader>>>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static Reader>>>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) { /// /// Functor map operator /// public static Reader>>>>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static Reader>>>>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) { /// /// Functor map operator /// public static Reader>>>>>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static Reader>>>>>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) { /// /// Functor map operator /// public static Reader>>>>>>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static Reader>>>>>>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) { /// /// Functor map operator /// public static Reader>>>>>>>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static Reader>>>>>>>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } } ================================================ FILE: LanguageExt.Core/Monads/State and Environment Monads/Reader/Reader/Operators/Reader.Operators.Monad.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public static partial class ReaderExtensions { extension(K, A> self) { /// /// Monad bind operator /// /// Monad to bind /// Binding function /// Mapped monad public static Reader operator >> (K, A> ma, Func, B>> f) => +ma.Bind(f); /// /// Sequentially compose two actions, discarding any value produced by the first, like sequencing operators (such /// as the semicolon) in C#. /// /// First action to run /// Second action to run /// Result of the second action public static Reader operator >> (K, A> lhs, K, B> rhs) => lhs >> (_ => rhs); } extension(K, A> self) { /// /// Sequentially compose two actions. The second action is a unit-returning action, so the result of the /// first action is propagated. /// /// First action to run /// Second action to run /// Result of the first action public static Reader operator >> (K, A> lhs, K, Unit> rhs) => lhs >> (x => (_ => x) * rhs); } } ================================================ FILE: LanguageExt.Core/Monads/State and Environment Monads/Reader/Reader/Operators/Reader.Operators.cs ================================================ using LanguageExt.Traits; namespace LanguageExt; public static partial class ReaderExtensions { extension(K, A> _) { /// /// Downcast operator /// public static Reader operator +(K, A> ma) => (Reader)ma; /// /// Downcast operator /// public static Reader operator >> (K, A> ma, Lower lower) => +ma; } } ================================================ FILE: LanguageExt.Core/Monads/State and Environment Monads/Reader/Reader/Reader.Module.cs ================================================ using System; namespace LanguageExt; public partial class Reader { public static Reader pure(A value) => Reader.Pure(value); public static Reader ask() => Reader.Asks(Prelude.identity); public static Reader asks(Func f) => Reader.Asks(f); public static Reader asksM(Func> f) => Reader.AsksM(f); public static Reader local(Func f, Reader ma) => ma.As().Local(f); } ================================================ FILE: LanguageExt.Core/Monads/State and Environment Monads/Reader/Reader/Reader.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; /// /// `Reader` monad transformer, which adds a static environment to a given monad. /// /// Reader environment type /// Given monad trait /// Bound value type public record Reader(Func runReader) : K, A> { /// /// Lift a pure value into the monad-transformer /// /// Value to lift /// `Reader` public static Reader Pure(A value) => new(_ => value); /// /// Extracts the environment value and maps it to the bound value /// /// Environment mapping function /// `Reader` public static Reader Asks(Func f) => new(f); /// /// Extracts the environment value and maps it to the bound value /// /// Environment mapping function /// `Reader` public static Reader AsksM(Func> f) => Reader>.Asks(f).Flatten(); /// /// Lifts a given monad into the transformer /// /// Monad to lift /// `Reader` public static Reader Lift(Pure monad) => Pure(monad.Value); /// /// Lifts a unit function into the transformer /// /// Function to lift /// `Reader` public static Reader Lift(Func f) => new (_ => f()); /// /// Maps the Reader's environment value /// /// Mapping function /// `Reader` public Reader With(Func f) => new (e => runReader(f(e))); /// /// Maps the Reader's environment value /// /// Mapping function /// `Reader` public Reader Local(Func f) => new (e => runReader(f(e))); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Map // /// /// Maps the bound value /// /// Mapping function /// Target bound value type /// `Reader` public Reader Map(Func f) => new(e => f(runReader(e))); /// /// Maps the bound value /// /// Mapping transducer /// Target bound value type /// `Reader` public Reader Select(Func f) => new(e => f(runReader(e))); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Bind // /// /// Monad bind operation /// /// Mapping function /// Target bound value type /// `Reader` public Reader Bind(Func, B>> f) => Bind(x => f(x).As()); /// /// Monad bind operation /// /// Mapping function /// Target bound value type /// `Reader` public Reader Bind(Func> f) => new(e => f(runReader(e)).runReader(e)); /// /// Monad bind operation /// /// Mapping function /// Target bound value type /// `Reader` public Reader Bind(Func> f) => Bind(x => (Reader)f(x)); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // SelectMany // /// /// Monad bind operation /// /// Monadic bind function /// Projection function /// Intermediate bound value type /// Target bound value type /// `Reader` public Reader SelectMany(Func, B>> bind, Func project) => SelectMany(x => bind(x).As(), project); /// /// Monad bind operation /// /// Monadic bind function /// Projection function /// Intermediate bound value type /// Target bound value type /// `Reader` public Reader SelectMany(Func> bind, Func project) => Bind(x => bind(x).Map(y => project(x, y))); /// /// Monad bind operation /// /// Monadic bind function /// Projection function /// Intermediate bound value type /// Target bound value type /// `Reader` public Reader SelectMany(Func> bind, Func project) => Map(x => project(x, bind(x).Value)); /// /// Monad bind operation /// /// Monadic bind function /// Projection function /// Intermediate bound value type /// Target bound value type /// `Reader` public Reader SelectMany(Func> bind, Func project) => SelectMany(x => bind(x).ToReader(), project); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Operators // public static implicit operator Reader(Pure ma) => Pure(ma.Value); public static implicit operator Reader(Ask ma) => Asks(ma.F); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Run the reader // /// /// Run the reader monad /// /// Input environment /// Computed value public A Run(Env env) => runReader(env); } ================================================ FILE: LanguageExt.Core/Monads/State and Environment Monads/Reader/Reader/Trait/Reader.TraitImpl.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; /// /// Trait implementation for `Reader` /// /// Reader environment type public partial class Reader : Monad>, Readable, Env> { static K, B> Monad>.Bind(K, A> ma, Func, B>> f) => ma.As().Bind(f); static K, B> Monad>.Recur(A value, Func, Next>> f) => new Reader(env => { while (true) { var mr = f(value).Run(env); if (mr.IsDone) return mr.Done; value = mr.Loop; } }); static K, B> Functor>.Map(Func f, K, A> ma) => ma.As().Map(f); static K, A> Applicative>.Pure(A value) => Reader.Pure(value); static K, B> Applicative>.Apply(K, Func> mf, K, A> ma) => mf.As().Bind(x => ma.As().Map(x)); static K, B> Applicative>.Apply(K, Func> mf, Memo, A> ma) => mf.As().Bind(x => ma.Value.As().Map(x)); static K, Env> Readable, Env>.Ask => Reader.Asks(Prelude.identity); static K, A> Readable, Env>.Asks(Func f) => Reader.Asks(f); static K, A> Readable, Env>.Local(Func f, K, A> ma) => ma.As().Local(f); } ================================================ FILE: LanguageExt.Core/Monads/State and Environment Monads/Reader/ReaderT/Extensions/ReaderT.Extensions.cs ================================================ using System; using static LanguageExt.Prelude; using System.Diagnostics.Contracts; using LanguageExt.Traits; namespace LanguageExt; /// /// Reader monad extensions /// public static partial class ReaderTExtensions { // public static Reader As(this K, A> ma) => // (Reader)ma; public static ReaderT As(this K, A> ma) where M : Monad => (ReaderT)ma; /// /// Run the reader monad /// /// Input environment /// Bound monad public static K Run(this K, A> ma, Env env) where M : Monad => ((ReaderT)ma).runReader(env); /// /// Monadic join /// [Pure] public static ReaderT Flatten(this ReaderT> mma) where M : Monad => mma.Bind(identity); /// /// Monadic join /// [Pure] public static ReaderT Flatten(this ReaderT, A>> mma) where M : Monad => mma.Bind(identity); /// /// Monad bind operation /// /// Monadic bind function /// Projection function /// Intermediate bound value type /// Target bound value type /// `ReaderT` public static ReaderT SelectMany( this K ma, Func, B>> bind, Func project) where M : Monad => ReaderT.Lift(ma).SelectMany(bind, project); /// /// Monad bind operation /// /// Monadic bind function /// Projection function /// Intermediate bound value type /// Target bound value type /// `ReaderT` public static ReaderT SelectMany( this K ma, Func> bind, Func project) where M : Monad => ReaderT.Lift(ma).SelectMany(bind, project); } ================================================ FILE: LanguageExt.Core/Monads/State and Environment Monads/Reader/ReaderT/Operators/ReaderT.Operators.Applicative.cs ================================================ using System; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; public static partial class ReaderTExtensions { extension(K, A> self) where M : Monad { /// /// Applicative sequence operator /// public static ReaderT operator >>> (K, A> ma, K, B> mb) => +ma.Action(mb); /// /// Applicative apply operator /// public static ReaderT operator * (K, Func> mf, K, A> ma) => +mf.Apply(ma); /// /// Applicative apply operator /// public static ReaderT operator * (K, A> ma, K, Func> mf) => +mf.Apply(ma); } extension(K, A> self) where M : Monad { /// /// Applicative apply operator /// public static ReaderT> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static ReaderT> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) where M : Monad { /// /// Applicative apply operator /// public static ReaderT>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static ReaderT>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) where M : Monad { /// /// Applicative apply operator /// public static ReaderT>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static ReaderT>>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) where M : Monad { /// /// Applicative apply operator /// public static ReaderT>>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static ReaderT>>>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) where M : Monad { /// /// Applicative apply operator /// public static ReaderT>>>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static ReaderT>>>>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) where M : Monad { /// /// Applicative apply operator /// public static ReaderT>>>>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static ReaderT>>>>>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) where M : Monad { /// /// Applicative apply operator /// public static ReaderT>>>>>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static ReaderT>>>>>>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) where M : Monad { /// /// Applicative apply operator /// public static ReaderT>>>>>>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static ReaderT>>>>>>>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) where M : Monad { /// /// Applicative apply operator /// public static ReaderT>>>>>>>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static ReaderT>>>>>>>>> operator *( K, A> ma, K, Func> mf) => curry * mf * ma; } } ================================================ FILE: LanguageExt.Core/Monads/State and Environment Monads/Reader/ReaderT/Operators/ReaderT.Operators.Choice.cs ================================================ using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt; public static partial class ReaderTExtensions { extension(K, A> self) where M : Monad, Choice { /// /// Choice operator. Usually means if the first argument succeeds, return it, otherwise return the second /// argument. /// /// Left hand side operand /// Right hand side operand /// public static ReaderT operator |(K, A> lhs, K, A> rhs) => new (env => lhs.As().runReader(env) | rhs.As().runReader(env)); /// /// Choice operator. Usually means if the first argument succeeds, return it, otherwise return the second /// argument. /// /// Left hand side operand /// Right hand side operand /// public static ReaderT operator |(K, A> lhs, Pure rhs) => new (env => lhs.As().runReader(env) | rhs); } } ================================================ FILE: LanguageExt.Core/Monads/State and Environment Monads/Reader/ReaderT/Operators/ReaderT.Operators.Fallible.cs ================================================ using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt; public static partial class ReaderTExtensions { extension(K, A> self) where M : Monad, Fallible { public static ReaderT operator |(K, A> lhs, CatchM rhs) => new(env => lhs.As().runReader(env) | rhs); public static ReaderT operator |(K, A> lhs, Fail rhs) => new(env => lhs.As().runReader(env) | rhs); } } ================================================ FILE: LanguageExt.Core/Monads/State and Environment Monads/Reader/ReaderT/Operators/ReaderT.Operators.Final.cs ================================================ namespace LanguageExt.Traits; public static class ReaderTExtensions { extension(K, A> _) where M : Monad, Final { /// /// Run a `finally` operation after the main operation regardless of whether it succeeds or not. /// /// Primary operation /// Finally operation /// Result of primary operation public static ReaderT operator |(K, A> lhs, Finally rhs) => new(env => lhs.As().runReader(env) | rhs); } } ================================================ FILE: LanguageExt.Core/Monads/State and Environment Monads/Reader/ReaderT/Operators/ReaderT.Operators.Functor.cs ================================================ using System; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; public static partial class ReaderTExtensions { extension(K, A> _) where M : Monad { /// /// Functor map operator /// public static ReaderT operator *(Func f, K, A> ma) => +ma.Map(f); /// /// Functor map operator /// public static ReaderT operator *(K, A> ma, Func f) => +ma.Map(f); } extension(K, A> _) where M : Monad { /// /// Functor map operator /// public static ReaderT> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static ReaderT> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) where M : Monad { /// /// Functor map operator /// public static ReaderT>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static ReaderT>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) where M : Monad { /// /// Functor map operator /// public static ReaderT>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static ReaderT>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) where M : Monad { /// /// Functor map operator /// public static ReaderT>>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static ReaderT>>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) where M : Monad { /// /// Functor map operator /// public static ReaderT>>>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static ReaderT>>>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) where M : Monad { /// /// Functor map operator /// public static ReaderT>>>>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static ReaderT>>>>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) where M : Monad { /// /// Functor map operator /// public static ReaderT>>>>>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static ReaderT>>>>>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) where M : Monad { /// /// Functor map operator /// public static ReaderT>>>>>>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static ReaderT>>>>>>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) where M : Monad { /// /// Functor map operator /// public static ReaderT>>>>>>>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static ReaderT>>>>>>>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } } ================================================ FILE: LanguageExt.Core/Monads/State and Environment Monads/Reader/ReaderT/Operators/ReaderT.Operators.Monad.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public static partial class ReaderTExtensions { extension(K, A> self) where M : Monad { /// /// Monad bind operator /// /// Monad to bind /// Binding function /// Mapped monad public static ReaderT operator >> (K, A> ma, Func, B>> f) => +ma.Bind(f); /// /// Sequentially compose two actions, discarding any value produced by the first, like sequencing operators (such /// as the semicolon) in C#. /// /// First action to run /// Second action to run /// Result of the second action public static ReaderT operator >> (K, A> lhs, K, B> rhs) => lhs >> (_ => rhs); } extension(K, A> self) where M : Monad { /// /// Sequentially compose two actions. The second action is a unit-returning action, so the result of the /// first action is propagated. /// /// First action to run /// Second action to run /// Result of the first action public static ReaderT operator >> (K, A> lhs, K, Unit> rhs) => lhs >> (x => (_ => x) * rhs); } } ================================================ FILE: LanguageExt.Core/Monads/State and Environment Monads/Reader/ReaderT/Operators/ReaderT.Operators.SemigroupK.cs ================================================ using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt; public static partial class ReaderTExtensions { extension(K, A> self) where M : Monad, SemigroupK { /// /// Semigroup combine operator: an associative binary operation. /// /// Left-hand side operand /// Right-hand side operand /// public static ReaderT operator +(K, A> lhs, K, A> rhs) => new(env => lhs.As().runReader(env) + rhs.As().runReader(env)); //+lhs.Combine(rhs); /// /// Semigroup combine operator: an associative binary operation. /// /// Left-hand side operand /// Right-hand side operand /// public static ReaderT operator +(K, A> lhs, Pure rhs) => new(env => lhs.As().runReader(env) + M.Pure(rhs.Value)); } extension(K, A> self) where M : Monad, SemigroupK, Fallible { /// /// Semigroup combine operator: an associative binary operation. /// /// Left-hand side operand /// Right-hand side operand /// public static ReaderT operator +(K, A> lhs, Fail rhs) => new(env => lhs.As().runReader(env) + M.Fail(rhs.Value)); } } ================================================ FILE: LanguageExt.Core/Monads/State and Environment Monads/Reader/ReaderT/Operators/ReaderT.Operators.cs ================================================ using LanguageExt.Traits; namespace LanguageExt; public static partial class ReaderTExtensions { extension(K, A> _) where M : Monad { /// /// Downcast operator /// public static ReaderT operator +(K, A> ma) => (ReaderT)ma; /// /// Downcast operator /// public static ReaderT operator >> (K, A> ma, Lower lower) => +ma; } } ================================================ FILE: LanguageExt.Core/Monads/State and Environment Monads/Reader/ReaderT/Prelude/ReaderT.Prelude.mapapply.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public static partial class Prelude { /// /// Functor map operation /// /// /// Unwraps the value within the functor, passes it to the map function `f` provided, and /// then takes the mapped value and wraps it back up into a new functor. /// /// Functor to map /// Mapping function /// Mapped functor public static ReaderT map(Func f, K, A> ma) where M : Monad, Alternative => Functor.map(f, ma).As(); /// /// Applicative action: runs the first applicative, ignores the result, and returns the second applicative /// public static ReaderT action(K, A> ma, K, B> mb) where M : Monad, Alternative => Applicative.action(ma, mb).As(); /// /// Applicative functor apply operation /// /// /// Unwraps the value within the `ma` applicative-functor, passes it to the unwrapped function(s) within `mf`, and /// then takes the resulting value and wraps it back up into a new applicative-functor. /// /// Value(s) applicative functor /// Mapping function(s) /// Mapped applicative functor public static ReaderT apply(K, Func> mf, K, A> ma) where M : Monad, Alternative => Applicative.apply(mf, ma).As(); } ================================================ FILE: LanguageExt.Core/Monads/State and Environment Monads/Reader/ReaderT/ReaderT.Module.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; /// /// `MonadReaderT` trait implementation for `ReaderT` /// /// Reader environment type /// Given monad trait public class ReaderT { public static ReaderT lift(K ma) where M : Monad => ReaderT.Lift(ma); } public partial class ReaderT { public static ReaderT pure(A value) => ReaderT.Pure(value); /// /// Lifts a given monad into the transformer /// /// Monad to lift /// `ReaderT` public static ReaderT liftIO(IO effect) => ReaderT.LiftIO(effect); } /// /// `MonadReaderT` trait implementation for `ReaderT` /// /// Reader environment type /// Given monad trait public class ReaderT { public static ReaderT pure(A value) where M : Monad => ReaderT.Pure(value); public static ReaderT lift(K ma) where M : Monad => ReaderT.Lift(ma); /// /// Lifts a given monad into the transformer /// /// Monad to lift /// `ReaderT` public static ReaderT liftIO(IO effect) where M : Monad => ReaderT.LiftIO(effect); public static ReaderT ask() where M : Monad => ReaderT.Asks(Prelude.identity); public static ReaderT asks(Func f) where M : Monad => ReaderT.Asks(f); public static ReaderT asksM(Func> f) where M : Monad => ReaderT.AsksM(f); public static ReaderT local(Func f, ReaderT ma) where M : Monad => ma.As().Local(f); public static ReaderT with(Func f, ReaderT ma) where M : Monad => ma.As().With(f); } ================================================ FILE: LanguageExt.Core/Monads/State and Environment Monads/Reader/ReaderT/ReaderT.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; /// /// `ReaderT` monad transformer, which adds a static environment to a given monad. /// /// Reader environment type /// Given monad trait /// Bound value type public record ReaderT(Func> runReader) : K, A> where M : Monad { /// /// Lift a pure value into the monad-transformer /// /// Value to lift /// `ReaderT` public static ReaderT Pure(A value) => Lift(M.Pure(value)); /// /// Extracts the environment value and maps it to the bound value /// /// Environment mapping function /// `ReaderT` public static ReaderT Asks(Func f) => new(env => M.Pure(f(env))); /// /// Extracts the environment value and maps it to the bound value /// /// Environment mapping function /// `ReaderT` public static ReaderT AsksM(Func> f) => new(f); /// /// Lifts a given monad into the transformer /// /// Monad to lift /// `ReaderT` public static ReaderT Lift(Pure monad) => Pure(monad.Value); /// /// Lifts a given monad into the transformer /// /// Monad to lift /// `ReaderT` public static ReaderT Lift(K monad) => new(_ => monad); /// /// Lifts a unit function into the transformer /// /// Function to lift /// `ReaderT` public static ReaderT Lift(Func f) => new (_ => M.Pure(f())); /// /// Lifts a unit function into the transformer /// /// Function to lift /// `ReaderT` public static ReaderT LiftIO(IO ma) => new (_ => M.LiftIOMaybe(ma)); /// /// Maps the Reader's environment value /// /// Mapping function /// `ReaderT` public ReaderT With(Func f) => new (env1 => runReader(f(env1))); /// /// Maps the Reader's environment value /// /// Mapping function /// `ReaderT` public ReaderT Local(Func f) => new (env1 => runReader(f(env1))); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Map // /// /// Maps the given monad /// /// Mapping function /// Trait of the monad to map to /// `ReaderT` public ReaderT MapM(Func, K> f) where M1 : Monad, Alternative => new (env => f(runReader(env))); /// /// Maps the bound value /// /// Mapping function /// Target bound value type /// `ReaderT` public ReaderT Map(Func f) => new(env => M.Map(f, runReader(env))); /// /// Maps the bound value /// /// Mapping transducer /// Target bound value type /// `ReaderT` public ReaderT Select(Func f) => new(env => M.Map(f, runReader(env))); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Bind // /// /// Monad bind operation /// /// Mapping function /// Target bound value type /// `ReaderT` public ReaderT Bind(Func, B>> f) => Bind(x => f(x).As()); /// /// Monad bind operation /// /// Mapping function /// Target bound value type /// `ReaderT` public ReaderT Bind(Func> f) => new(env => M.Bind(runReader(env), x => f(x).runReader(env))); /// /// Monad bind operation /// /// Mapping function /// Target bound value type /// `ReaderT` public ReaderT Bind(Func> f) => Bind(x => (ReaderT)f(x)); /// /// Monad bind operation /// /// Mapping function /// Target bound value type /// `ReaderT` public ReaderT Bind(Func> f) => Bind(x => ReaderT.LiftIO(f(x))); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // SelectMany // /// /// Monad bind operation /// /// Monadic bind function /// Projection function /// Intermediate bound value type /// Target bound value type /// `ReaderT` public ReaderT SelectMany(Func, B>> bind, Func project) => SelectMany(x => bind(x).As(), project); /// /// Monad bind operation /// /// Monadic bind function /// Projection function /// Intermediate bound value type /// Target bound value type /// `ReaderT` public ReaderT SelectMany(Func> bind, Func project) => new(env => M.Bind(runReader(env), x => M.Map(y => project(x, y), bind(x).runReader(env)))); /// /// Monad bind operation /// /// Monadic bind function /// Projection function /// Intermediate bound value type /// Target bound value type /// `ReaderT` public ReaderT SelectMany(Func> bind, Func project) => new(env => M.Bind(runReader(env), x => M.Map(y => project(x, y), bind(x)))); /// /// Monad bind operation /// /// Monadic bind function /// Projection function /// Intermediate bound value type /// Target bound value type /// `ReaderT` public ReaderT SelectMany(Func> bind, Func project) => Map(x => project(x, bind(x).Value)); /// /// Monad bind operation /// /// Monadic bind function /// Projection function /// Intermediate bound value type /// Target bound value type /// `ReaderT` public ReaderT SelectMany(Func> bind, Func project) => SelectMany(x => bind(x).ToReaderT(), project); /// /// Monad bind operation /// /// Monadic bind function /// Projection function /// Intermediate bound value type /// Target bound value type /// `ReaderT` public ReaderT SelectMany(Func> bind, Func project) => Bind(x => bind(x).Map(y => project(x, y))); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Operators // public static implicit operator ReaderT(Pure ma) => Pure(ma.Value); public static implicit operator ReaderT(Ask ma) => Asks(ma.F); public static implicit operator ReaderT(IO ma) => LiftIO(ma); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Run the reader // /// /// Run the reader monad /// /// Input environment /// Bound monad public K Run(Env env) => runReader(env); } ================================================ FILE: LanguageExt.Core/Monads/State and Environment Monads/Reader/ReaderT/Trait/ReaderT.TraitImpl.cs ================================================ using System; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; /// /// `MonadReaderT` trait implementation for `ReaderT` /// /// Reader environment type /// Given monad trait public partial class ReaderT : MonadT, M>, Readable, Env>, MonadUnliftIO> where M : Monad { static K, B> Monad>.Bind( K, A> ma, Func, B>> f) => new ReaderT(env => ma.As().runReader(env).Bind(a => f(a).As().runReader(env))); static K, B> Monad>. Recur(A value, Func, Next>> f) => new ReaderT(env => { return M.Recur(f(value).Run(env), go); K>, B>> go(K> ma) => ma >> (n => n switch { { IsDone: true, Done: var x } => M.Pure(Next.Done>, B>(x)), { IsLoop: true, Loop: var x } => M.Pure(Next.Loop>, B>(f(x).Run(env))), _ => throw new NotSupportedException() }); }); static K, B> Functor>.Map(Func f, K, A> ma) => new ReaderT(env => ma.As().runReader(env).Map(f)); static K, A> Applicative>.Pure(A value) => new ReaderT(_ => M.Pure(value)); static K, B> Applicative>.Apply(K, Func> mf, K, A> ma) => new ReaderT(env => mf.As().runReader(env).Apply(ma.As().runReader(env))); static K, B> Applicative>.Apply(K, Func> mf, Memo, A> ma) => new ReaderT(env => mf.As().runReader(env).Apply(ma.Value.As().runReader(env))); static K, A> MonadT, M>.Lift(K ma) => new ReaderT(_ => ma); static K, Env> Readable, Env>.Ask => new ReaderT(M.Pure); static K, A> Readable, Env>.Asks(Func f) => new ReaderT(env => M.Pure(f(env))); static K, A> Readable, Env>.Local(Func f, K, A> ma) => new ReaderT(env => ma.As().runReader(f(env))); static K, A> MonadIO>.LiftIO(IO ma) => new ReaderT(_ => M.LiftIOMaybe(ma)); static K, IO> MonadUnliftIO>.ToIO(K, A> ma) => new ReaderT>(env => M.ToIOMaybe(ma.As().runReader(env))); static K, B> MonadUnliftIO>.MapIO(K, A> ma, Func, IO> f) => new ReaderT(env => M.MapIOMaybe(ma.As().runReader(env), f)); } ================================================ FILE: LanguageExt.Core/Monads/State and Environment Monads/State/PutGet.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; /// /// State put /// /// /// This is a convenience type that is created by the Prelude `put` function. It avoids /// the need for lots of generic parameters when used in `StateT` and `State` based /// monads. /// /// Mapping from the environment /// State type public readonly record struct Put(S Value) { /// /// Convert to a `Stateful` /// public K ToStateful() where M : Stateful => Stateful.put(Value); /// /// Convert to a `StateT` /// public StateT ToStateT() where M : Monad => Stateful.put, S>(Value).As(); /// /// Convert to a `State` /// public State ToState() => Stateful.put, S>(Value).As(); /// /// Monadic bind /// public StateT SelectMany(Func> bind, Func project) where M : Monad => ToStateT().SelectMany(bind, project); /// /// Monadic bind /// public StateT SelectMany(Func, B>> bind, Func project) where M : Monad => ToStateT().SelectMany(bind, project); /// /// Monadic bind /// public State SelectMany(Func> bind, Func project) => ToState().SelectMany(bind, project); /// /// Monadic bind /// public State SelectMany(Func, B>> bind, Func project) => ToState().SelectMany(bind, project); /// /// Monadic bind /// public RWST SelectMany(Func> bind, Func project) where W : Monoid where M : Stateful, Monad, Choice => ToStateful>().SelectMany(bind, project).As(); /// /// Monadic bind /// public RWST SelectMany(Func, B>> bind, Func project) where W : Monoid where M : Stateful, Monad, Choice => ToStateful>().SelectMany(bind, project).As(); } /// /// State modify /// /// /// This is a convenience type that is created by the Prelude `modify` function. It avoids /// the need for lots of generic parameters when used in `StateT` and `State` based /// monads. /// /// Mapping from the environment /// State type public readonly record struct Modify(Func f) { /// /// Convert with `Stateful` /// public K ToStateful() where M : Stateful => Stateful.modify(f); /// /// Convert to a `StateT` /// public StateT ToStateT() where M : Monad => StateT.Modify(f); /// /// Convert to a `State` /// public State ToState() => State.Modify(f); /// /// Monadic bind /// public StateT SelectMany(Func> bind, Func project) where M : Monad => ToStateT().SelectMany(bind, project); /// /// Monadic bind /// public StateT SelectMany(Func, B>> bind, Func project) where M : Monad => ToStateT().SelectMany(bind, project); /// /// Monadic bind /// public State SelectMany(Func> bind, Func project) => ToState().SelectMany(bind, project); /// /// Monadic bind /// public State SelectMany(Func, B>> bind, Func project) => ToState().SelectMany(bind, project); /// /// Monadic bind /// public RWST SelectMany(Func> bind, Func project) where W : Monoid where M : Stateful, Monad, Choice => ToStateful>().SelectMany(bind, project).As(); /// /// Monadic bind /// public RWST SelectMany(Func, B>> bind, Func project) where W : Monoid where M : Stateful, Monad, Choice => ToStateful>().SelectMany(bind, project).As(); } /// /// State modify /// /// /// This is a convenience type that is created by the Prelude `modify` function. It avoids /// the need for lots of generic parameters when used in `StateT` and `State` based /// monads. /// /// Mapping from the environment /// State type public readonly record struct Gets(Func f) { /// /// Convert with `Stateful` /// public K ToStateful() where M : Stateful => Stateful.gets(f); /// /// Convert ot a `StateT` /// public StateT ToStateT() where M : Monad => StateT.Gets(f); /// /// Convert ot a `State` /// public State ToState() => State.Gets(f); /// /// Monadic bind /// public StateT SelectMany(Func> bind, Func project) where M : Monad => ToStateT().SelectMany(bind, project); /// /// Monadic bind /// public StateT SelectMany(Func, B>> bind, Func project) where M : Monad => ToStateT().SelectMany(bind, project); /// /// Monadic bind /// public State SelectMany(Func> bind, Func project) => ToState().SelectMany(bind, project); /// /// Monadic bind /// public State SelectMany(Func, B>> bind, Func project) => ToState().SelectMany(bind, project); /// /// Monadic bind /// public RWST SelectMany(Func> bind, Func project) where W : Monoid where M : Stateful, Monad, Choice => ToStateful>().SelectMany(bind, project).As(); /// /// Monadic bind /// public RWST SelectMany(Func, B>> bind, Func project) where W : Monoid where M : Stateful, Monad, Choice => ToStateful>().SelectMany(bind, project).As(); } ================================================ FILE: LanguageExt.Core/Monads/State and Environment Monads/State/State/Extensions/State.Extensions.cs ================================================ using System.Diagnostics.Contracts; using LanguageExt.Traits; namespace LanguageExt; /// /// State monad extensions /// public static partial class StateExtensions { public static State As(this K, A> ma) => (State)ma; /// /// Run the state monad /// /// Initial state public static (A Value, S State) Run(this K, A> ma, S state) => ((State)ma).runState(state); /// /// Monadic join /// [Pure] public static State Flatten(this State> mma) => mma.Bind(x => x); /// /// Monadic join /// [Pure] public static State Flatten(this State, A>> mma) => mma.Bind(x => x); } ================================================ FILE: LanguageExt.Core/Monads/State and Environment Monads/State/State/Operators/State.Operators.Applicative.cs ================================================ using System; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; public static partial class StateExtensions { extension(K, A> self) { /// /// Applicative sequence operator /// public static State operator >>> (K, A> ma, K, B> mb) => +ma.Action(mb); /// /// Applicative apply operator /// public static State operator * (K, Func> mf, K, A> ma) => +mf.Apply(ma); /// /// Applicative apply operator /// public static State operator * (K, A> ma, K, Func> mf) => +mf.Apply(ma); } extension(K, A> self) { /// /// Applicative apply operator /// public static State> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static State> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) { /// /// Applicative apply operator /// public static State>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static State>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) { /// /// Applicative apply operator /// public static State>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static State>>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) { /// /// Applicative apply operator /// public static State>>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static State>>>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) { /// /// Applicative apply operator /// public static State>>>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static State>>>>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) { /// /// Applicative apply operator /// public static State>>>>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static State>>>>>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) { /// /// Applicative apply operator /// public static State>>>>>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static State>>>>>>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) { /// /// Applicative apply operator /// public static State>>>>>>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static State>>>>>>>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) { /// /// Applicative apply operator /// public static State>>>>>>>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static State>>>>>>>>> operator *( K, A> ma, K, Func> mf) => curry * mf * ma; } } ================================================ FILE: LanguageExt.Core/Monads/State and Environment Monads/State/State/Operators/State.Operators.Functor.cs ================================================ using System; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; public static partial class StateExtensions { extension(K, A> _) { /// /// Functor map operator /// public static State operator *(Func f, K, A> ma) => +ma.Map(f); /// /// Functor map operator /// public static State operator *(K, A> ma, Func f) => +ma.Map(f); } extension(K, A> _) { /// /// Functor map operator /// public static State> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static State> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) { /// /// Functor map operator /// public static State>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static State>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) { /// /// Functor map operator /// public static State>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static State>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) { /// /// Functor map operator /// public static State>>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static State>>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) { /// /// Functor map operator /// public static State>>>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static State>>>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) { /// /// Functor map operator /// public static State>>>>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static State>>>>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) { /// /// Functor map operator /// public static State>>>>>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static State>>>>>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) { /// /// Functor map operator /// public static State>>>>>>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static State>>>>>>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) { /// /// Functor map operator /// public static State>>>>>>>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static State>>>>>>>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } } ================================================ FILE: LanguageExt.Core/Monads/State and Environment Monads/State/State/Operators/State.Operators.Monad.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public static partial class WriterExtensions { extension(K, A> self) where W : Monoid { /// /// Monad bind operator /// /// Monad to bind /// Binding function /// Mapped monad public static Writer operator >> (K, A> ma, Func, B>> f) => +ma.Bind(f); /// /// Sequentially compose two actions, discarding any value produced by the first, like sequencing operators (such /// as the semicolon) in C#. /// /// First action to run /// Second action to run /// Result of the second action public static Writer operator >> (K, A> lhs, K, B> rhs) => lhs >> (_ => rhs); } extension(K, A> self) where W : Monoid { /// /// Sequentially compose two actions. The second action is a unit-returning action, so the result of the /// first action is propagated. /// /// First action to run /// Second action to run /// Result of the first action public static Writer operator >> (K, A> lhs, K, Unit> rhs) => lhs >> (x => (_ => x) * rhs); } } ================================================ FILE: LanguageExt.Core/Monads/State and Environment Monads/State/State/Operators/State.Operators.cs ================================================ using LanguageExt.Traits; namespace LanguageExt; public static partial class StateExtensions { extension(K, A> _) { /// /// Downcast operator /// public static State operator +(K, A> ma) => (State)ma; /// /// Downcast operator /// public static State operator >> (K, A> ma, Lower lower) => +ma; } } ================================================ FILE: LanguageExt.Core/Monads/State and Environment Monads/State/State/State.Module.cs ================================================ using System; namespace LanguageExt; public partial class State { public static State pure(A value) => State.Pure(value); } public class State { public static State pure(A value) => State.Pure(value); public static State get() => State.Get; public static State gets(Func f) => State.Gets(f); public static State getsM(Func> f) => State.GetsM(f); public static State put(S state) => State.Put(state); public static State modify(Func f) => State.Modify(f); } ================================================ FILE: LanguageExt.Core/Monads/State and Environment Monads/State/State/State.cs ================================================ using System; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; /// /// `State` monad transformer, which adds a modifiable state to a given monad. /// /// State type /// Given monad trait /// Bound value type public record State(Func runState) : K, A> { /// /// Lift a pure value into the monad-transformer /// /// Value to lift /// `State` public static State Pure(A value) => new (s => (value, s)); /// /// Extracts the state value, maps it, and then puts it back into /// the monadic state. /// /// State mapping function /// `State` public static State Modify(Func f) => new (s => (unit, f(s))); /// /// Writes the value into the monadic state /// /// `State` public static State Put(S value) => new (_ => (unit, value)); /// /// Extracts the state value and returns it as the bound value /// /// `State` public static State Get { get; } = new (s => (s, s)); /// /// Extracts the state value and maps it to the bound value /// /// State mapping function /// `State` public static State Gets(Func f) => new (s => (f(s), s)); /// /// Extracts the state value and maps it to the bound value /// /// State mapping function /// `State` public static State GetsM(Func> f) => State>.Gets(f).Flatten(); /// /// Lifts a given monad into the transformer /// /// Monad to lift /// `State` public static State Lift(Pure monad) => Pure(monad.Value); /// /// Lifts a function into the transformer /// /// Function to lift /// `State` public static State Lift(Func f) => new(s => (f(), s)); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Map // /// /// Maps the bound value /// /// Mapping function /// Target bound value type /// `State` public State Map(Func f) => new(s => mapFirst(f, runState(s))); /// /// Maps the bound value /// /// Mapping transducer /// Target bound value type /// `State` public State Select(Func f) => new(s => mapFirst(f, runState(s))); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Bind // /// /// Monad bind operation /// /// Mapping function /// Target bound value type /// `State` public State Bind(Func, B>> f) => Bind(x => f(x).As()); /// /// Monad bind operation /// /// Mapping function /// Target bound value type /// `State` public State Bind(Func> f) => new(s => { var (a, s1) = runState(s); return f(a).runState(s1); }); /// /// Monad bind operation /// /// Mapping function /// Target bound value type /// `State` public State Bind(Func> f) => Bind(x => f(x).ToState()); /// /// Monad bind operation /// /// Mapping function /// Target bound value type /// `State` public State Bind(Func> f) => Bind(x => f(x).ToState()); /// /// Monad bind operation /// /// Mapping function /// Target bound value type /// `State` public State Bind(Func> f) => Bind(x => f(x).ToState()); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // SelectMany // /// /// Monad bind operation /// /// Monadic bind function /// Projection function /// Intermediate bound value type /// Target bound value type /// `State` public State SelectMany(Func, B>> bind, Func project) => SelectMany(x => bind(x).As(), project); /// /// Monad bind operation /// /// Monadic bind function /// Projection function /// Intermediate bound value type /// Target bound value type /// `State` public State SelectMany(Func> bind, Func project) => Bind(x => bind(x).Map(y => project(x, y))); /// /// Monad bind operation /// /// Monadic bind function /// Projection function /// Intermediate bound value type /// Target bound value type /// `State` public State SelectMany(Func> bind, Func project) => Map(x => project(x, bind(x).Value)); /// /// Monad bind operation /// /// Monadic bind function /// Projection function /// Intermediate bound value type /// Target bound value type /// `State` public State SelectMany(Func> bind, Func project) => SelectMany(x => bind(x).ToState(), project); /// /// Monad bind operation /// /// Monadic bind function /// Projection function /// Intermediate bound value type /// Target bound value type /// `State` public State SelectMany(Func> bind, Func project) => SelectMany(x => bind(x).ToState(), project); /// /// Monad bind operation /// /// Monadic bind function /// Projection function /// Intermediate bound value type /// Target bound value type /// `State` public State SelectMany(Func> bind, Func project) => SelectMany(x => bind(x).ToState(), project); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Operators // public static implicit operator State(Pure ma) => Pure(ma.Value); public static State operator >> (State lhs, State rhs) => lhs.Bind(_ => rhs); public static State operator >> (State lhs, K, A> rhs) => lhs.Bind(_ => rhs); /// /// Sequentially compose two actions. The second action is a unit returning action, so the result of the /// first action is propagated. /// /// First action to run /// Second action to run /// Result of the first action public static State operator >> (State lhs, State rhs) => lhs.Bind(x => rhs.Map(_ => x)); /// /// Sequentially compose two actions. The second action is a unit returning action, so the result of the /// first action is propagated. /// /// First action to run /// Second action to run /// Result of the first action public static State operator >> (State lhs, K, Unit> rhs) => lhs.Bind(x => rhs.Map(_ => x)); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Run the monad // /// /// Run the state monad /// /// Initial state /// Bound monad public (A Value, S State) Run(S state) => runState(state); } ================================================ FILE: LanguageExt.Core/Monads/State and Environment Monads/State/State/Trait/State.TraitImpl.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; /// /// Traits implementation for `State` /// /// State environment type public partial class State : Monad>, Stateful, S> { static K, B> Monad>.Bind(K, A> ma, Func, B>> f) => ma.As().Bind(f); static K, B> Monad>.Recur(A value, Func, Next>> f) => new State(state => { while (true) { (var mr, state) = f(value).Run(state); if (mr.IsDone) return (mr.Done, state); value = mr.Loop; } }); static K, B> Functor>.Map(Func f, K, A> ma) => ma.As().Map(f); static K, A> Applicative>.Pure(A value) => State.Pure(value); static K, B> Applicative>.Apply(K, Func> mf, K, A> ma) => mf.As().Bind(x => ma.As().Map(x)); static K, B> Applicative>.Apply(K, Func> mf, Memo, A> ma) => mf.As().Bind(x => ma.Value.As().Map(x)); static K, Unit> Stateful, S>.Modify(Func modify) => State.Modify(modify); static K, A> Stateful, S>.Gets(Func f) => State.Gets(f); static K, Unit> Stateful, S>.Put(S value) => State.Put(value); } ================================================ FILE: LanguageExt.Core/Monads/State and Environment Monads/State/StateT/Extensions/StateT.Extensions.cs ================================================ using System; using System.Diagnostics.Contracts; using LanguageExt.Traits; namespace LanguageExt; /// /// State monad extensions /// public static partial class StateTExtensions { extension(K, A> ma) where M : Monad { public StateT As() => (StateT)ma; /// /// Run the state monad /// /// Initial state /// Bound monad public K Run(S state) => ((StateT)ma).runState(state); } /// /// Monadic join /// [Pure] public static StateT Flatten(this StateT> mma) where M : Monad => mma.Bind(x => x); /// /// Monad bind operation /// /// Monadic bind function /// Projection function /// Intermediate bound value type /// Target bound value type /// `StateT` public static StateT SelectMany( this K ma, Func, B>> bind, Func project) where M : Monad => StateT.Lift(ma).SelectMany(bind, project); /// /// Monad bind operation /// /// Monadic bind function /// Projection function /// Intermediate bound value type /// Target bound value type /// `StateT` public static StateT SelectMany( this K ma, Func> bind, Func project) where M : Monad => StateT.Lift(ma).SelectMany(bind, project); } ================================================ FILE: LanguageExt.Core/Monads/State and Environment Monads/State/StateT/Operators/StateT.Operators.Applicative.cs ================================================ using System; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; public static partial class StateTExtensions { extension(K, A> self) where M : Monad { /// /// Applicative sequence operator /// public static StateT operator >>> (K, A> ma, K, B> mb) => +ma.Action(mb); /// /// Applicative apply operator /// public static StateT operator * (K, Func> mf, K, A> ma) => +mf.Apply(ma); /// /// Applicative apply operator /// public static StateT operator * (K, A> ma, K, Func> mf) => +mf.Apply(ma); } extension(K, A> self) where M : Monad { /// /// Applicative apply operator /// public static StateT> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static StateT> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) where M : Monad { /// /// Applicative apply operator /// public static StateT>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static StateT>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) where M : Monad { /// /// Applicative apply operator /// public static StateT>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static StateT>>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) where M : Monad { /// /// Applicative apply operator /// public static StateT>>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static StateT>>>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) where M : Monad { /// /// Applicative apply operator /// public static StateT>>>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static StateT>>>>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) where M : Monad { /// /// Applicative apply operator /// public static StateT>>>>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static StateT>>>>>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) where M : Monad { /// /// Applicative apply operator /// public static StateT>>>>>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static StateT>>>>>>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) where M : Monad { /// /// Applicative apply operator /// public static StateT>>>>>>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static StateT>>>>>>>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) where M : Monad { /// /// Applicative apply operator /// public static StateT>>>>>>>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static StateT>>>>>>>>> operator *( K, A> ma, K, Func> mf) => curry * mf * ma; } } ================================================ FILE: LanguageExt.Core/Monads/State and Environment Monads/State/StateT/Operators/StateT.Operators.Choice.cs ================================================ using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt; public static partial class StateTExtensions { extension(K, A> self) where M : Monad, Choice { /// /// Choice operator. Usually means if the first argument succeeds, return it, otherwise return the second /// argument. /// /// Left hand side operand /// Right hand side operand /// public static StateT operator |(K, A> lhs, K, A> rhs) => new (s => lhs.As().runState(s) | rhs.As().runState(s)); /// /// Choice operator. Usually means if the first argument succeeds, return it, otherwise return the second /// argument. /// /// Left hand side operand /// Right hand side operand /// public static StateT operator |(K, A> lhs, Pure rhs) => new (s => lhs.As().runState(s) | rhs.Map(x => (x, env: s))); } } ================================================ FILE: LanguageExt.Core/Monads/State and Environment Monads/State/StateT/Operators/StateT.Operators.Fallible.cs ================================================ using LanguageExt.Traits; namespace LanguageExt; public static partial class StateTExtensions { extension(K, A> self) where M : Monad, Fallible { public static StateT operator |(K, A> lhs, CatchM rhs) => new(s => lhs.As().runState(s) | rhs.Map(a => (a, env: s))); public static StateT operator |(K, A> lhs, Fail rhs) => new(s => lhs.As().runState(s) | rhs); } } ================================================ FILE: LanguageExt.Core/Monads/State and Environment Monads/State/StateT/Operators/StateT.Operators.Final.cs ================================================ namespace LanguageExt.Traits; public static class StateTExtensions { extension(K, A> _) where M : Monad, Final { /// /// Run a `finally` operation after the main operation regardless of whether it succeeds or not. /// /// Primary operation /// Finally operation /// Result of primary operation public static StateT operator |(K, A> lhs, Finally rhs) => new(s => lhs.As().runState(s) | rhs); } } ================================================ FILE: LanguageExt.Core/Monads/State and Environment Monads/State/StateT/Operators/StateT.Operators.Functor.cs ================================================ using System; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; public static partial class StateTExtensions { extension(K, A> _) where M : Monad { /// /// Functor map operator /// public static StateT operator *(Func f, K, A> ma) => +ma.Map(f); /// /// Functor map operator /// public static StateT operator *(K, A> ma, Func f) => +ma.Map(f); } extension(K, A> _) where M : Monad { /// /// Functor map operator /// public static StateT> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static StateT> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) where M : Monad { /// /// Functor map operator /// public static StateT>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static StateT>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) where M : Monad { /// /// Functor map operator /// public static StateT>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static StateT>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) where M : Monad { /// /// Functor map operator /// public static StateT>>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static StateT>>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) where M : Monad { /// /// Functor map operator /// public static StateT>>>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static StateT>>>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) where M : Monad { /// /// Functor map operator /// public static StateT>>>>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static StateT>>>>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) where M : Monad { /// /// Functor map operator /// public static StateT>>>>>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static StateT>>>>>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) where M : Monad { /// /// Functor map operator /// public static StateT>>>>>>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static StateT>>>>>>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) where M : Monad { /// /// Functor map operator /// public static StateT>>>>>>>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static StateT>>>>>>>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } } ================================================ FILE: LanguageExt.Core/Monads/State and Environment Monads/State/StateT/Operators/StateT.Operators.Monad.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public static partial class StateTExtensions { extension(K, A> self) where M : Monad { /// /// Monad bind operator /// /// Monad to bind /// Binding function /// Mapped monad public static StateT operator >> (K, A> ma, Func, B>> f) => +ma.Bind(f); /// /// Sequentially compose two actions, discarding any value produced by the first, like sequencing operators (such /// as the semicolon) in C#. /// /// First action to run /// Second action to run /// Result of the second action public static StateT operator >> (K, A> lhs, K, B> rhs) => lhs >> (_ => rhs); } extension(K, A> self) where M : Monad { /// /// Sequentially compose two actions. The second action is a unit-returning action, so the result of the /// first action is propagated. /// /// First action to run /// Second action to run /// Result of the first action public static StateT operator >> (K, A> lhs, K, Unit> rhs) => lhs >> (x => (_ => x) * rhs); } } ================================================ FILE: LanguageExt.Core/Monads/State and Environment Monads/State/StateT/Operators/StateT.Operators.SemigroupK.cs ================================================ using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt; public static partial class StateTExtensions { extension(K, A> self) where M : Monad, SemigroupK { /// /// Semigroup combine operator: an associative binary operation. /// /// Left-hand side operand /// Right-hand side operand /// public static StateT operator +(K, A> lhs, K, A> rhs) => new(s => lhs.As().runState(s) + rhs.As().runState(s)); /// /// Semigroup combine operator: an associative binary operation. /// /// Left-hand side operand /// Right-hand side operand /// public static StateT operator +(K, A> lhs, Pure rhs) => new(s => lhs.As().runState(s) + M.Pure((rhs.Value, env: s))); } extension(K, A> self) where M : Monad, SemigroupK, Fallible { /// /// Semigroup combine operator: an associative binary operation. /// /// Left-hand side operand /// Right-hand side operand /// public static StateT operator +(K, A> lhs, Fail rhs) => new(s => lhs.As().runState(s) + M.Fail<(A, S)>(rhs.Value)); } } ================================================ FILE: LanguageExt.Core/Monads/State and Environment Monads/State/StateT/Operators/StateT.Operators.cs ================================================ using LanguageExt.Traits; namespace LanguageExt; public static partial class StateTExtensions { extension(K, A> _) where M : Monad { /// /// Downcast operator /// public static StateT operator +(K, A> ma) => (StateT)ma; /// /// Downcast operator /// public static StateT operator >> (K, A> ma, Lower lower) => +ma; } } ================================================ FILE: LanguageExt.Core/Monads/State and Environment Monads/State/StateT/StateT.Module.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public class StateT { public static StateT lift(K ma) where M : Monad => StateT.Lift(ma); } public partial class StateT { public static StateT pure(A value) => StateT.Pure(value); /// /// Lifts a given monad into the transformer /// /// Monad to lift /// `StateT` public static StateT liftIO(IO effect) => StateT.LiftIO(effect); } public class StateT { public static StateT pure(A value) where M : Monad => StateT.Pure(value); public static StateT lift(K ma) where M : Monad => StateT.Lift(ma); /// /// Lifts a given monad into the transformer /// /// Monad to lift /// `StateT` public static StateT liftIO(IO effect) where M : Monad => StateT.LiftIO(effect); public static StateT get() where M : Monad => StateT.Get; public static StateT gets(Func f) where M : Monad => StateT.Gets(f); public static StateT getsM(Func> f) where M : Monad => StateT.GetsM(f); public static StateT put(S state) where M : Monad => StateT.Put(state); public static StateT modify(Func f) where M : Monad => StateT.Modify(f); public static StateT modifyM(Func> f) where M : Monad => StateT.ModifyM(f); } ================================================ FILE: LanguageExt.Core/Monads/State and Environment Monads/State/StateT/StateT.cs ================================================ using System; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; /// /// `StateT` monad transformer, which adds a modifiable state to a given monad. /// /// Function that represents the transformer operation /// State type /// Given monad trait /// Bound value type public record StateT(Func> runState) : K, A> where M : Monad { /// /// Lift a pure value into the monad-transformer /// /// Value to lift /// `StateT` public static StateT Pure(A value) => Lift(M.Pure(value)); /// /// Extracts the state value, maps it, and then puts it back into /// the monadic state. /// /// State mapping function /// `StateT` public static StateT Modify(Func f) => new(state => M.Pure((unit, f(state)))); /// /// Extracts the state value, maps it, and then puts it back into /// the monadic state. /// /// State mapping function /// `StateT` public static StateT ModifyM(Func> f) => new(state => f(state).Map(s => (unit, s))); /// /// Writes the value into the monadic state /// /// `StateT` public static StateT Put(S value) => new(_ => M.Pure((unit, value))); /// /// Writes a value and state into the monad /// /// `StateT` public static StateT State(A value, S state) => new(_ => M.Pure((value, state))); /// /// Writes a value and state into the monad /// /// `StateT` public static StateT State((A value, S state) ma) => new(_ => M.Pure(ma)); /// /// Extracts the state value and returns it as the bound value /// /// `StateT` public static StateT Get { get; } = new(state => M.Pure((state, state))); /// /// Extracts the state value and maps it to the bound value /// /// State mapping function /// `StateT` public static StateT Gets(Func f) => new(state => M.Pure((f(state), state))); /// /// Extracts the state value and maps it to the bound value /// /// State mapping function /// `StateT` public static StateT GetsM(Func> f) => new(state => M.Map(v => (v, state), f(state))); /// /// Lifts a given monad into the transformer /// /// Monad to lift /// `StateT` public static StateT Lift(Pure monad) => Pure(monad.Value); /// /// Lifts a given monad into the transformer /// /// Monad to lift /// `StateT` public static StateT Lift(K monad) => new(state => M.Map(value => (value, state), monad)); /// /// Lifts a function into the transformer /// /// Function to lift /// `StateT` public static StateT Lift(Func f) => new(state => M.Pure((f(), state))); /// /// Lifts an IO monad into the monad /// /// NOTE: If the IO monad isn't the innermost monad of the transformer /// stack then this will throw an exception. /// IO monad to lift /// `StateT` public static StateT LiftIO(IO ma) => Lift(M.LiftIOMaybe(ma)); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Map // /// /// Maps the given monad /// /// Mapping function /// Trait of the monad to map to /// `StateT` public StateT MapT(Func, K> f) where M1 : Monad, Choice => new (state => f(runState(state))); /// /// Maps the given monad /// /// Mapping function /// `StateT` public StateT MapM(Func, K> f) => new(state => runState(state) .Bind(vs => f(M.Pure(vs.Value)).Map(x => (x, vs.State)))); /// /// Maps the bound value /// /// Mapping function /// Target bound value type /// `StateT` public StateT Map(Func f) => new(state => M.Map(x => (f(x.Value), x.State), runState(state))); /// /// Maps the bound value /// /// Mapping transducer /// Target bound value type /// `StateT` public StateT Select(Func f) => new(state => M.Map(x => (f(x.Value), x.State), runState(state))); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Bind // /// /// Monad bind operation /// /// Mapping function /// Target bound value type /// `StateT` public StateT Bind(Func, B>> f) => Bind(x => f(x).As()); /// /// Monad bind operation /// /// Mapping function /// Target bound value type /// `StateT` public StateT Bind(Func> f) => new(state => M.Bind(runState(state), x => f(x.Value).runState(x.State))); /// /// Monad bind operation /// /// Mapping function /// Target bound value type /// `StateT` public StateT Bind(Func> f) => Bind(x => f(x).ToStateT()); /// /// Monad bind operation /// /// Mapping function /// Target bound value type /// `StateT` public StateT Bind(Func> f) => Bind(x => f(x).ToStateT()); /// /// Monad bind operation /// /// Mapping function /// Target bound value type /// `StateT` public StateT Bind(Func> f) => Bind(x => f(x).ToStateT()); /// /// Monad bind operation /// /// Mapping function /// Target bound value type /// `StateT` public StateT Bind(Func> f) => Bind(x => StateT.LiftIO(f(x))); /// /// Monad bind operation /// /// Mapping function /// Target bound value type /// `StateT` public StateT Bind(Func> f) => Bind(x => StateT.LiftIO(f(x).As())); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // SelectMany // /// /// Monad bind operation /// /// Monadic bind function /// Projection function /// Intermediate bound value type /// Target bound value type /// `StateT` public StateT SelectMany(Func, B>> bind, Func project) => SelectMany(x => bind(x).As(), project); /// /// Monad bind operation /// /// Monadic bind function /// Projection function /// Intermediate bound value type /// Target bound value type /// `StateT` public StateT SelectMany(Func> bind, Func project) => new(state => M.Bind(runState(state), x => M.Map(y => (project(x.Value, y.Value), y.State), bind(x.Value).runState(x.State)))); /// /// Monad bind operation /// /// Monadic bind function /// Projection function /// Intermediate bound value type /// Target bound value type /// `StateT` public StateT SelectMany(Func> bind, Func project) => new(state => M.Bind(runState(state), x => M.Map(y => (project(x.Value, y), x.State), bind(x.Value)))); /// /// Monad bind operation /// /// Monadic bind function /// Projection function /// Intermediate bound value type /// Target bound value type /// `StateT` public StateT SelectMany(Func> bind, Func project) => Map(x => project(x, bind(x).Value)); /// /// Monad bind operation /// /// Monadic bind function /// Projection function /// Intermediate bound value type /// Target bound value type /// `StateT` public StateT SelectMany(Func> bind, Func project) => Bind(x => bind(x).ToStateT().Map(y => project(x, y))); /// /// Monad bind operation /// /// Monadic bind function /// Projection function /// Intermediate bound value type /// Target bound value type /// `StateT` public StateT SelectMany(Func> bind, Func project) => Bind(x => bind(x).ToStateT().Map(y => project(x, y))); /// /// Monad bind operation /// /// Monadic bind function /// Projection function /// Intermediate bound value type /// Target bound value type /// `StateT` public StateT SelectMany(Func> bind, Func project) => Bind(x => bind(x).ToStateT().Map(y => project(x, y))); /// /// Monad bind operation /// /// Monadic bind function /// Projection function /// Intermediate bound value type /// Target bound value type /// `StateT` public StateT SelectMany(Func> bind, Func project) => Bind(x => bind(x).Map(y => project(x, y))); /// /// Monad bind operation /// /// Monadic bind function /// Projection function /// Intermediate bound value type /// Target bound value type /// `StateT` public StateT SelectMany(Func> bind, Func project) => Bind(x => bind(x).As().Map(y => project(x, y))); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Operators // /// /// Sequentially compose two actions, discarding any value produced by the first, like sequencing operators (such /// as the semicolon) in C#. /// /// First action to run /// Second action to run /// Result of the second action public static StateT operator >> (StateT lhs, StateT rhs) => lhs.Bind(_ => rhs); /// /// Sequentially compose two actions, discarding any value produced by the first, like sequencing operators (such /// as the semicolon) in C#. /// /// First action to run /// Second action to run /// Result of the second action public static StateT operator >> (StateT lhs, K, A> rhs) => lhs.Bind(_ => rhs); /// /// Sequentially compose two actions. The second action is a unit returning action, so the result of the /// first action is propagated. /// /// First action to run /// Second action to run /// Result of the first action public static StateT operator >> (StateT lhs, StateT rhs) => lhs.Bind(x => rhs.Map(_ => x)); /// /// Sequentially compose two actions. The second action is a unit returning action, so the result of the /// first action is propagated. /// /// First action to run /// Second action to run /// Result of the first action public static StateT operator >> (StateT lhs, K, Unit> rhs) => lhs.Bind(x => rhs.Map(_ => x)); public static implicit operator StateT(Pure ma) => Pure(ma.Value); public static implicit operator StateT(IO ma) => LiftIO(ma); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Run the monad // /// /// Run the state monad /// /// Initial state /// Bound monad public K Run(S state) => runState(state); } ================================================ FILE: LanguageExt.Core/Monads/State and Environment Monads/State/StateT/Trait/StateT.TraitImpl.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; /// /// `MonadStateT` trait implementation for `StateT` /// /// State environment type /// Given monad trait public partial class StateT : MonadT, M>, Stateful, S> where M : Monad { static K, B> Monad>.Bind(K, A> ma, Func, B>> f) => ma.As().Bind(f); static K, B> Monad>.Recur(A value, Func, Next>> f) => new StateT( input => { return M.Recur(f(value).As().runState(input), go); K, S)>, (B, S)>> go(K Next, S State)> ma) => ma >> (n => n.Next switch { { IsDone: true, Done: var x } => M.Pure(Next.Done, S)>, (B, S)>((x, n.State))), { IsLoop: true, Loop: var x } => M.Pure(Next.Loop, S)>, (B, S)>(f(x).As().Run(n.State))), _ => throw new NotSupportedException() }); }); static K, B> Functor>.Map(Func f, K, A> ma) => ma.As().Map(f); static K, A> Applicative>.Pure(A value) => StateT.Pure(value); static K, B> Applicative>.Apply(K, Func> mf, K, A> ma) => mf.As().Bind(x => ma.As().Map(x)); static K, B> Applicative>.Apply(K, Func> mf, Memo, A> ma) => mf.As().Bind(x => ma.Value.As().Map(x)); static K, A> MonadT, M>.Lift(K ma) => StateT.Lift(ma); static K, Unit> Stateful, S>.Modify(Func modify) => StateT.Modify(modify); static K, A> Stateful, S>.Gets(Func f) => StateT.Gets(f); static K, Unit> Stateful, S>.Put(S value) => StateT.Put(value); static K, A> Maybe.MonadIO>.LiftIOMaybe(IO ma) => StateT.Lift(M.LiftIOMaybe(ma)); } ================================================ FILE: LanguageExt.Core/Monads/State and Environment Monads/Writer/Tell.cs ================================================ using System; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; /// /// State put /// /// /// This is a convenience type that is created by the Prelude `put` function. It avoids /// the need for lots of generic parameters when used in `WriterT` and `State` based /// monads. /// /// Mapping from the environment /// State type public record Tell(W Value) where W : Monoid { /// /// Convert with `Writable` /// public K ToWritable() where M : Writable => Writable.tell(Value); /// /// Convert to a `WriterT` /// public WriterT ToWriterT() where M : Monad => Writable.tell, W>(Value).As(); /// /// Convert to a `WriterT` /// public Writer ToWriter() => Writable.tell, W>(Value).As(); /// /// Monadic bind with `WriterT` /// public WriterT SelectMany(Func> bind, Func project) where M : Monad => ToWriterT().SelectMany(bind, project); /// /// Monadic bind with `WriterT` /// public WriterT SelectMany(Func, B>> bind, Func project) where M : Monad => ToWriterT().SelectMany(bind, project); /// /// Monadic bind with `Writer` /// public Writer SelectMany(Func> bind, Func project) => ToWriter().SelectMany(bind, project); /// /// Monadic bind with `Writer` /// public Writer SelectMany(Func, B>> bind, Func project) => ToWriter().SelectMany(bind, project); /// /// Monadic bind with `RWST` /// public RWST SelectMany(Func> bind, Func project) where M : Writable, Monad, Choice => ToWritable().SelectMany(bind, project); /// /// Monadic bind with `RWST` /// public RWST SelectMany(Func, B>> bind, Func project) where M : Writable, Monad, Choice => ToWritable().SelectMany(bind, project); } ================================================ FILE: LanguageExt.Core/Monads/State and Environment Monads/Writer/Writer/Extensions/Writer.Extensions.cs ================================================ using System.Diagnostics.Contracts; using LanguageExt.Traits; namespace LanguageExt; public static partial class WriterExtensions { extension(K, A> ma) where W : Monoid { public Writer As() => (Writer)ma; /// /// Run the writer /// /// Bound monad public (A Value, W Output) Run(W initial) => ((Writer)ma).runWriter(initial); /// /// Run the writer /// /// Bound monad public (A Value, W Output) Run() => ((Writer)ma).runWriter(W.Empty); } /// /// Monadic join /// [Pure] public static Writer Flatten(this Writer> mma) where W : Monoid => mma.Bind(x => x); } ================================================ FILE: LanguageExt.Core/Monads/State and Environment Monads/Writer/Writer/Operators/Writer.Operators.Applicative.cs ================================================ using System; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; public static partial class WriterExtensions { extension(K, A> self) where W : Monoid { /// /// Applicative sequence operator /// public static Writer operator >>> (K, A> ma, K, B> mb) => +ma.Action(mb); /// /// Applicative apply operator /// public static Writer operator * (K, Func> mf, K, A> ma) => +mf.Apply(ma); /// /// Applicative apply operator /// public static Writer operator * (K, A> ma, K, Func> mf) => +mf.Apply(ma); } extension(K, A> self) where W : Monoid { /// /// Applicative apply operator /// public static Writer> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static Writer> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) where W : Monoid { /// /// Applicative apply operator /// public static Writer>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static Writer>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) where W : Monoid { /// /// Applicative apply operator /// public static Writer>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static Writer>>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) where W : Monoid { /// /// Applicative apply operator /// public static Writer>>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static Writer>>>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) where W : Monoid { /// /// Applicative apply operator /// public static Writer>>>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static Writer>>>>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) where W : Monoid { /// /// Applicative apply operator /// public static Writer>>>>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static Writer>>>>>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) where W : Monoid { /// /// Applicative apply operator /// public static Writer>>>>>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static Writer>>>>>>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) where W : Monoid { /// /// Applicative apply operator /// public static Writer>>>>>>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static Writer>>>>>>>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) where W : Monoid { /// /// Applicative apply operator /// public static Writer>>>>>>>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static Writer>>>>>>>>> operator *( K, A> ma, K, Func> mf) => curry * mf * ma; } } ================================================ FILE: LanguageExt.Core/Monads/State and Environment Monads/Writer/Writer/Operators/Writer.Operators.Functor.cs ================================================ using System; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; public static partial class WriterExtensions { extension(K, A> _) where W : Monoid { /// /// Functor map operator /// public static Writer operator *(Func f, K, A> ma) => +ma.Map(f); /// /// Functor map operator /// public static Writer operator *(K, A> ma, Func f) => +ma.Map(f); } extension(K, A> _) where W : Monoid { /// /// Functor map operator /// public static Writer> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static Writer> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) where W : Monoid { /// /// Functor map operator /// public static Writer>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static Writer>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) where W : Monoid { /// /// Functor map operator /// public static Writer>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static Writer>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) where W : Monoid { /// /// Functor map operator /// public static Writer>>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static Writer>>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) where W : Monoid { /// /// Functor map operator /// public static Writer>>>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static Writer>>>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) where W : Monoid { /// /// Functor map operator /// public static Writer>>>>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static Writer>>>>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) where W : Monoid { /// /// Functor map operator /// public static Writer>>>>>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static Writer>>>>>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) where W : Monoid { /// /// Functor map operator /// public static Writer>>>>>>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static Writer>>>>>>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) where W : Monoid { /// /// Functor map operator /// public static Writer>>>>>>>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static Writer>>>>>>>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } } ================================================ FILE: LanguageExt.Core/Monads/State and Environment Monads/Writer/Writer/Operators/Writer.Operators.Monad.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public static partial class ReaderExtensions { extension(K, A> self) { /// /// Monad bind operator /// /// Monad to bind /// Binding function /// Mapped monad public static State operator >> (K, A> ma, Func, B>> f) => +ma.Bind(f); /// /// Sequentially compose two actions, discarding any value produced by the first, like sequencing operators (such /// as the semicolon) in C#. /// /// First action to run /// Second action to run /// Result of the second action public static State operator >> (K, A> lhs, K, B> rhs) => lhs >> (_ => rhs); } extension(K, A> self) { /// /// Sequentially compose two actions. The second action is a unit-returning action, so the result of the /// first action is propagated. /// /// First action to run /// Second action to run /// Result of the first action public static State operator >> (K, A> lhs, K, Unit> rhs) => lhs >> (x => (_ => x) * rhs); } } ================================================ FILE: LanguageExt.Core/Monads/State and Environment Monads/Writer/Writer/Operators/Writer.Operators.cs ================================================ using LanguageExt.Traits; namespace LanguageExt; public static partial class WriterExtensions { extension(K, A> _) where W : Monoid { /// /// Downcast operator /// public static Writer operator +(K, A> ma) => (Writer)ma; /// /// Downcast operator /// public static Writer operator >> (K, A> ma, Lower lower) => +ma; } } ================================================ FILE: LanguageExt.Core/Monads/State and Environment Monads/Writer/Writer/Prelude/Writer.Prelude.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public partial class Prelude { /// /// Tell is an action that produces the writer output /// /// Item to tell /// Writer type /// Structure with the told item public static Writer tell(W item) where W : Monoid => new (w => (default, w + item)); /// /// Writes an item and returns a value at the same time /// public static Writer write((A, W) item) where W : Monoid => Writable.write, A>(item).As(); /// /// Writes an item and returns a value at the same time /// public static Writer write(A value, W item) where W : Monoid => Writable.write, A>(value, item).As(); /// /// `pass` is an action that executes the `action`, which returns a value and a /// function; it then returns the value with the output having been applied to /// the function. /// public static Writer pass(Writer Function)> action) where W : Monoid => Writable.pass(action).As(); /// /// `listen` executes the action `ma` and adds the result of applying `f` to the /// output to the value of the computation. /// public static Writer listen(Writer ma) where W : Monoid => Writable.listen, A>(ma).As(); /// /// `listens` executes the action `ma` and adds the result of applying `f` to the /// output to the value of the computation. /// public static Writer listens(Func f, Writer ma) where W : Monoid => Writable.listens(f, ma).As(); /// /// `censor` executes the action `ma` and applies the function `f` to its output, /// leaving the return value unchanged. /// public static Writer censor(Func f, Writer ma) where W : Monoid => Writable.censor(f, ma).As(); } ================================================ FILE: LanguageExt.Core/Monads/State and Environment Monads/Writer/Writer/Trait/Writer.TraitImpl.cs ================================================ using System; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; /// /// `MonadStateT` trait implementation for `StateT` /// /// State environment type /// Given monad trait public partial class Writer : Monad>, Writable, W> where W : Monoid { static K, B> Monad>.Bind(K, A> ma, Func, B>> f) => ma.As().Bind(f); static K, B> Monad>.Recur(A value, Func, Next>> f) => new Writer(output => { while (true) { (var mr, output) = f(value).Run(output); if (mr.IsDone) return (mr.Done, output); value = mr.Loop; } }); static K, B> Functor>.Map(Func f, K, A> ma) => ma.As().Map(f); static K, A> Applicative>.Pure(A value) => Writer.Pure(value); static K, B> Applicative>.Apply(K, Func> mf, K, A> ma) => mf.As().Bind(x => ma.As().Map(x)); static K, B> Applicative>.Apply(K, Func> mf, Memo, A> ma) => mf.As().Bind(x => ma.Value.As().Map(x)); static K, Unit> Writable, W>.Tell(W item) => new Writer(w => (unit, w + item)); static K, (A Value, W Output)> Writable, W>.Listen(K, A> ma) => ma.As().Listen(); static K, A> Writable, W>.Pass( K, (A Value, Func Function)> action) => new Writer( w => { var ((a, f), w1) = action.As().Run(); return (a, w + f(w1)); }); } ================================================ FILE: LanguageExt.Core/Monads/State and Environment Monads/Writer/Writer/Writer.Module.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public partial class Writer { public static Writer pure(A value) => Writer.Pure(value); } public class Writer { public static Writer pure(A value) where W : Monoid => Writer.Pure(value); /// /// Tell is an action that produces the writer output /// /// Item to tell /// Writer type /// Structure with the told item public static Writer tell(W item) where W : Monoid => new (w => (default, w + item)); /// /// Writes an item and returns a value at the same time /// public static Writer write((A, W) item) where W : Monoid => Writable.write, A>(item).As(); /// /// Writes an item and returns a value at the same time /// public static Writer write(A value, W item) where W : Monoid => Writable.write, A>(value, item).As(); /// /// `pass` is an action that executes the `action`, which returns a value and a /// function; it then returns the value with the output having been applied to /// the function. /// public static Writer pass(Writer Function)> action) where W : Monoid => Writable.pass(action).As(); /// /// `listen` executes the action `ma` and adds the result of applying `f` to the /// output to the value of the computation. /// public static Writer listen(Writer ma) where W : Monoid => Writable.listen, A>(ma).As(); /// /// `listens` executes the action `ma` and adds the result of applying `f` to the /// output to the value of the computation. /// public static Writer listens(Func f, Writer ma) where W : Monoid => Writable.listens(f, ma).As(); /// /// `censor` executes the action `ma` and applies the function `f` to its output, /// leaving the return value unchanged. /// public static Writer censor(Func f, Writer ma) where W : Monoid => Writable.censor(f, ma).As(); } ================================================ FILE: LanguageExt.Core/Monads/State and Environment Monads/Writer/Writer/Writer.cs ================================================ using System; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; /// /// `Writer` monad transformer, which adds a modifiable state to a given monad. /// /// State type /// Given monad trait /// Bound value type public record Writer(Func runWriter) : K, A> where W : Monoid { /// /// Lift a pure value into the monad-transformer /// /// Value to lift /// `Writer` public static Writer Pure(A value) => new (w => (value, w)); /// /// Construct a writer computation from a (result, output) pair. /// /// /// The inverse of `Run()` /// /// Result / Output pair public Writer Write((A Value, W Output) result) => new(w => (result.Value, w.Combine(result.Output))); /// /// Construct a writer computation from a (result, output) pair. /// /// /// The inverse of `Run()` /// /// Result / Output pair public Writer Write(A value, W output) => new(w => (value, w.Combine(output))); /// /// Writes an item and returns a value at the same time /// public Writer Listen() => Listens(x => x); /// /// `Listens` executes the action and adds the result of applying `f` to the /// output to the value of the computation. /// public Writer Listens(Func f) => new(w => { var (a, w1) = Run(); return ((a, f(w1)), w + w1); }); /// /// `Censor` executes the action and applies the function `f` to its output, /// leaving the return value unchanged. /// public Writer Censor(Func f) => new (w => { var (a, w1) = Run(); return (a, w + f(w1)); }); /// /// Lifts a given monad into the transformer /// /// Monad to lift /// `Writer` public static Writer Lift(Pure monad) => Pure(monad.Value); /// /// Lifts a function into the transformer /// /// Function to lift /// `Writer` public static Writer Lift(Func f) => new(w => (f(), w)); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Map // /// /// Maps the bound value /// /// Mapping function /// Target bound value type /// `Writer` public Writer Map(Func f) => new(w => mapFirst(f, runWriter(w))); /// /// Maps the bound value /// /// Mapping transducer /// Target bound value type /// `Writer` public Writer Select(Func f) => new(w => mapFirst(f, runWriter(w))); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Bind // /// /// Monad bind operation /// /// Mapping function /// Target bound value type /// `Writer` public Writer Bind(Func, B>> f) => Bind(x => f(x).As()); /// /// Monad bind operation /// /// Mapping function /// Target bound value type /// `Writer` public Writer Bind(Func> f) => new(w => { var (a, w1) = runWriter(w); return f(a).runWriter(w1); }); /// /// Monad bind operation /// /// Mapping function /// Target bound value type /// `Writer` public Writer Bind(Func> f) => Bind(x => f(x).ToWriter()); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // SelectMany // /// /// Monad bind operation /// /// Monadic bind function /// Projection function /// Intermediate bound value type /// Target bound value type /// `Writer` public Writer SelectMany(Func, B>> bind, Func project) => SelectMany(x => bind(x).As(), project); /// /// Monad bind operation /// /// Monadic bind function /// Projection function /// Intermediate bound value type /// Target bound value type /// `Writer` public Writer SelectMany(Func> bind, Func project) => Bind(x => bind(x).Map(y => project(x, y))); /// /// Monad bind operation /// /// Monadic bind function /// Projection function /// Intermediate bound value type /// Target bound value type /// `Writer` public Writer SelectMany(Func> bind, Func project) => Map(x => project(x, bind(x).Value)); /// /// Monad bind operation /// /// Monadic bind function /// Projection function /// Intermediate bound value type /// Target bound value type /// `Writer` public Writer SelectMany(Func> bind, Func project) => SelectMany(x => bind(x).ToWriter(), project); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Operators // /// /// Sequentially compose two actions, discarding any value produced by the first, like sequencing operators (such /// as the semicolon) in C#. /// /// First action to run /// Second action to run /// Result of the second action public static Writer operator >> (Writer lhs, Writer rhs) => lhs.Bind(_ => rhs); /// /// Sequentially compose two actions, discarding any value produced by the first, like sequencing operators (such /// as the semicolon) in C#. /// /// First action to run /// Second action to run /// Result of the second action public static Writer operator >> (Writer lhs, K, A> rhs) => lhs.Bind(_ => rhs); /// /// Sequentially compose two actions. The second action is a unit returning action, so the result of the /// first action is propagated. /// /// First action to run /// Second action to run /// Result of the first action public static Writer operator >> (Writer lhs, Writer rhs) => lhs.Bind(x => rhs.Map(_ => x)); /// /// Sequentially compose two actions. The second action is a unit returning action, so the result of the /// first action is propagated. /// /// First action to run /// Second action to run /// Result of the first action public static Writer operator >> (Writer lhs, K, Unit> rhs) => lhs.Bind(x => rhs.Map(_ => x)); public static implicit operator Writer(Pure ma) => Pure(ma.Value); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Run the monad // /// /// Run the writer /// /// Bound monad public (A Value, W Output) Run() => runWriter(W.Empty); } ================================================ FILE: LanguageExt.Core/Monads/State and Environment Monads/Writer/WriterT/Extensions/WriterT.Extensions.cs ================================================ using System; using System.Diagnostics.Contracts; using LanguageExt.Traits; namespace LanguageExt; public static partial class WriterTExtensions { public static WriterT As(this K, A> ma) where M : Monad where W : Monoid => (WriterT)ma; /// /// Run the writer /// /// Bound monad public static K Run(this K, A> ma) where M : Monad where W : Monoid => ((WriterT)ma).runWriter(W.Empty); /// /// Run the writer /// /// Bound monad public static K Run(this K, A> ma, W initial) where M : Monad where W : Monoid => ((WriterT)ma).runWriter(initial); /// /// Monadic join /// [Pure] public static WriterT Flatten(this WriterT> mma) where W : Monoid where M : Monad => mma.Bind(x => x); /// /// Monad bind operation /// /// Monadic bind function /// Projection function /// Intermediate bound value type /// Target bound value type /// `StateT` public static WriterT SelectMany( this K ma, Func, B>> bind, Func project) where W : Monoid where M : Monad => WriterT.Lift(ma).SelectMany(bind, project); /// /// Monad bind operation /// /// Monadic bind function /// Projection function /// Intermediate bound value type /// Target bound value type /// `StateT` public static WriterT SelectMany( this K ma, Func> bind, Func project) where W : Monoid where M : Monad => WriterT.Lift(ma).SelectMany(bind, project); } ================================================ FILE: LanguageExt.Core/Monads/State and Environment Monads/Writer/WriterT/Operators/WriterT.Operators.Applicative.cs ================================================ using System; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; public static partial class StateTExtensions { extension(K, A> self) where M : Monad where W : Monoid { /// /// Applicative sequence operator /// public static WriterT operator >>> (K, A> ma, K, B> mb) => +ma.Action(mb); /// /// Applicative apply operator /// public static WriterT operator * (K, Func> mf, K, A> ma) => +mf.Apply(ma); /// /// Applicative apply operator /// public static WriterT operator * (K, A> ma, K, Func> mf) => +mf.Apply(ma); } extension(K, A> self) where M : Monad where W : Monoid { /// /// Applicative apply operator /// public static WriterT> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static WriterT> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) where M : Monad where W : Monoid { /// /// Applicative apply operator /// public static WriterT>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static WriterT>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) where M : Monad where W : Monoid { /// /// Applicative apply operator /// public static WriterT>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static WriterT>>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) where M : Monad where W : Monoid { /// /// Applicative apply operator /// public static WriterT>>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static WriterT>>>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) where M : Monad where W : Monoid { /// /// Applicative apply operator /// public static WriterT>>>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static WriterT>>>>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) where M : Monad where W : Monoid { /// /// Applicative apply operator /// public static WriterT>>>>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static WriterT>>>>>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) where M : Monad where W : Monoid { /// /// Applicative apply operator /// public static WriterT>>>>>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static WriterT>>>>>>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) where M : Monad where W : Monoid { /// /// Applicative apply operator /// public static WriterT>>>>>>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static WriterT>>>>>>>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) where M : Monad where W : Monoid { /// /// Applicative apply operator /// public static WriterT>>>>>>>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static WriterT>>>>>>>>> operator *( K, A> ma, K, Func> mf) => curry * mf * ma; } } ================================================ FILE: LanguageExt.Core/Monads/State and Environment Monads/Writer/WriterT/Operators/WriterT.Operators.Choice.cs ================================================ using LanguageExt.Traits; namespace LanguageExt; public static partial class WriterTExtensions { extension(K, A> self) where M : Monad, Choice where W : Monoid { /// /// Choice operator. Usually means if the first argument succeeds, return it, otherwise return the second /// argument. /// /// Left hand side operand /// Right hand side operand /// public static WriterT operator |(K, A> lhs, K, A> rhs) => new (output => lhs.As().runWriter(output) | rhs.As().runWriter(output)); /// /// Choice operator. Usually means if the first argument succeeds, return it, otherwise return the second /// argument. /// /// Left hand side operand /// Right hand side operand /// public static WriterT operator |(K, A> lhs, Pure rhs) => new (output => lhs.As().runWriter(output) | rhs.Map(x => (x, env: output))); } } ================================================ FILE: LanguageExt.Core/Monads/State and Environment Monads/Writer/WriterT/Operators/WriterT.Operators.Fallible.cs ================================================ using LanguageExt.Traits; namespace LanguageExt; public static partial class WriterTExtensions { extension(K, A> self) where M : Monad, Fallible where W : Monoid { public static WriterT operator |(K, A> lhs, CatchM rhs) => new(output => lhs.As().runWriter(output) | rhs.Map(a => (a, env: output))); public static WriterT operator |(K, A> lhs, Fail rhs) => new(output => lhs.As().runWriter(output) | rhs); } } ================================================ FILE: LanguageExt.Core/Monads/State and Environment Monads/Writer/WriterT/Operators/WriterT.Operators.Final.cs ================================================ namespace LanguageExt.Traits; public static class WriterTExtensions { extension(K, A> _) where M : Monad, Final where W : Monoid { /// /// Run a `finally` operation after the main operation regardless of whether it succeeds or not. /// /// Primary operation /// Finally operation /// Result of primary operation public static WriterT operator |(K, A> lhs, Finally rhs) => new(output => lhs.As().runWriter(output) | rhs); } } ================================================ FILE: LanguageExt.Core/Monads/State and Environment Monads/Writer/WriterT/Operators/WriterT.Operators.Functor.cs ================================================ using System; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; public static partial class WriterTExtensions { extension(K, A> _) where M : Monad where W : Monoid { /// /// Functor map operator /// public static WriterT operator *(Func f, K, A> ma) => +ma.Map(f); /// /// Functor map operator /// public static WriterT operator *(K, A> ma, Func f) => +ma.Map(f); } extension(K, A> _) where M : Monad where W : Monoid { /// /// Functor map operator /// public static WriterT> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static WriterT> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) where M : Monad where W : Monoid { /// /// Functor map operator /// public static WriterT>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static WriterT>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) where M : Monad where W : Monoid { /// /// Functor map operator /// public static WriterT>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static WriterT>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) where M : Monad where W : Monoid { /// /// Functor map operator /// public static WriterT>>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static WriterT>>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) where M : Monad where W : Monoid { /// /// Functor map operator /// public static WriterT>>>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static WriterT>>>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) where M : Monad where W : Monoid { /// /// Functor map operator /// public static WriterT>>>>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static WriterT>>>>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) where M : Monad where W : Monoid { /// /// Functor map operator /// public static WriterT>>>>>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static WriterT>>>>>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) where M : Monad where W : Monoid { /// /// Functor map operator /// public static WriterT>>>>>>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static WriterT>>>>>>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) where M : Monad where W : Monoid { /// /// Functor map operator /// public static WriterT>>>>>>>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static WriterT>>>>>>>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } } ================================================ FILE: LanguageExt.Core/Monads/State and Environment Monads/Writer/WriterT/Operators/WriterT.Operators.Monad.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public static partial class WriterTExtensions { extension(K, A> self) where M : Monad where W : Monoid { /// /// Monad bind operator /// /// Monad to bind /// Binding function /// Mapped monad public static WriterT operator >> (K, A> ma, Func, B>> f) => +ma.Bind(f); /// /// Sequentially compose two actions, discarding any value produced by the first, like sequencing operators (such /// as the semicolon) in C#. /// /// First action to run /// Second action to run /// Result of the second action public static WriterT operator >> (K, A> lhs, K, B> rhs) => lhs >> (_ => rhs); } extension(K, A> self) where M : Monad where W : Monoid { /// /// Sequentially compose two actions. The second action is a unit-returning action, so the result of the /// first action is propagated. /// /// First action to run /// Second action to run /// Result of the first action public static WriterT operator >> (K, A> lhs, K, Unit> rhs) => lhs >> (x => (_ => x) * rhs); } } ================================================ FILE: LanguageExt.Core/Monads/State and Environment Monads/Writer/WriterT/Operators/WriterT.Operators.SemigroupK.cs ================================================ using LanguageExt.Traits; namespace LanguageExt; public static partial class WriterTExtensions { extension(K, A> self) where M : Monad, SemigroupK where W : Monoid { /// /// Semigroup combine operator: an associative binary operation. /// /// Left-hand side operand /// Right-hand side operand /// public static WriterT operator +(K, A> lhs, K, A> rhs) => new(output => lhs.As().runWriter(output) + rhs.As().runWriter(output)); /// /// Semigroup combine operator: an associative binary operation. /// /// Left-hand side operand /// Right-hand side operand /// public static WriterT operator +(K, A> lhs, Pure rhs) => new(s => lhs.As().runWriter(s) + M.Pure((rhs.Value, env: s))); } extension(K, A> self) where M : Monad, SemigroupK, Fallible where W : Monoid { /// /// Semigroup combine operator: an associative binary operation. /// /// Left-hand side operand /// Right-hand side operand /// public static WriterT operator +(K, A> lhs, Fail rhs) => new(output => lhs.As().runWriter(output) + M.Fail<(A, W)>(rhs.Value)); } } ================================================ FILE: LanguageExt.Core/Monads/State and Environment Monads/Writer/WriterT/Operators/WriterT.Operators.cs ================================================ using LanguageExt.Traits; namespace LanguageExt; public static partial class WriterTExtensions { extension(K, A> _) where M : Monad where W : Monoid { /// /// Downcast operator /// public static WriterT operator +(K, A> ma) => (WriterT)ma; /// /// Downcast operator /// public static WriterT operator >> (K, A> ma, Lower lower) => +ma; } } ================================================ FILE: LanguageExt.Core/Monads/State and Environment Monads/Writer/WriterT/Trait/WriterT.TraitImpl.cs ================================================ using System; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; /// /// `MonadStateT` trait implementation for `StateT` /// /// State environment type /// Given monad trait public partial class WriterT : MonadT, M>, Writable, W> where M : Monad where W : Monoid { static K, B> Monad>.Bind(K, A> ma, Func, B>> f) => ma.As().Bind(f); static K, B> Monad>.Recur(A value, Func, Next>> f) => new WriterT( output => { return M.Recur(f(value).As().runWriter(output), go); K, W)>, (B, W)>> go(K Next, W Output)> ma) => ma >> (n => n.Next switch { { IsDone: true, Done: var x } => M.Pure(Next.Done, W)>, (B, W)>((x, n.Output))), { IsLoop: true, Loop: var x } => M.Pure(Next.Loop, W)>, (B, W)>(f(x).As().Run(n.Output))), _ => throw new NotSupportedException() }); }); static K, B> Functor>.Map(Func f, K, A> ma) => ma.As().Map(f); static K, A> Applicative>.Pure(A value) => WriterT.Pure(value); static K, B> Applicative>.Apply(K, Func> mf, K, A> ma) => mf.As().Bind(x => ma.As().Map(x)); static K, B> Applicative>.Apply(K, Func> mf, Memo, A> ma) => mf.As().Bind(x => ma.Value.As().Map(x)); static K, A> MonadT, M>.Lift(K ma) => WriterT.Lift(ma); static K, A> Maybe.MonadIO>.LiftIOMaybe(IO ma) => WriterT.Lift(M.LiftIOMaybe(ma)); static K, Unit> Writable, W>.Tell(W item) => new WriterT(w => M.Pure((unit, w + item))); static K, (A Value, W Output)> Writable, W>.Listen(K, A> ma) => ma.As().Listen; static K, A> Writable, W>.Pass( K, (A Value, Func Function)> action) => new WriterT( w => action.As() .Run() .Map(afw => (afw.Value.Value, w + afw.Value.Function(afw.Output)))); } ================================================ FILE: LanguageExt.Core/Monads/State and Environment Monads/Writer/WriterT/WriterT.Module.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public class WriterT where W : Monoid { public static WriterT lift(K ma) where M : Monad => WriterT.Lift(ma); } public partial class WriterT { public static WriterT pure(A value) => WriterT.Pure(value); /// /// Lifts a given monad into the transformer /// /// Monad to lift /// `WriterT` public static WriterT liftIO(IO effect) => WriterT.LiftIO(effect); } public class WriterT { public static WriterT pure(A value) where W : Monoid where M : Monad => WriterT.Pure(value); public static WriterT lift(K ma) where W : Monoid where M : Monad => WriterT.Lift(ma); /// /// Lifts a given monad into the transformer /// /// Monad to lift /// `WriterT` public static WriterT liftIO(IO effect) where W : Monoid where M : Monad => WriterT.LiftIO(effect); /// /// Tell is an action that produces the writer output /// /// Item to tell /// Writer type /// Structure with the told item public static WriterT tell(W item) where M : Monad where W : Monoid => new (w => M.Pure((default(Unit), w + item))); /// /// Writes an item and returns a value at the same time /// public static WriterT write((A, W) item) where M : Monad where W : Monoid => new (w => M.Pure((item.Item1, w + item.Item2))); /// /// Writes an item and returns a value at the same time /// public static WriterT write(A value, W item) where M : Monad where W : Monoid => new (w => M.Pure((value, w + item))); /// /// `pass` is an action that executes the `action`, which returns a value and a /// function; it then returns the value with the output having been applied to /// the function. /// public static WriterT pass(WriterT Function)> action) where M : Monad where W : Monoid => Writable.pass(action).As(); /// /// `listen` executes the action `ma` and adds the result of applying `f` to the /// output to the value of the computation. /// public static WriterT listen(WriterT ma) where M : Monad where W : Monoid => Writable.listen, A>(ma).As(); /// /// `listens` executes the action `ma` and adds the result of applying `f` to the /// output to the value of the computation. /// public static WriterT listens(Func f, WriterT ma) where M : Monad where W : Monoid => Writable.listens(f, ma).As(); /// /// `censor` executes the action `ma` and applies the function `f` to its output, /// leaving the return value unchanged. /// public static WriterT censor(Func f, WriterT ma) where M : Monad where W : Monoid => Writable.censor(f, ma).As(); } ================================================ FILE: LanguageExt.Core/Monads/State and Environment Monads/Writer/WriterT/WriterT.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; /// /// `WriterT` monad transformer, which adds a modifiable state to a given monad. /// /// State type /// Given monad trait /// Bound value type public record WriterT(Func> runWriter) : K, A> where M : Monad where W : Monoid { /// /// Lift a pure value into the monad-transformer /// /// Value to lift /// `WriterT` public static WriterT Pure(A value) => new (w => M.Pure((value, w))); /// /// Construct a writer computation from a (result, output) pair. /// /// /// The inverse of `Run()` /// /// Result / Output pair public static WriterT Write((A Value, W Output) result) => new(w => M.Pure((result.Value, w.Combine(result.Output)))); /// /// Construct a writer computation from a (result, output) pair. /// /// /// The inverse of `Run()` /// /// Result / Output pair public static WriterT Write(A value, W output) => new(w => M.Pure((value, w.Combine(output)))); /// /// Writes an item and returns a value at the same time /// public WriterT Listen => Listens(x => x); /// /// `Listens` executes the action and adds the result of applying `f` to the /// output to the value of the computation. /// public WriterT Listens(Func f) => new(w => Run().Map(aw => ((aw.Value, f(aw.Output)), w + aw.Output))); /// /// `Censor` executes the action and applies the function `f` to its output, /// leaving the return value unchanged. /// public WriterT Censor(Func f) => new(w => Run().Map(aw => (aw.Value, w + f(aw.Output)))); /// /// Lifts a given monad into the transformer /// /// Monad to lift /// `WriterT` public static WriterT Lift(Pure monad) => Pure(monad.Value); /// /// Lifts a given monad into the transformer /// /// Monad to lift /// `WriterT` public static WriterT Lift(K monad) => new(w => M.Map(value => (value, w), monad)); /// /// Lifts a function into the transformer /// /// Function to lift /// `WriterT` public static WriterT Lift(Func f) => new(w => M.Pure((f(), w))); /// /// Lifts a an IO monad into the monad /// /// NOTE: If the IO monad isn't the innermost monad of the transformer /// stack then this will throw an exception. /// IO monad to lift /// `WriterT` public static WriterT LiftIO(IO ma) => Lift(M.LiftIOMaybe(ma)); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Map // /// /// Maps the given monad /// /// Mapping function /// Trait of the monad to map to /// `WriterT` public WriterT MapT(Func, K> f) where M1 : Monad, Choice => new (w => f(runWriter(w))); /// /// Maps the given monad /// /// Mapping function /// `StateT` public WriterT MapM(Func, K> f) => new(state => runWriter(state) .Bind(vs => f(M.Pure(vs.Value)).Map(x => (x, vs.Output)))); /// /// Maps the bound value /// /// Mapping function /// Target bound value type /// `WriterT` public WriterT Map(Func f) => new(w => M.Map(x => (f(x.Value), x.Output), runWriter(w))); /// /// Maps the bound value /// /// Mapping transducer /// Target bound value type /// `WriterT` public WriterT Select(Func f) => new(w => M.Map(x => (f(x.Value), x.Output), runWriter(w))); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Bind // /// /// Monad bind operation /// /// Mapping function /// Target bound value type /// `WriterT` public WriterT Bind(Func, B>> f) => Bind(x => f(x).As()); /// /// Monad bind operation /// /// Mapping function /// Target bound value type /// `WriterT` public WriterT Bind(Func> f) => new(w => M.Bind( runWriter(w), mx => f(mx.Value).runWriter(mx.Output))); /// /// Monad bind operation /// /// Mapping function /// Target bound value type /// `WriterT` public WriterT Bind(Func> f) => Bind(x => f(x).ToWriterT()); /// /// Monad bind operation /// /// Mapping function /// Target bound value type /// `WriterT` public WriterT Bind(Func> f) => Bind(x => WriterT.LiftIO(f(x))); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // SelectMany // /// /// Monad bind operation /// /// Monadic bind function /// Projection function /// Intermediate bound value type /// Target bound value type /// `WriterT` public WriterT SelectMany(Func, B>> bind, Func project) => SelectMany(x => bind(x).As(), project); /// /// Monad bind operation /// /// Monadic bind function /// Projection function /// Intermediate bound value type /// Target bound value type /// `WriterT` public WriterT SelectMany(Func> bind, Func project) => Bind(x => bind(x).Map(y => project(x, y))); /// /// Monad bind operation /// /// Monadic bind function /// Projection function /// Intermediate bound value type /// Target bound value type /// `WriterT` public WriterT SelectMany(Func> bind, Func project) => SelectMany(x => WriterT.Lift(bind(x)), project); /// /// Monad bind operation /// /// Monadic bind function /// Projection function /// Intermediate bound value type /// Target bound value type /// `WriterT` public WriterT SelectMany(Func> bind, Func project) => Map(x => project(x, bind(x).Value)); /// /// Monad bind operation /// /// Monadic bind function /// Projection function /// Intermediate bound value type /// Target bound value type /// `WriterT` public WriterT SelectMany(Func> bind, Func project) => SelectMany(x => bind(x).ToWriterT(), project); /// /// Monad bind operation /// /// Monadic bind function /// Projection function /// Intermediate bound value type /// Target bound value type /// `WriterT` public WriterT SelectMany(Func> bind, Func project) => Bind(x => bind(x).Map(y => project(x, y))); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Operators // /// /// Sequentially compose two actions, discarding any value produced by the first, like sequencing operators (such /// as the semicolon) in C#. /// /// First action to run /// Second action to run /// Result of the second action public static WriterT operator >> (WriterT lhs, WriterT rhs) => lhs.Bind(_ => rhs); /// /// Sequentially compose two actions, discarding any value produced by the first, like sequencing operators (such /// as the semicolon) in C#. /// /// First action to run /// Second action to run /// Result of the second action public static WriterT operator >> (WriterT lhs, K, A> rhs) => lhs.Bind(_ => rhs); /// /// Sequentially compose two actions. The second action is a unit returning action, so the result of the /// first action is propagated. /// /// First action to run /// Second action to run /// Result of the first action public static WriterT operator >> (WriterT lhs, WriterT rhs) => lhs.Bind(x => rhs.Map(_ => x)); /// /// Sequentially compose two actions. The second action is a unit returning action, so the result of the /// first action is propagated. /// /// First action to run /// Second action to run /// Result of the first action public static WriterT operator >> (WriterT lhs, K, Unit> rhs) => lhs.Bind(x => rhs.Map(_ => x)); public static implicit operator WriterT(Pure ma) => Pure(ma.Value); public static implicit operator WriterT(IO ma) => LiftIO(ma); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Run the monad // /// /// Run the writer /// /// Bound monad public K Run() => runWriter(W.Empty); } ================================================ FILE: LanguageExt.Core/Monads/Trampoline/Trampoline.cs ================================================ using System; using static LanguageExt.Prelude; namespace LanguageExt; public static class Trampoline { public static Trampoline Pure(A value) => new Trampoline.PureStep(value); public static Trampoline More(Func> value) => new Trampoline.MoreStep(value); public static Trampoline Bind(Trampoline ma, Func> f) => ma switch { Trampoline.BindStep self => self.Fix(f), _ => new Trampoline.BindStep(ma, f) }; } /// /// /// /// /// Based on: https://blog.higher-order.com/assets/trampolines.pdf /// /// public abstract record Trampoline { public A Run() { var f = () => this; while (true) { switch (f().Resume()) { case Either>, A>.Left (var nf): f = nf; break; case Either>, A>.Right (var value): return value; } } } public Trampoline Bind(Func> f) => Trampoline.Bind(this, f); public Trampoline Map(Func f) => Trampoline.Bind(this, x => Trampoline.Pure(f(x))); public Trampoline Select(Func f) => Trampoline.Bind(this, x => Trampoline.Pure(f(x))); public Trampoline SelectMany(Func> bind, Func project) => Bind(x => bind(x).Map(y => project(x, y))); protected abstract Either>, A> Resume(); internal record PureStep(A Result) : Trampoline { protected override Either>, A> Resume() => Result; } internal record MoreStep(Func> Next) : Trampoline { protected override Either>, A> Resume() => Next; } internal abstract record BindStep : Trampoline { public abstract Trampoline Fix(Func> f); } internal record BindStep(Trampoline Sub, Func> Next) : BindStep { public override Trampoline Fix(Func> f) => new Trampoline.BindStep(Sub, x => Trampoline.Bind(Next(x), f)); protected override Either>, A> Resume() => Sub switch { Trampoline.PureStep (var x) => Next(x).Resume(), Trampoline.MoreStep (var f) => Left(() => Trampoline.Bind(f(), Next)), Trampoline.BindStep bind => bind.Resume() switch { Either>, X>.Right (var x) => Left(() => Next(x)), Either>, X>.Left (var f) => Left(() => Trampoline.Bind(f(), Next)), _ => throw new NotSupportedException() }, _ => throw new NotSupportedException() }; } } ================================================ FILE: LanguageExt.Core/Number.cs ================================================ using System.Numerics; using LanguageExt.Traits; namespace LanguageExt; /// /// `Num〈A〉` trait for `INumber〈A〉` /// /// public class Number : Num where A : INumber { public static int GetHashCode(A x) => x.GetHashCode(); public static bool Equals(A x, A y) => x == y; public static int Compare(A x, A y) => x.CompareTo(y); public static A Add(A x, A y) => x + y; public static A Subtract(A x, A y) => x - y; public static A Multiply(A x, A y) => x * y; public static A Negate(A x) => -x; public static A Abs(A x) => x < A.Zero ? -x : x; public static A Signum(A x) => x < A.Zero ? -A.One : x > A.Zero ? A.One : A.Zero; public static A FromInteger(int x) => A.CreateChecked(x); public static A FromDecimal(decimal x) => A.CreateChecked(x); public static A FromFloat(float x) => A.CreateChecked(x); public static A FromDouble(double x) => A.CreateChecked(x); public static A Divide(A x, A y) => x / y; } ================================================ FILE: LanguageExt.Core/Obsolete and Deprecated/Change.cs ================================================ namespace LanguageExt; internal class Change { public const string UseCollectionIntialiser = "Use collection intialiser instead. So, instead of: (x, y, z), you should now call [x, y, z]"; public const string UseCollectionIntialiserSeq = "Use collection intialiser instead. So, instead of: Seq1(x), you should now call [x] - alternatively use Seq(x) as Seq1(x) has been deprecated."; public const string UseToArrayInstead = "Use ToArray() instead"; public const string UseToListInstead = "Use ToList() instead"; public const string UseToSeqInstead = "Use ToList() instead"; } ================================================ FILE: LanguageExt.Core/Obsolete and Deprecated/Fin.Prelude.cs ================================================ using System; using System.Diagnostics.Contracts; using LanguageExt.Common; namespace LanguageExt; public static partial class Prelude { /// /// Fin constructor /// Constructs a Fin in a success state /// /// Bound value type /// Success value /// A new Fin instance [Pure] [Obsolete("FinSucc has been deprecated in favour of `Fin.Succ` or `Prelude.Pure`")] public static Fin FinSucc(A value) => new Fin.Succ(value); /// /// Fin constructor /// Constructs a Fin in a failure state /// /// Bound value type /// Failure value /// A new Fin instance [Pure] [Obsolete("FinFail has been deprecated in favour of `Fin.Fail` or `Prelude.Fail`")] public static Fin FinFail(Error value) => new Fin.Fail(value); } ================================================ FILE: LanguageExt.Core/Opt.cs ================================================ using System.Runtime.CompilerServices; namespace LanguageExt { internal static class Opt { internal const MethodImplOptions Default = MethodImplOptions.AggressiveInlining; } } ================================================ FILE: LanguageExt.Core/Prelude/Currying and Partial Application/Prelude.Curry.cs ================================================ using System; using System.Diagnostics.Contracts; namespace LanguageExt; public static partial class Prelude { /// /// Curry the function 'f' provided. /// You can then partially apply by calling: /// /// var curried = curry(f); /// var r = curried(a)(b) /// /// [Pure] public static Func> curry(Func f) => a => b => f(a, b); /// /// Curry the function 'f' provided. /// You can then partially apply by calling: /// /// var curried = curry(f); /// var r = curried(a)(b)(c) /// /// [Pure] public static Func>> curry(Func f) => a => b => c => f(a, b, c); /// /// Curry the function 'f' provided. /// You can then partially apply by calling: /// /// var curried = curry(f); /// var r = curried(a)(b)(c)(d) /// /// [Pure] public static Func>>> curry(Func f) => a => b => c => d => f(a, b, c, d); /// /// Curry the function 'f' provided. /// You can then partially apply by calling: /// /// var curried = curry(f); /// var r = curried(a)(b)(c)(d)(e) /// /// [Pure] public static Func>>>> curry(Func f) => a => b => c => d => e => f(a, b, c, d, e); /// /// Curry the function 'func' provided. /// You can then partially apply by calling: /// /// var curried = curry(f); /// var r = curried(a)(b)(c)(d)(e)(f) /// /// [Pure] public static Func>>>>> curry(Func func) => a => b => c => d => e => f => func(a, b, c, d, e, f); /// /// Curry the function 'func' provided. /// You can then partially apply by calling: /// /// var curried = curry(f); /// var r = curried(a)(b)(c)(d)(e)(f)(g) /// /// [Pure] public static Func>>>>>> curry(Func func) => a => b => c => d => e => f => g => func(a, b, c, d, e, f, g); /// /// Curry the function 'func' provided. /// You can then partially apply by calling: /// /// var curried = curry(f); /// var r = curried(a)(b)(c)(d)(e)(f)(g)(h) /// /// [Pure] public static Func>>>>>>> curry(Func func) => a => b => c => d => e => f => g => h => func(a, b, c, d, e, f, g, h); /// /// Curry the function 'func' provided. /// You can then partially apply by calling: /// /// var curried = curry(f); /// var r = curried(a)(b)(c)(d)(e)(f)(g)(h)(i) /// /// [Pure] public static Func>>>>>>>> curry(Func func) => a => b => c => d => e => f => g => h => i => func(a, b, c, d, e, f, g, h, i); /// /// Curry the function 'func' provided. /// You can then partially apply by calling: /// /// var curried = curry(f); /// var r = curried(a)(b)(c)(d)(e)(f)(g)(h)(i)(j) /// /// [Pure] public static Func>>>>>>>>> curry(Func func) => a => b => c => d => e => f => g => h => i => j => func(a, b, c, d, e, f, g, h, i, j); } ================================================ FILE: LanguageExt.Core/Prelude/Currying and Partial Application/Prelude.PartialApplication.cs ================================================ using System; using System.Diagnostics.Contracts; namespace LanguageExt; public static partial class Prelude { /// /// Left partially apply /// [Pure] public static Func lpar(Func func, T2 b) => a => func(a, b); /// /// Partially apply /// [Pure] public static Func par(Func func, T1 a) => b => func(a, b); /// /// Partially apply /// [Pure] public static Func par(Func func, T1 a, T2 b) => c => func(a, b, c); /// /// Partially apply /// [Pure] public static Func par(Func func, T1 a) => (b, c) => func(a, b, c); /// /// Partially apply /// [Pure] public static Func par(Func func, T1 a, T2 b, T3 c) => d => func(a, b, c, d); /// /// Partially apply /// [Pure] public static Func par(Func func, T1 a, T2 b) => (c, d) => func(a, b, c, d); /// /// Partially apply /// [Pure] public static Func par(Func func, T1 a) => (b, c, d) => func(a, b, c, d); /// /// Partially apply /// [Pure] public static Func par(Func func, T1 a, T2 b, T3 c, T4 d) => e => func(a, b, c, d, e); /// /// Partially apply /// [Pure] public static Func par(Func func, T1 a, T2 b, T3 c) => (d, e) => func(a, b, c, d, e); /// /// Partially apply /// [Pure] public static Func par(Func func, T1 a, T2 b) => (c, d, e) => func(a, b, c, d, e); /// /// Partially apply /// [Pure] public static Func par(Func func, T1 a) => (b, c, d, e) => func(a, b, c, d, e); /// /// Partially apply /// [Pure] public static Func par(Func func, T1 a, T2 b, T3 c, T4 d, T5 e) => f => func(a, b, c, d, e, f); /// /// Partially apply /// [Pure] public static Func par(Func func, T1 a, T2 b, T3 c, T4 d) => (e, f) => func(a, b, c, d, e, f); /// /// Partially apply /// [Pure] public static Func par(Func func, T1 a, T2 b, T3 c) => (d, e, f) => func(a, b, c, d, e, f); /// /// Partially apply /// [Pure] public static Func par(Func func, T1 a, T2 b) => (c, d, e, f) => func(a, b, c, d, e, f); /// /// Partially apply /// [Pure] public static Func par(Func func, T1 a) => (b, c, d, e, f) => func(a, b, c, d, e, f); /// /// Partially apply /// [Pure] public static Func par(Func func, T1 a, T2 b, T3 c, T4 d, T5 e, T6 f) => g => func(a, b, c, d, e, f, g); /// /// Partially apply /// [Pure] public static Func par(Func func, T1 a, T2 b, T3 c, T4 d, T5 e) => (f, g) => func(a, b, c, d, e, f, g); /// /// Partially apply /// [Pure] public static Func par(Func func, T1 a, T2 b, T3 c, T4 d) => (e, f, g) => func(a, b, c, d, e, f, g); /// /// Partially apply /// [Pure] public static Func par(Func func, T1 a, T2 b, T3 c) => (d, e, f, g) => func(a, b, c, d, e, f, g); /// /// Partially apply /// [Pure] public static Func par(Func func, T1 a, T2 b) => (c, d, e, f, g) => func(a, b, c, d, e, f, g); /// /// Partially apply /// [Pure] public static Func par(Func func, T1 a) => (b, c, d, e, f, g) => func(a, b, c, d, e, f, g); /// /// Partially apply /// [Pure] public static Func par(Func func, T1 a) => (b, c, d, e, f, g, h) => func(a, b, c, d, e, f, g, h); /// /// Partially apply /// [Pure] public static Func par(Func func, T1 a, T2 b) => (c, d, e, f, g, h) => func(a, b, c, d, e, f, g, h); /// /// Partially apply /// [Pure] public static Func par(Func func, T1 a, T2 b, T3 c) => (d, e, f, g, h) => func(a, b, c, d, e, f, g, h); /// /// Partially apply /// [Pure] public static Func par(Func func, T1 a, T2 b, T3 c, T4 d) => (e, f, g, h) => func(a, b, c, d, e, f, g, h); /// /// Partially apply /// [Pure] public static Func par(Func func, T1 a, T2 b, T3 c, T4 d, T5 e) => (f, g, h) => func(a, b, c, d, e, f, g, h); /// /// Partially apply /// [Pure] public static Func par(Func func, T1 a, T2 b, T3 c, T4 d, T5 e, T6 f) => (g, h) => func(a, b, c, d, e, f, g, h); /// /// Partially apply /// [Pure] public static Func par(Func func, T1 a, T2 b, T3 c, T4 d, T5 e, T6 f, T7 g) => h => func(a, b, c, d, e, f, g, h); /// /// Partially apply /// [Pure] public static Func par(Func func, T1 a) => (b, c, d, e, f, g, h, i) => func(a, b, c, d, e, f, g, h, i); /// /// Partially apply /// [Pure] public static Func par(Func func, T1 a, T2 b) => (c, d, e, f, g, h, i) => func(a, b, c, d, e, f, g, h, i); /// /// Partially apply /// [Pure] public static Func par(Func func, T1 a, T2 b, T3 c) => (d, e, f, g, h, i) => func(a, b, c, d, e, f, g, h, i); /// /// Partially apply /// [Pure] public static Func par(Func func, T1 a, T2 b, T3 c, T4 d) => (e, f, g, h, i) => func(a, b, c, d, e, f, g, h, i); /// /// Partially apply /// [Pure] public static Func par(Func func, T1 a, T2 b, T3 c, T4 d, T5 e) => (f, g, h, i) => func(a, b, c, d, e, f, g, h, i); /// /// Partially apply /// [Pure] public static Func par(Func func, T1 a, T2 b, T3 c, T4 d, T5 e, T6 f) => (g, h, i) => func(a, b, c, d, e, f, g, h, i); /// /// Partially apply /// [Pure] public static Func par(Func func, T1 a, T2 b, T3 c, T4 d, T5 e, T6 f, T7 g) => (h, i) => func(a, b, c, d, e, f, g, h, i); /// /// Partially apply /// [Pure] public static Func par(Func func, T1 a, T2 b, T3 c, T4 d, T5 e, T6 f, T7 g, T8 h) => i => func(a, b, c, d, e, f, g, h, i); /// /// Partially apply /// [Pure] public static Func par(Func func, T1 a) => (b, c, d, e, f, g, h, i, j) => func(a, b, c, d, e, f, g, h, i, j); /// /// Partially apply /// [Pure] public static Func par(Func func, T1 a, T2 b) => (c, d, e, f, g, h, i, j) => func(a, b, c, d, e, f, g, h, i, j); /// /// Partially apply /// [Pure] public static Func par(Func func, T1 a, T2 b, T3 c) => (d, e, f, g, h, i, j) => func(a, b, c, d, e, f, g, h, i, j); /// /// Partially apply /// [Pure] public static Func par(Func func, T1 a, T2 b, T3 c, T4 d) => (e, f, g, h, i, j) => func(a, b, c, d, e, f, g, h, i, j); /// /// Partially apply /// [Pure] public static Func par(Func func, T1 a, T2 b, T3 c, T4 d, T5 e) => (f, g, h, i, j) => func(a, b, c, d, e, f, g, h, i, j); /// /// Partially apply /// [Pure] public static Func par(Func func, T1 a, T2 b, T3 c, T4 d, T5 e, T6 f) => (g, h, i, j) => func(a, b, c, d, e, f, g, h, i, j); /// /// Partially apply /// [Pure] public static Func par(Func func, T1 a, T2 b, T3 c, T4 d, T5 e, T6 f, T7 g) => (h, i, j) => func(a, b, c, d, e, f, g, h, i, j); /// /// Partially apply /// [Pure] public static Func par(Func func, T1 a, T2 b, T3 c, T4 d, T5 e, T6 f, T7 g, T8 h) => (i, j) => func(a, b, c, d, e, f, g, h, i, j); /// /// Partially apply /// [Pure] public static Func par(Func func, T1 a, T2 b, T3 c, T4 d, T5 e, T6 f, T7 g, T8 h, T9 i) => j => func(a, b, c, d, e, f, g, h, i, j); /// /// Partially apply /// [Pure] public static Action par(Action func, T1 a) => b => func(a, b); /// /// Partially apply /// [Pure] public static Action par(Action func, T1 a, T2 b) => c => func(a, b, c); /// /// Partially apply /// [Pure] public static Action par(Action func, T1 a) => (b, c) => func(a, b, c); /// /// Partially apply /// [Pure] public static Action par(Action func, T1 a, T2 b, T3 c) => d => func(a, b, c, d); /// /// Partially apply /// [Pure] public static Action par(Action func, T1 a, T2 b) => (c, d) => func(a, b, c, d); /// /// Partially apply /// [Pure] public static Action par(Action func, T1 a) => (b, c, d) => func(a, b, c, d); /// /// Partially apply /// [Pure] public static Action par(Action func, T1 a, T2 b, T3 c, T4 d) => e => func(a, b, c, d, e); /// /// Partially apply /// [Pure] public static Action par(Action func, T1 a, T2 b, T3 c) => (d, e) => func(a, b, c, d, e); /// /// Partially apply /// [Pure] public static Action par(Action func, T1 a, T2 b) => (c, d, e) => func(a, b, c, d, e); /// /// Partially apply /// [Pure] public static Action par(Action func, T1 a) => (b, c, d, e) => func(a, b, c, d, e); /// /// Partially apply /// [Pure] public static Action par(Action func, T1 a, T2 b, T3 c, T4 d, T5 e) => f => func(a, b, c, d, e, f); /// /// Partially apply /// [Pure] public static Action par(Action func, T1 a, T2 b, T3 c, T4 d) => (e, f) => func(a, b, c, d, e, f); /// /// Partially apply /// [Pure] public static Action par(Action func, T1 a, T2 b, T3 c) => (d, e, f) => func(a, b, c, d, e, f); /// /// Partially apply /// [Pure] public static Action par(Action func, T1 a, T2 b) => (c, d, e, f) => func(a, b, c, d, e, f); /// /// Partially apply /// [Pure] public static Action par(Action func, T1 a) => (b, c, d, e, f) => func(a, b, c, d, e, f); /// /// Partially apply /// [Pure] public static Action par(Action func, T1 a, T2 b, T3 c, T4 d, T5 e, T6 f) => g => func(a, b, c, d, e, f, g); /// /// Partially apply /// [Pure] public static Action par(Action func, T1 a, T2 b, T3 c, T4 d, T5 e) => (f, g) => func(a, b, c, d, e, f, g); /// /// Partially apply /// [Pure] public static Action par(Action func, T1 a, T2 b, T3 c, T4 d) => (e, f, g) => func(a, b, c, d, e, f, g); /// /// Partially apply /// [Pure] public static Action par(Action func, T1 a, T2 b, T3 c) => (d, e, f, g) => func(a, b, c, d, e, f, g); /// /// Partially apply /// [Pure] public static Action par(Action func, T1 a, T2 b) => (c, d, e, f, g) => func(a, b, c, d, e, f, g); /// /// Partially apply /// [Pure] public static Action par(Action func, T1 a) => (b, c, d, e, f, g) => func(a, b, c, d, e, f, g); /// /// Partially apply /// [Pure] public static Action par(Action func, T1 a) => (b, c, d, e, f, g, h) => func(a, b, c, d, e, f, g, h); /// /// Partially apply /// [Pure] public static Action par(Action func, T1 a, T2 b) => (c, d, e, f, g, h) => func(a, b, c, d, e, f, g, h); /// /// Partially apply /// [Pure] public static Action par(Action func, T1 a, T2 b, T3 c) => (d, e, f, g, h) => func(a, b, c, d, e, f, g, h); /// /// Partially apply /// [Pure] public static Action par(Action func, T1 a, T2 b, T3 c, T4 d) => (e, f, g, h) => func(a, b, c, d, e, f, g, h); /// /// Partially apply /// [Pure] public static Action par(Action func, T1 a, T2 b, T3 c, T4 d, T5 e) => (f, g, h) => func(a, b, c, d, e, f, g, h); /// /// Partially apply /// [Pure] public static Action par(Action func, T1 a, T2 b, T3 c, T4 d, T5 e, T6 f) => (g, h) => func(a, b, c, d, e, f, g, h); /// /// Partially apply /// [Pure] public static Action par(Action func, T1 a, T2 b, T3 c, T4 d, T5 e, T6 f, T7 g) => h => func(a, b, c, d, e, f, g, h); /// /// Partially apply /// [Pure] public static Action par(Action func, T1 a) => (b, c, d, e, f, g, h, i) => func(a, b, c, d, e, f, g, h, i); /// /// Partially apply /// [Pure] public static Action par(Action func, T1 a, T2 b) => (c, d, e, f, g, h, i) => func(a, b, c, d, e, f, g, h, i); /// /// Partially apply /// [Pure] public static Action par(Action func, T1 a, T2 b, T3 c) => (d, e, f, g, h, i) => func(a, b, c, d, e, f, g, h, i); /// /// Partially apply /// [Pure] public static Action par(Action func, T1 a, T2 b, T3 c, T4 d) => (e, f, g, h, i) => func(a, b, c, d, e, f, g, h, i); /// /// Partially apply /// [Pure] public static Action par(Action func, T1 a, T2 b, T3 c, T4 d, T5 e) => (f, g, h, i) => func(a, b, c, d, e, f, g, h, i); /// /// Partially apply /// [Pure] public static Action par(Action func, T1 a, T2 b, T3 c, T4 d, T5 e, T6 f) => (g, h, i) => func(a, b, c, d, e, f, g, h, i); /// /// Partially apply /// [Pure] public static Action par(Action func, T1 a, T2 b, T3 c, T4 d, T5 e, T6 f, T7 g) => (h, i) => func(a, b, c, d, e, f, g, h, i); /// /// Partially apply /// [Pure] public static Action par(Action func, T1 a, T2 b, T3 c, T4 d, T5 e, T6 f, T7 g, T8 h) => i => func(a, b, c, d, e, f, g, h, i); /// /// Partially apply /// [Pure] public static Action par(Action func, T1 a) => (b, c, d, e, f, g, h, i, j) => func(a, b, c, d, e, f, g, h, i, j); /// /// Partially apply /// [Pure] public static Action par(Action func, T1 a, T2 b) => (c, d, e, f, g, h, i, j) => func(a, b, c, d, e, f, g, h, i, j); /// /// Partially apply /// [Pure] public static Action par(Action func, T1 a, T2 b, T3 c) => (d, e, f, g, h, i, j) => func(a, b, c, d, e, f, g, h, i, j); /// /// Partially apply /// [Pure] public static Action par(Action func, T1 a, T2 b, T3 c, T4 d) => (e, f, g, h, i, j) => func(a, b, c, d, e, f, g, h, i, j); /// /// Partially apply /// [Pure] public static Action par(Action func, T1 a, T2 b, T3 c, T4 d, T5 e) => (f, g, h, i, j) => func(a, b, c, d, e, f, g, h, i, j); /// /// Partially apply /// [Pure] public static Action par(Action func, T1 a, T2 b, T3 c, T4 d, T5 e, T6 f) => (g, h, i, j) => func(a, b, c, d, e, f, g, h, i, j); /// /// Partially apply /// [Pure] public static Action par(Action func, T1 a, T2 b, T3 c, T4 d, T5 e, T6 f, T7 g) => (h, i, j) => func(a, b, c, d, e, f, g, h, i, j); /// /// Partially apply /// [Pure] public static Action par(Action func, T1 a, T2 b, T3 c, T4 d, T5 e, T6 f, T7 g, T8 h) => (i, j) => func(a, b, c, d, e, f, g, h, i, j); /// /// Partially apply /// [Pure] public static Action par(Action func, T1 a, T2 b, T3 c, T4 d, T5 e, T6 f, T7 g, T8 h, T9 i) => j => func(a, b, c, d, e, f, g, h, i, j); } ================================================ FILE: LanguageExt.Core/Prelude/Currying and Partial Application/Prelude.Uncurry.cs ================================================ using System; using System.Diagnostics.Contracts; namespace LanguageExt; public static partial class Prelude { /// /// Transforms a curried function into a function that takes multiple arguments /// [Pure] public static Func Uncurry(this Func> function) => (arg1, arg2) => function(arg1)(arg2); /// /// Transforms a curried function into a function that takes multiple arguments /// [Pure] public static Func Uncurry(this Func>> function) => (arg1, arg2, arg3) => function(arg1)(arg2)(arg3); /// /// Transforms a curried function into a function that takes multiple arguments /// [Pure] public static Func Uncurry(this Func>>> function) => (arg1, arg2, arg3, arg4) => function(arg1)(arg2)(arg3)(arg4); /// /// Transforms a curried function into a function that takes multiple arguments /// [Pure] public static Func Uncurry(this Func>>>> function) => (arg1, arg2, arg3, arg4, arg5) => function(arg1)(arg2)(arg3)(arg4)(arg5); /// /// Transforms a curried function into a function that takes multiple arguments /// [Pure] public static Func Uncurry(this Func>>>>> function) => (arg1, arg2, arg3, arg4, arg5, arg6) => function(arg1)(arg2)(arg3)(arg4)(arg5)(arg6); /// /// Transforms a curried function into a function that takes multiple arguments /// [Pure] public static Func Uncurry(this Func>>>>>> function) => (arg1, arg2, arg3, arg4, arg5, arg6, arg7) => function(arg1)(arg2)(arg3)(arg4)(arg5)(arg6)(arg7); /// /// Transforms a curried function into a function that takes multiple arguments /// [Pure] public static Func Uncurry(this Func>>>>>>> function) => (arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8) => function(arg1)(arg2)(arg3)(arg4)(arg5)(arg6)(arg7)(arg8); /// /// Transforms a curried function into a function that takes multiple arguments /// [Pure] public static Func uncurry(Func> function) => (arg1, arg2) => function(arg1)(arg2); /// /// Transforms a curried function into a function that takes multiple arguments /// [Pure] public static Func uncurry(Func>> function) => (arg1, arg2, arg3) => function(arg1)(arg2)(arg3); /// /// Transforms a curried function into a function that takes multiple arguments /// [Pure] public static Func uncurry(Func>>> function) => (arg1, arg2, arg3, arg4) => function(arg1)(arg2)(arg3)(arg4); /// /// Transforms a curried function into a function that takes multiple arguments /// [Pure] public static Func uncurry(Func>>>> function) => (arg1, arg2, arg3, arg4, arg5) => function(arg1)(arg2)(arg3)(arg4)(arg5); /// /// Transforms a curried function into a function that takes multiple arguments /// [Pure] public static Func uncurry(Func>>>>> function) => (arg1, arg2, arg3, arg4, arg5, arg6) => function(arg1)(arg2)(arg3)(arg4)(arg5)(arg6); /// /// Transforms a curried function into a function that takes multiple arguments /// [Pure] public static Func uncurry(Func>>>>>> function) => (arg1, arg2, arg3, arg4, arg5, arg6, arg7) => function(arg1)(arg2)(arg3)(arg4)(arg5)(arg6)(arg7); /// /// Transforms a curried function into a function that takes multiple arguments /// [Pure] public static Func uncurry(Func>>>>>>> function) => (arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8) => function(arg1)(arg2)(arg3)(arg4)(arg5)(arg6)(arg7)(arg8); } ================================================ FILE: LanguageExt.Core/Prelude/Function argument flipping/Prelude.Flip.cs ================================================ using System; using System.Diagnostics.Contracts; namespace LanguageExt; public static partial class Prelude { /// /// Reverse the order of the arguments to a curried function /// [Pure] public static Func> flip(Func> f) => b => a => f(a)(b); /// /// Reverse the order of the arguments to a curried function /// [Pure] public static Func>> flip(Func>> f) => c => b => a => f(a)(b)(c); /// /// Reverse the order of the arguments to a function /// [Pure] public static Func flip(Func f) => (b, a) => f(a, b); /// /// Reverse the order of the arguments to a function /// [Pure] public static Func flip(Func f) => (c, b, a) => f(a, b, c); /// /// Reverse the order of the arguments to a curried function /// [Pure] public static Func> Flip(this Func> f) => b => a => f(a)(b); /// /// Reverse the order of the arguments to a curried function /// [Pure] public static Func>> Flip(this Func>> f) => c => b => a => f(a)(b)(c); /// /// Reverse the order of the arguments to a function /// [Pure] public static Func Flip(this Func f) => (b, a) => f(a, b); /// /// Reverse the order of the arguments to a function /// [Pure] public static Func Flip(this Func f) => (c, b, a) => f(a, b, c); } ================================================ FILE: LanguageExt.Core/Prelude/Hash code functions/Prelude.Hash.cs ================================================ using System.Collections.Generic; using System.Diagnostics.Contracts; using System.Runtime.CompilerServices; using LanguageExt.ClassInstances; using LanguageExt.Traits; namespace LanguageExt; public static partial class Prelude { /// /// Calculate a hash-code for an enumerable /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int hash(IEnumerable xs) => FNV32.Hash, A>(xs); /// /// Calculate a hash-code for an enumerable by using the /// Hashable class-instance to calculate each item's hash /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int hash(IEnumerable xs) where HashA : Hashable => FNV32.Hash(xs); // // The following overloads are to avoid boxing when using the // hash function, perhaps as a first-class function for a map // /// /// Calculate a hash-code for the collection provided /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int hash(Arr xs) => xs.GetHashCode(); /// /// Calculate a hash-code for the collection provided /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int hash(HashMap xs) => xs.GetHashCode(); /// /// Calculate a hash-code for the collection provided /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int hash(HashMap xs) where EqK : Eq => xs.GetHashCode(); /// /// Calculate a hash-code for the collection provided /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int hash(HashSet xs) => xs.GetHashCode(); /// /// Calculate a hash-code for the collection provided /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int hash(HashSet xs) where EqA : Eq => xs.GetHashCode(); /// /// Calculate a hash-code for the collection provided /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int hash(Lst xs) => xs.GetHashCode(); /// /// Calculate a hash-code for the collection provided /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int hash(Map xs) => xs.GetHashCode(); /// /// Calculate a hash-code for the collection provided /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int hash(Map xs) where OrdK : Ord => xs.GetHashCode(); /// /// Calculate a hash-code for the collection provided /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int hash(Que xs) => xs.GetHashCode(); /// /// Calculate a hash-code for the collection provided /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int hash(Seq xs) => xs.GetHashCode(); /// /// Calculate a hash-code for the collection provided /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int hash(Set xs) => xs.GetHashCode(); /// /// Calculate a hash-code for the collection provided /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int hash(Set xs) where OrdA : Ord => xs.GetHashCode(); /// /// Calculate a hash-code for the collection provided /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int hash(Stck xs) => xs.GetHashCode(); } ================================================ FILE: LanguageExt.Core/Prelude/Lambda function inference/Prelude.Func.cs ================================================ using System; using System.Diagnostics.Contracts; using System.Linq.Expressions; namespace LanguageExt; public static partial class Prelude { /// /// Func type inference helper /// /// Try it with lambdas, instead of doing: /// /// Func〈int,int,int〉 add = (int x, int y) => x + y; /// /// You can use this function and do: /// /// var add = fun((int x, int y) => x + y); /// /// /// Function to infer /// The same func you gave it, but allows the type system to work out what f is [Pure] public static Func fun(Func f) => f; /// /// Func type inference helper /// /// Try it with lambdas, instead of doing: /// /// Func〈int,int,int〉 add = (int x, int y) => x + y; /// /// You can use this function and do: /// /// var add = fun((int x, int y) => x + y); /// /// /// Function to infer /// The same func you gave it, but allows the type system to work out what f is [Pure] public static Func fun(Func f) => f; /// /// Func type inference helper /// /// Try it with lambdas, instead of doing: /// /// Func〈int,int,int〉 add = (int x, int y) => x + y; /// /// You can use this function and do: /// /// var add = fun((int x, int y) => x + y); /// /// /// Function to infer /// The same func you gave it, but allows the type system to work out what f is [Pure] public static Func fun(Func f) => f; /// /// Func type inference helper /// /// Try it with lambdas, instead of doing: /// /// Func〈int,int,int〉 add = (int x, int y) => x + y; /// /// You can use this function and do: /// /// var add = fun((int x, int y) => x + y); /// /// /// Function to infer /// The same func you gave it, but allows the type system to work out what f is [Pure] public static Func fun(Func f) => f; /// /// Func type inference helper /// /// Try it with lambdas, instead of doing: /// /// Func〈int,int,int〉 add = (int x, int y) => x + y; /// /// You can use this function and do: /// /// var add = fun((int x, int y) => x + y); /// /// /// Function to infer /// The same func you gave it, but allows the type system to work out what f is [Pure] public static Func fun(Func f) => f; /// /// Func type inference helper /// /// Try it with lambdas, instead of doing: /// /// Func〈int,int,int〉 add = (int x, int y) => x + y; /// /// You can use this function and do: /// /// var add = fun((int x, int y) => x + y); /// /// /// Function to infer /// The same func you gave it, but allows the type system to work out what f is [Pure] public static Func fun(Func f) => f; /// /// Func type inference helper /// /// Try it with lambdas, instead of doing: /// /// Func〈int,int,int〉 add = (int x, int y) => x + y; /// /// You can use this function and do: /// /// var add = fun((int x, int y) => x + y); /// /// /// Function to infer /// The same func you gave it, but allows the type system to work out what f is [Pure] public static Func fun(Func f) => f; /// /// Func type inference helper /// /// Try it with lambdas, instead of doing: /// /// Func〈int,int,int〉 add = (int x, int y) => x + y; /// /// You can use this function and do: /// /// var add = fun((int x, int y) => x + y); /// /// /// Function to infer /// The same func you gave it, but allows the type system to work out what f is [Pure] public static Func fun(Func f) => f; /// /// Func type inference helper /// /// Try it with lambdas, instead of doing: /// /// Func〈int,int,int〉 add = (int x, int y) => x + y; /// /// You can use this function and do: /// /// var add = fun((int x, int y) => x + y); /// /// /// Function to infer /// The same func you gave it, but allows the type system to work out what f is [Pure] public static Func fun(Func f) => f; /// /// Func type inference helper /// /// Try it with lambdas, instead of doing: /// /// Func〈int,int,int〉 add = (int x, int y) => x + y; /// /// You can use this function and do: /// /// var add = fun((int x, int y) => x + y); /// /// /// Function to infer /// The same func you gave it, but allows the type system to work out what f is [Pure] public static Func fun(Func f) => f; /// /// Func type inference helper /// /// Try it with lambdas, instead of doing: /// /// Func〈int,int,int〉 add = (int x, int y) => x + y; /// /// You can use this function and do: /// /// var add = fun((int x, int y) => x + y); /// /// /// Function to infer /// The same func you gave it, but allows the type system to work out what f is [Pure] public static Func fun(Func f) => f; /// /// Func type inference helper /// /// Try it with lambdas, instead of doing: /// /// Func〈int,int,int〉 add = (int x, int y) => x + y; /// /// You can use this function and do: /// /// var add = fun((int x, int y) => x + y); /// /// /// Function to infer /// The same func you gave it, but allows the type system to work out what f is [Pure] public static Func fun(Func f) => f; /// /// Func type inference helper /// /// Try it with lambdas, instead of doing: /// /// Func〈int,int,int〉 add = (int x, int y) => x + y; /// /// You can use this function and do: /// /// var add = fun((int x, int y) => x + y); /// /// /// Function to infer /// The same func you gave it, but allows the type system to work out what f is [Pure] public static Func fun(Func f) => f; /// /// Func type inference helper /// /// Try it with lambdas, instead of doing: /// /// Func〈int,int,int〉 add = (int x, int y) => x + y; /// /// You can use this function and do: /// /// var add = fun((int x, int y) => x + y); /// /// /// Function to infer /// The same func you gave it, but allows the type system to work out what f is [Pure] public static Func fun(Func f) => f; /// /// Func type inference helper /// /// Try it with lambdas, instead of doing: /// /// Func〈int,int,int〉 add = (int x, int y) => x + y; /// /// You can use this function and do: /// /// var add = fun((int x, int y) => x + y); /// /// /// Function to infer /// The same func you gave it, but allows the type system to work out what f is [Pure] public static Func fun(Func f) => f; /// /// Func type inference helper /// /// Try it with lambdas, instead of doing: /// /// Func〈int,int,int〉 add = (int x, int y) => x + y; /// /// You can use this function and do: /// /// var add = fun((int x, int y) => x + y); /// /// /// Function to infer /// The same func you gave it, but allows the type system to work out what f is [Pure] public static Func fun(Func f) => f; /// /// Func type inference helper /// /// Try it with lambdas, instead of doing: /// /// Func〈int,int,int〉 add = (int x, int y) => x + y; /// /// You can use this function and do: /// /// var add = fun((int x, int y) => x + y); /// /// /// Function to infer /// The same func you gave it, but allows the type system to work out what f is [Pure] public static Func fun(Func f) => f; /// /// Action type inference helper and converts it to a Func that returns a Unit instead of void /// /// Try it with lambdas, instead of doing: /// /// Func〈string,Unit〉 putStr = (string x) => { Console.WriteLine(x); return unit; } /// /// You can use this function and do: /// /// var putStr = fun((string x) => Console.WriteLine(x) ); /// /// /// Function to infer /// Func that returns a Unit [Pure] public static Func fun(Action f) => () => { f(); return unit; }; /// /// Action type inference helper and converts it to a Func that returns a Unit instead of void /// /// Try it with lambdas, instead of doing: /// /// Func〈string,Unit〉 putStr = (string x) => { Console.WriteLine(x); return unit; } /// /// You can use this function and do: /// /// var putStr = fun((string x) => Console.WriteLine(x) ); /// /// /// Function to infer /// Func that returns a Unit [Pure] public static Func fun(Action f) => (a1) => { f(a1); return unit; }; /// /// Action type inference helper and converts it to a Func that returns a Unit instead of void /// /// Try it with lambdas, instead of doing: /// /// Func〈string,Unit〉 putStr = (string x) => { Console.WriteLine(x); return unit; } /// /// You can use this function and do: /// /// var putStr = fun((string x) => Console.WriteLine(x) ); /// /// /// Function to infer /// Func that returns a Unit [Pure] public static Func fun(Action f) => (a1, a2) => { f(a1, a2); return unit; }; /// /// Action type inference helper and converts it to a Func that returns a Unit instead of void /// /// Try it with lambdas, instead of doing: /// /// Func〈string,Unit〉 putStr = (string x) => { Console.WriteLine(x); return unit; } /// /// You can use this function and do: /// /// var putStr = fun((string x) => Console.WriteLine(x) ); /// /// /// Function to infer /// Func that returns a Unit [Pure] public static Func fun(Action f) => (a1, a2, a3) => { f(a1, a2, a3); return unit; }; /// /// Action type inference helper and converts it to a Func that returns a Unit instead of void /// /// Try it with lambdas, instead of doing: /// /// Func〈string,Unit〉 putStr = (string x) => { Console.WriteLine(x); return unit; } /// /// You can use this function and do: /// /// var putStr = fun((string x) => Console.WriteLine(x) ); /// /// /// Function to infer /// Func that returns a Unit [Pure] public static Func fun(Action f) => (a1, a2, a3, a4) => { f(a1, a2, a3, a4); return unit; }; /// /// Action type inference helper and converts it to a Func that returns a Unit instead of void /// /// Try it with lambdas, instead of doing: /// /// Func〈string,Unit〉 putStr = (string x) => { Console.WriteLine(x); return unit; } /// /// You can use this function and do: /// /// var putStr = fun((string x) => Console.WriteLine(x) ); /// /// /// Function to infer /// Func that returns a Unit [Pure] public static Func fun(Action f) => (a1, a2, a3, a4, a5) => { f(a1, a2, a3, a4, a5); return unit; }; /// /// Action type inference helper and converts it to a Func that returns a Unit instead of void /// /// Try it with lambdas, instead of doing: /// /// Func〈string,Unit〉 putStr = (string x) => { Console.WriteLine(x); return unit; } /// /// You can use this function and do: /// /// var putStr = fun((string x) => Console.WriteLine(x) ); /// /// /// Function to infer /// Func that returns a Unit [Pure] public static Func fun(Action f) => (a1, a2, a3, a4, a5, a6) => { f(a1, a2, a3, a4, a5, a6); return unit; }; /// /// Action type inference helper and converts it to a Func that returns a Unit instead of void /// /// Try it with lambdas, instead of doing: /// /// Func〈string,Unit〉 putStr = (string x) => { Console.WriteLine(x); return unit; } /// /// You can use this function and do: /// /// var putStr = fun((string x) => Console.WriteLine(x) ); /// /// /// Function to infer /// Func that returns a Unit [Pure] public static Func fun(Action f) => (a1, a2, a3, a4, a5, a6, a7) => { f(a1, a2, a3, a4, a5, a6, a7); return unit; }; /// /// Action type inference helper /// /// Try it with lambdas, instead of doing: /// /// Action〈string〉 putStr = (string x) => Console.WriteLine(x); /// /// You can use this function and do: /// /// var putStr = act((string x) => Console.WriteLine(x)); /// /// /// Action to infer /// The same Action you gave it, but allows the type system to work out what f is [Pure] public static Action act(Action f) => f; /// /// Action type inference helper /// /// Try it with lambdas, instead of doing: /// /// Action〈string〉 putStr = (string x) => Console.WriteLine(x); /// /// You can use this function and do: /// /// var putStr = act((string x) => Console.WriteLine(x)); /// /// /// Action to infer /// The same Action you gave it, but allows the type system to work out what f is [Pure] public static Action act(Action f) => f; /// /// Action type inference helper /// /// Try it with lambdas, instead of doing: /// /// Action〈string〉 putStr = (string x) => Console.WriteLine(x); /// /// You can use this function and do: /// /// var putStr = act((string x) => Console.WriteLine(x)); /// /// /// Action to infer /// The same Action you gave it, but allows the type system to work out what f is [Pure] public static Action act(Action f) => f; /// /// Action type inference helper /// /// Try it with lambdas, instead of doing: /// /// Action〈string〉 putStr = (string x) => Console.WriteLine(x); /// /// You can use this function and do: /// /// var putStr = act((string x) => Console.WriteLine(x)); /// /// /// Action to infer /// The same Action you gave it, but allows the type system to work out what f is [Pure] public static Action act(Action f) => f; /// /// Action type inference helper /// /// Try it with lambdas, instead of doing: /// /// Action〈string〉 putStr = (string x) => Console.WriteLine(x); /// /// You can use this function and do: /// /// var putStr = act((string x) => Console.WriteLine(x)); /// /// /// Action to infer /// The same Action you gave it, but allows the type system to work out what f is [Pure] public static Action act(Action f) => f; /// /// Action type inference helper /// /// Try it with lambdas, instead of doing: /// /// Action〈string〉 putStr = (string x) => Console.WriteLine(x); /// /// You can use this function and do: /// /// var putStr = act((string x) => Console.WriteLine(x)); /// /// /// Action to infer /// The same Action you gave it, but allows the type system to work out what f is [Pure] public static Action act(Action f) => f; /// /// Action type inference helper /// /// Try it with lambdas, instead of doing: /// /// Action〈string〉 putStr = (string x) => Console.WriteLine(x); /// /// You can use this function and do: /// /// var putStr = act((string x) => Console.WriteLine(x)); /// /// /// Action to infer /// The same Action you gave it, but allows the type system to work out what f is [Pure] public static Action act(Action f) => f; /// /// Action type inference helper /// /// Try it with lambdas, instead of doing: /// /// Action〈string〉 putStr = (string x) => Console.WriteLine(x); /// /// You can use this function and do: /// /// var putStr = act((string x) => Console.WriteLine(x)); /// /// /// Action to infer /// The same Action you gave it, but allows the type system to work out what f is [Pure] public static Action act(Action f) => f; /// /// Func type inference helper; converts it to an Action by dropping the return value /// /// Try it with lambdas, instead of doing: /// /// Func〈string, string〉 thereIs = ... /// /// Action〈string,Unit〉 thereIsNoReturn = (string x) => { thereis(x); }; /// /// You can use this function and do: /// /// var thereIsNoReturn = act(thereIs); /// /// /// Function to infer /// Action that is the same as the Func passed in, but with the return type dropped [Pure] public static Action act(Func f) => () => f(); /// /// Func type inference helper; converts it to an Action by dropping the return value /// /// Try it with lambdas, instead of doing: /// /// Func〈string, string〉 thereIs = ... /// /// Action〈string,Unit〉 thereIsNoReturn = (string x) => { thereis(x); }; /// /// You can use this function and do: /// /// var thereIsNoReturn = act(thereIs); /// /// /// Function to infer /// Action that is the same as the Func passed in, but with the return type dropped [Pure] public static Action act(Func f) => a1 => f(a1); /// /// Func type inference helper; converts it to an Action by dropping the return value /// /// Try it with lambdas, instead of doing: /// /// Func〈string, string〉 thereIs = ... /// /// Action〈string,Unit〉 thereIsNoReturn = (string x) => { thereis(x); }; /// /// You can use this function and do: /// /// var thereIsNoReturn = act(thereIs); /// /// /// Function to infer /// Action that is the same as the Func passed in, but with the return type dropped [Pure] public static Action act(Func f) => (a1, a2) => f(a1, a2); /// /// Func type inference helper; converts it to an Action by dropping the return value /// /// Try it with lambdas, instead of doing: /// /// Func〈string, string〉 thereIs = ... /// /// Action〈string,Unit〉 thereIsNoReturn = (string x) => { thereis(x); }; /// /// You can use this function and do: /// /// var thereIsNoReturn = act(thereIs); /// /// /// Function to infer /// Action that is the same as the Func passed in, but with the return type dropped [Pure] public static Action act(Func f) => (a1, a2, a3) => f(a1, a2, a3); /// /// Func type inference helper; converts it to an Action by dropping the return value /// /// Try it with lambdas, instead of doing: /// /// Func〈string, string〉 thereIs = ... /// /// Action〈string,Unit〉 thereIsNoReturn = (string x) => { thereis(x); }; /// /// You can use this function and do: /// /// var thereIsNoReturn = act(thereIs); /// /// /// Function to infer /// Action that is the same as the Func passed in, but with the return type dropped [Pure] public static Action act(Func f) => (a1, a2, a3, a4) => f(a1, a2, a3, a4); /// /// Func type inference helper; converts it to an Action by dropping the return value /// /// Try it with lambdas, instead of doing: /// /// Func〈string, string〉 thereIs = ... /// /// Action〈string,Unit〉 thereIsNoReturn = (string x) => { thereis(x); }; /// /// You can use this function and do: /// /// var thereIsNoReturn = act(thereIs); /// /// /// Function to infer /// Action that is the same as the Func passed in, but with the return type dropped [Pure] public static Action act(Func f) => (a1, a2, a3, a4, a5) => f(a1, a2, a3, a4, a5); /// /// Func type inference helper; converts it to an Action by dropping the return value /// /// Try it with lambdas, instead of doing: /// /// Func〈string, string〉 thereIs = ... /// /// Action〈string,Unit〉 thereIsNoReturn = (string x) => { thereis(x); }; /// /// You can use this function and do: /// /// var thereIsNoReturn = act(thereIs); /// /// /// Function to infer /// Action that is the same as the Func passed in, but with the return type dropped [Pure] public static Action act(Func f) => (a1, a2, a3, a4, a5, a6) => f(a1, a2, a3, a4, a5, a6); /// /// Func type inference helper; converts it to an Action by dropping the return value /// /// Try it with lambdas, instead of doing: /// /// Func〈string, string〉 thereIs = ... /// /// Action〈string,Unit〉 thereIsNoReturn = (string x) => { thereis(x); }; /// /// You can use this function and do: /// /// var thereIsNoReturn = act(thereIs); /// /// /// Function to infer /// Action that is the same as the Func passed in, but with the return type dropped [Pure] public static Action act(Func f) => (a1, a2, a3, a4, a5, a6, a7) => f(a1, a2, a3, a4, a5, a6, a7); /// /// Expression inference /// /// Same expression passed in, just gives the type system a chance to infer [Pure] public static Expression> expr(Expression> f) => f; /// /// Expression inference /// /// Same expression passed in, just gives the type system a chance to infer [Pure] public static Expression> expr(Expression> f) => f; /// /// Expression inference /// /// Same expression passed in, just gives the type system a chance to infer [Pure] public static Expression> expr(Expression> f) => f; /// /// Expression inference /// /// Same expression passed in, just gives the type system a chance to infer [Pure] public static Expression> expr(Expression> f) => f; /// /// Expression inference /// /// Same expression passed in, just gives the type system a chance to infer [Pure] public static Expression> expr(Expression> f) => f; /// /// Expression inference /// /// Same expression passed in, just gives the type system a chance to infer [Pure] public static Expression> expr(Expression> f) => f; /// /// Expression inference /// /// Same expression passed in, just gives the type system a chance to infer [Pure] public static Expression> expr(Expression> f) => f; /// /// Expression inference /// /// Same expression passed in, just gives the type system a chance to infer [Pure] public static Expression> expr(Expression> f) => f; /// /// Expression inference /// /// Same expression passed in, just gives the type system a chance to infer [Pure] public static Expression expr(Expression f) => f; /// /// Expression inference /// /// Same expression passed in, just gives the type system a chance to infer [Pure] public static Expression> expr(Expression> f) => f; /// /// Expression inference /// /// Same expression passed in, just gives the type system a chance to infer [Pure] public static Expression> expr(Expression> f) => f; /// /// Expression inference /// /// Same expression passed in, just gives the type system a chance to infer [Pure] public static Expression> expr(Expression> f) => f; /// /// Expression inference /// /// Same expression passed in, just gives the type system a chance to infer [Pure] public static Expression> expr(Expression> f) => f; /// /// Expression inference /// /// Same expression passed in, just gives the type system a chance to infer [Pure] public static Expression> expr(Expression> f) => f; /// /// Expression inference /// /// Same expression passed in, just gives the type system a chance to infer [Pure] public static Expression> expr(Expression> f) => f; /// /// Expression inference /// /// Same expression passed in, just gives the type system a chance to infer [Pure] public static Expression> expr(Expression> f) => f; /// /// Function composition /// /// b(a(())) [Pure] public static Func compose(Func a, Func b) => () => b(a()); /// /// Function composition /// /// b(a(v)) [Pure] public static Func compose(Func a, Func b) => v => b(a(v)); /// /// Function composition /// /// c(b(a(v))) [Pure] public static Func compose(Func a, Func b, Func c) => v => c(b(a(v))); /// /// Function composition /// /// c(b(a(v))) [Pure] public static Func compose(Func a, Func b, Func c, Func d) => v => d(c(b(a(v)))); /// /// Function composition /// /// c(b(a(v))) [Pure] public static Func compose(Func a, Func b, Func c, Func d, Func e) => v => e(d(c(b(a(v))))); /// /// Function composition /// /// c(b(a(v))) [Pure] public static Func compose(Func a, Func b, Func c, Func d, Func e, Func f) => v => f(e(d(c(b(a(v)))))); } ================================================ FILE: LanguageExt.Core/Prelude/Prelude.cs ================================================ using System; using System.Diagnostics.CodeAnalysis; using System.Diagnostics.Contracts; using System.Runtime.CompilerServices; using LanguageExt.ClassInstances; using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt; public static partial class Prelude { /// /// Identity function /// [Pure] public static A identity(A x) => x; /// /// Constant function /// Always returns the first argument /// [Pure] public static Func constant(A x) => _ => x; /// /// Constant function /// Always returns the first argument /// [Pure] public static A constant(A x, B _) => x; /// /// Constant function /// Always returns the first argument /// [Pure] public static Func constantA(A x) => _ => x; /// /// Constant function /// Always returns the first argument /// [Pure] public static A constantA(A x, A _) => x; /// /// Raises a lazy Exception with the message provided /// /// Exception message /// Action that when executed throws public static Action failwith(string message) => () => throw new Exception(message); /// /// Raises an Exception with the message provided /// /// The return type of the expression this function is being used in. /// This allows exceptions to be thrown in ternary operators, or LINQ expressions for /// example /// Exception message /// Throws an exception public static R failwith(string message) => throw new Exception(message); /// /// Raises an ApplicationException with the message provided /// /// The return type of the expression this function is being used in. /// This allows exceptions to be thrown in ternary operators, or LINQ expressions for /// example /// ApplicationException message /// Throws an ApplicationException public static R raiseapp(string message) => throw new ApplicationException(message); /// /// Raise an exception /// /// The return type of the expression this function is being used in. /// This allows exceptions to be thrown in ternary operators, or LINQ expressions for /// example /// Exception to throw /// Throws an exception public static R raise(Exception ex) => ex.Rethrow(); /// /// Identifies an exception as being of type E /// /// Type to match /// Exception to test /// True if e is of type E [Pure] public static bool exceptionIs(Exception e) { if (e is E) return true; if (e.InnerException == null) return false; return exceptionIs(e.InnerException); } /// /// Not function, for prettifying code and removing the need to /// use the ! operator. /// /// Predicate function to perform the not operation on /// !f public static Func not(Func f) => x => !f(x); /// /// Not function, for prettifying code and removing the need to /// use the ! operator. /// /// Predicate function to perform the not operation on /// !f public static Func not(Func f) => (x, y) => !f(x, y); /// /// Not function, for prettifying code and removing the need to /// use the ! operator. /// /// Predicate function to perform the not operation on /// !f public static Func not(Func f) => (x, y, z) => !f(x, y, z); /// /// Not function, for prettifying code and removing the need to /// use the ! operator. /// /// Value to perform the not operation on /// !value [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool not(bool value) => !value; /// /// Returns true if the value is equal to this type's /// default value. /// /// /// isDefault(0) // true /// isDefault(1) // false /// /// True if the value is equal to this type's /// default value [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] #nullable disable public static bool isDefault(A value) => Check.IsDefault(value); #nullable restore /// /// Returns true if the value is not equal to this type's /// default value. /// /// /// notDefault(0) // false /// notDefault(1) // true /// /// True if the value is not equal to this type's /// default value [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] #nullable disable public static bool notDefault(A value) => !Check.IsDefault(value); #nullable restore /// /// Returns true if the value is null, and does so without /// boxing of any value-types. Value-types will always /// return false. /// /// /// int x = 0; /// string y = null; /// /// isnull(x) // false /// isnull(y) // true /// /// True if the value is null, and does so without /// boxing of any value-types. Value-types will always /// return false. [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] #nullable disable public static bool isnull(A value) => Check.IsNull(value); #nullable restore /// /// Returns true if the value is not null, and does so without /// boxing of any value-types. Value-types will always return /// true. /// /// /// int x = 0; /// string y = null; /// string z = "Hello"; /// /// notnull(x) // true /// notnull(y) // false /// notnull(z) // true /// /// True if the value is null, and does so without /// boxing of any value-types. Value-types will always /// return false. [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] #nullable disable public static bool notnull(A value) => !Check.IsNull(value); #nullable restore /// /// Convert a value to string /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static string toString(A value) => value?.ToString() ?? ""; /// /// Returns true if the string is not null, nor empty, nor a whitespace /// /// String to test /// True if the string is null, empty, or whitespace [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool notEmpty(string? value) => !string.IsNullOrWhiteSpace(value); /// /// Returns true if the string is null, empty, or whitespace /// /// String to test /// True if the string is null, empty, or whitespace [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool isEmpty([NotNullWhen(false)] string? value) => string.IsNullOrWhiteSpace(value); [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static (A1, B) mapFirst(Func f, (A, B) pair) => (f(pair.Item1), pair.Item2); [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static (A, B1) mapSecond(Func f, (A, B) pair) => (pair.Item1, f(pair.Item2)); } ================================================ FILE: LanguageExt.Core/Prelude/README.md ================================================ `Prelude` is a `static partial class`, this type is loaded with functions for constructing the key data types, as well as many of the things you'd expect in a functional programming language's prelude. Note, the `Prelude` type extends into many other parts of the source-tree. It's the same type, but spread all over the code-base. And so, you may see `Prelude` in other areas of the documentation: it's the same type. Because it's so fundamental, you'll want to add this to the top of every code file: using static LanguageExt.Prelude; So what's in here? Well, apart from the modules listed below, there's the data-type constructors, for example: Option mx = Some(100); Seq mx = Seq(1, 2, 3); As well as the `camelCase` versions of the fluent-methods attached to each type: var items = Seq(1, 2, 3); // Fluent version var sum = items.Fold(0, (s, x) => s + x); // Prelude static function var sum = fold(items, 0, (s, x) => s + x); There is _mostly_ a 1-to-1 mapping between the fluent methods and the `Prelude` static functions ... mostly. ================================================ FILE: LanguageExt.Core/Prelude/Random/Prelude.Random.cs ================================================ using System; using System.Buffers; using System.Security.Cryptography; namespace LanguageExt; public static partial class Prelude { // There is no documentation that specifies whether the underlying RNG is thread-safe. // It is assumed that the implementation is `RNGCryptoServiceProvider`, which under the // hood calls `CryptGenRandom` from `advapi32` (i.e. a built-in Windows RNG). There is // no mention of thread-safety issues in any documentation, so we assume this must be // thread-safe. // // Documentation of `CrypGenRandom` // // https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-cryptgenrandom // // There is some discussion here: // // https://stackoverflow.com/questions/46147805/is-cryptgenrandom-thread-safe // static readonly RandomNumberGenerator rnd = RandomNumberGenerator.Create(); static readonly int wordTop = BitConverter.IsLittleEndian ? 3 : 0; /// /// Thread-safe cryptographically strong random number generator /// /// Maximum value to return + 1 /// A non-negative random number, less than the value specified. public static int random(int max) { var bytes = ArrayPool.Shared.Rent(4); rnd.GetBytes(bytes); bytes[wordTop] &= 0x7f; var value = BitConverter.ToInt32(bytes, 0) % max; ArrayPool.Shared.Return(bytes); return value; } /// /// Thread-safe cryptographically strong random base-64 string generator /// /// number of bytes generated that are then /// returned Base64 encoded /// Base64 encoded random string public static string randomBase64(int bytesCount) { if (bytesCount < 1) throw new ArgumentException($"The minimum value for {nameof(bytesCount)} is 1"); var bytes = ArrayPool.Shared.Rent(bytesCount); rnd.GetBytes(bytes); var r = Convert.ToBase64String(bytes); ArrayPool.Shared.Return(bytes); return r; } } ================================================ FILE: LanguageExt.Core/Prelude/Resources/Prelude.Resources.cs ================================================ using System; using System.Diagnostics.Contracts; using System.Runtime.CompilerServices; using LanguageExt.Common; using LanguageExt.DSL; using LanguageExt.Traits; namespace LanguageExt; public static partial class Prelude { /// /// Acquire a resource and have it tracked by the IO environment. The resource /// can be released manually using `release` or from wrapping a section of IO /// code with `bracketIO`. /// /// Computation that acquires the resource /// Action to release the resource /// Bound value type /// Acquired resource [Pure] [MethodImpl(Opt.Default)] public static K use(K acquire, Action release) where M : MonadUnliftIO => acquire.MapIO( acq => new IOUse( acq, x => IO.lift( () => { release(x); return unit; }), IO.pure)); /// /// Acquire a resource and have it tracked by the IO environment. The resource /// can be released manually using `release` or from wrapping a section of IO /// code with `bracketIO`. /// /// Computation that acquires the resource /// Action to release the resource /// Bound value type /// Acquired resource [Pure] [MethodImpl(Opt.Default)] public static IO use(IO acquire, Action release) => new IOUse( acquire, x => IO.lift( () => { release(x); return unit; }), IO.pure); /// /// Acquire a resource and have it tracked by the IO environment. The resource /// can be released manually using `release` or from wrapping a section of IO /// code with `@using`. /// /// Computation that acquires the resource /// Action to release the resource /// Bound value type /// Acquired resource [Pure] [MethodImpl(Opt.Default)] public static IO use(Func acquire, Action release) => use(IO.lift(acquire), release).As(); /// /// Acquire a resource and have it tracked by the IO environment. The resource /// can be released manually using `release` or from wrapping a section of IO /// code with `@using`. /// /// Computation that acquires the resource /// Action to release the resource /// Bound value type /// Acquired resource [Pure] [MethodImpl(Opt.Default)] public static IO use(Func acquire, Func> release) => use(IO.lift(acquire), release).As(); /// /// Acquire a resource and have it tracked by the IO environment. The resource /// can be released manually using `release` or from wrapping a section of IO /// code with `@using`. /// /// Computation that acquires the resource /// Action to release the resource /// Bound value type /// Acquired resource [Pure] [MethodImpl(Opt.Default)] public static K use(K acquire, Func> release) where M : MonadUnliftIO => acquire.MapIO(acq => new IOUse(acq, release, IO.pure)); /// /// Acquire a resource and have it tracked by the IO environment. The resource /// can be released manually using `release` or from wrapping a section of IO /// code with `@using`. /// /// Computation that acquires the resource /// Action to release the resource /// Bound value type /// Acquired resource [Pure] [MethodImpl(Opt.Default)] public static IO use(IO acquire, Func> release) => new IOUse(acquire, release, IO.pure); /// /// Acquire a resource and have it tracked by the IO environment. The resource /// can be released manually using `release` or from wrapping a section of IO /// code with `@using`. /// /// Computation that acquires the resource /// Bound value type /// Acquired resource [Pure] [MethodImpl(Opt.Default)] public static IO use(Func acquire) where A : IDisposable => use(IO.lift(acquire)).As(); /// /// Acquire a resource and have it tracked by the IO environment. The resource /// can be released manually using `release` or from wrapping a section of IO /// code with `@using`. /// /// Computation that acquires the resource /// Bound value type /// Acquired resource [Pure] [MethodImpl(Opt.Default)] public static IO use(Func acquire) where A : IDisposable => new IOUseDisposable(IO.lift(acquire), IO.pure); /// /// Acquire a resource and have it tracked by the IO environment. The resource /// can be released manually using `release` or from wrapping a section of IO /// code with `@using`. /// /// Computation that acquires the resource /// Bound value type /// Acquired resource [Pure] [MethodImpl(Opt.Default)] public static IO useAsync(Func acquire) where A : IAsyncDisposable => new IOUseAsyncDisposable(IO.lift(acquire), IO.pure); /// /// Acquire a resource and have it tracked by the IO environment. The resource /// can be released manually using `release` or from wrapping a section of IO /// code with `@using`. /// /// Computation that acquires the resource /// Bound value type /// Acquired resource [Pure] [MethodImpl(Opt.Default)] public static K use(K acquire) where M : MonadUnliftIO where A : IDisposable => acquire.MapIO(acq => new IOUseDisposable(acq, IO.pure)); /// /// Acquire a resource and have it tracked by the IO environment. The resource /// can be released manually using `release` or from wrapping a section of IO /// code with `@using`. /// /// Computation that acquires the resource /// Bound value type /// Acquired resource [Pure] [MethodImpl(Opt.Default)] public static K useMaybe(K acquire) where M : MonadIO where A : IDisposable => acquire.MapIOMaybe(acq => new IOUseDisposable(acq, IO.pure)); /// /// Acquire a resource and have it tracked by the IO environment. The resource /// can be released manually using `release` or from wrapping a section of IO /// code with `@using`. /// /// Computation that acquires the resource /// Bound value type /// Acquired resource [Pure] [MethodImpl(Opt.Default)] public static IO use(IO acquire) where A : IDisposable => new IOUseDisposable(acquire, IO.pure); /// /// Acquire a resource and have it tracked by the IO environment. The resource /// can be released manually using `release` or from wrapping a section of IO /// code with `@using`. /// /// Computation that acquires the resource /// Bound value type /// Acquired resource [Pure] [MethodImpl(Opt.Default)] public static K useAsync(K acquire) where M : MonadUnliftIO where A : IAsyncDisposable => acquire.MapIO(acq => new IOUseAsyncDisposable(acq, IO.pure)); /// /// Acquire a resource and have it tracked by the IO environment. The resource /// can be released manually using `release` or from wrapping a section of IO /// code with `@using`. /// /// Computation that acquires the resource /// Bound value type /// Acquired resource [Pure] [MethodImpl(Opt.Default)] public static IO useAsync(IO acquire) where A : IAsyncDisposable => new IOUseAsyncDisposable(acquire, IO.pure); /// /// Release the resource from the tracked IO environment /// /// Resource to release /// Bound value type /// Unit [Pure] [MethodImpl(Opt.Default)] public static IO release(A value) => IO.lift(env => env.Resources.Release(value).Run(env)); /// /// The IO monad tracks resources automatically; this creates a local resource environment /// to run the `computation` in. Once the computation is completed, any resources acquired /// are automatically released. Imagine this as the ultimate `using` statement. /// /// Computation to run in a local scope /// Bound value type /// Result of computation [Pure] [MethodImpl(Opt.Default)] public static K bracketIO(K computation) where M : MonadUnliftIO => computation.BracketIO(); /// /// The IO monad tracks resources automatically; this creates a local resource environment /// to run the `computation` in. Once the computation is completed, any resources acquired /// are automatically released. Imagine this as the ultimate `using` statement. /// /// Computation to run in a local scope /// Bound value type /// Result of computation [Pure] [MethodImpl(Opt.Default)] public static IO bracketIO(IO computation) where M : MonadUnliftIO => computation.Bracket(); /// /// When acquiring, using, and releasing various resources, it can be quite convenient to write a function to manage /// the acquisition and releasing, taking a function of the acquired value that specifies an action to be performed /// in between. /// /// /// Consider using `bracketIO(computation)` rather than `bracket(acq, use, fin)`, the semantics are more attractive /// as there's no need to provide function handlers, the cleanup is automatic. /// /// Acquire resource /// Function to use the acquired resource /// Function to invoke to release the resource [Pure] [MethodImpl(Opt.Default)] public static K bracketIO(K Acq, Func> Use, Func> Fin) where M : MonadUnliftIO => Acq.BracketIO(Use, Fin); /// /// When acquiring, using, and releasing various resources, it can be quite convenient to write a function to manage /// the acquisition and releasing, taking a function of the acquired value that specifies an action to be performed /// in between. /// /// /// Consider using `bracketIO(computation)` rather than `bracket(acq, use, fin)`, the semantics are more attractive /// as there's no need to provide function handlers, the cleanup is automatic. /// /// Acquire resource /// Function to use the acquired resource /// Function to invoke to release the resource [Pure] [MethodImpl(Opt.Default)] public static IO bracketIO(IO Acq, Func> Use, Func> Fin) where M : MonadUnliftIO => Acq.Bracket(Use, Fin); /// /// When acquiring, using, and releasing various resources, it can be quite convenient to write a function to manage /// the acquisition and releasing, taking a function of the acquired value that specifies an action to be performed /// in between. /// /// /// Consider using `bracketIO(computation)` rather than `bracket(acq, use, fin)`, the semantics are more attractive /// as there's no need to provide function handlers, the cleanup is automatic. /// /// Acquire resource /// Function to use the acquired resource /// Function called when an `Error` is raised within the `Acq` computation /// Function to invoke to release the resource [Pure] [MethodImpl(Opt.Default)] public static K bracketIO(K Acq, Func> Use, Func> Catch, Func> Fin) where M : MonadUnliftIO => Acq.BracketIO(Use, Catch, Fin); /// /// When acquiring, using, and releasing various resources, it can be quite convenient to write a function to manage /// the acquisition and releasing, taking a function of the acquired value that specifies an action to be performed /// in between. /// /// /// Consider using `bracketIO(computation)` rather than `bracket(acq, use, fin)`, the semantics are more attractive /// as there's no need to provide function handlers, the cleanup is automatic. /// /// Acquire resource /// Function to use the acquired resource /// Function called when an `Error` is raised within the `Acq` computation /// Function to invoke to release the resource [Pure] [MethodImpl(Opt.Default)] public static IO bracketIO(IO Acq, Func> Use, Func> Catch, Func> Fin) where M : MonadIO => Acq.Bracket(Use, Catch, Fin); } ================================================ FILE: LanguageExt.Core/Prelude/Timer/Prelude.Timer.cs ================================================ using System; using System.Threading.Tasks; namespace LanguageExt; public static partial class Prelude { /// /// Execute an action after a specified delay /// /// Action to execute /// Time span to delay for public static Unit delay(Action f, TimeSpan delayFor) { if (delayFor.TotalMilliseconds < 1) { f(); } else { Task.Delay(delayFor).ContinueWith(_ => f()); } return unit; } /// /// Execute a function at a specific time /// /// /// This will fail to be accurate across a Daylight Saving Time boundary /// /// Action to execute /// DateTime to wake up at. public static Unit delay(Action f, DateTime delayUntil) => delay(f, delayUntil.ToUniversalTime() - DateTime.UtcNow); } ================================================ FILE: LanguageExt.Core/Prelude/Value parsing/Prelude.Parse.cs ================================================ using System; using System.Diagnostics.CodeAnalysis; using System.Diagnostics.Contracts; using System.Net; using LanguageExt.Traits; namespace LanguageExt; public static partial class Prelude { [Pure] public static Option convert(object? value) { if (value == null) { return None; } try { var nvalue = (A)Convert.ChangeType(value, typeof(A)); return nvalue; } catch { return None; } } [Pure] public static K convert(object? value) where M : Alternative { if (value == null) { return M.Empty(); } try { var nvalue = (A)Convert.ChangeType(value, typeof(A)); return M.Pure(nvalue); } catch { return M.Empty(); } } [Pure] public static Option parseLong(string? value) => Parse(long.TryParse, value); [Pure] public static K parseLong(string? value) where M : Alternative => Parse(long.TryParse, value); [Pure] public static Option parseInt(string? value) => Parse(int.TryParse, value); [Pure] public static K parseInt(string? value) where M : Alternative => Parse(int.TryParse, value); [Pure] public static Option parseInt(string? value, int fromBase) { try { return Convert.ToInt32(value, fromBase); } catch { return None; } } [Pure] public static K parseInt(string? value, int fromBase) where M : Alternative { try { return M.Pure(Convert.ToInt32(value, fromBase)); } catch { return M.Empty(); } } [Pure] public static Option parseShort(string? value) => Parse(short.TryParse, value); [Pure] public static K parseShort(string? value) where M : Alternative => Parse(short.TryParse, value); [Pure] public static Option parseChar(string? value) => Parse(char.TryParse, value); [Pure] public static K parseChar(string? value) where M : Alternative => Parse(char.TryParse, value); [Pure] public static Option parseSByte(string? value) => Parse(sbyte.TryParse, value); [Pure] public static K parseSByte(string? value) where M : Alternative => Parse(sbyte.TryParse, value); [Pure] public static Option parseByte(string? value) => Parse(byte.TryParse, value); [Pure] public static K parseByte(string? value) where M : Alternative => Parse(byte.TryParse, value); [Pure] public static Option parseULong(string? value) => Parse(ulong.TryParse, value); [Pure] public static K parseULong(string? value) where M : Alternative => Parse(ulong.TryParse, value); [Pure] public static Option parseUInt(string? value) => Parse(uint.TryParse, value); [Pure] public static K parseUInt(string? value) where M : Alternative => Parse(uint.TryParse, value); [Pure] public static Option parseUShort(string? value) => Parse(ushort.TryParse, value); [Pure] public static K parseUShort(string? value) where M : Alternative => Parse(ushort.TryParse, value); [Pure] public static Option parseFloat(string? value) => Parse(float.TryParse, value); [Pure] public static K parseFloat(string? value) where M : Alternative => Parse(float.TryParse, value); [Pure] public static Option parseDouble(string? value) => Parse(double.TryParse, value); [Pure] public static K parseDouble(string? value) where M : Alternative => Parse(double.TryParse, value); [Pure] public static Option parseDecimal(string? value) => Parse(decimal.TryParse, value); [Pure] public static K parseDecimal(string? value) where M : Alternative => Parse(decimal.TryParse, value); [Pure] public static Option parseBool(string? value) => Parse(bool.TryParse, value); [Pure] public static K parseBool(string? value) where M : Alternative => Parse(bool.TryParse, value); [Pure] public static Option parseGuid(string? value) => Parse(Guid.TryParse, value); [Pure] public static K parseGuid(string? value) where M : Alternative => Parse(Guid.TryParse, value); [Pure] public static Option parseDateTime(string? value) => Parse(DateTime.TryParse, value); [Pure] public static K parseDateTime(string? value) where M : Alternative => Parse(DateTime.TryParse, value); [Pure] public static Option parseDateTimeOffset(string? value) => Parse(DateTimeOffset.TryParse, value); [Pure] public static K parseDateTimeOffset(string? value) where M : Alternative => Parse(DateTimeOffset.TryParse, value); [Pure] public static Option parseTimeSpan(string? value) => Parse(TimeSpan.TryParse, value); [Pure] public static K parseTimeSpan(string? value) where M : Alternative => Parse(TimeSpan.TryParse, value); [Pure] public static Option parseEnum(string? value) where TEnum : struct => Parse(Enum.TryParse, value); [Pure] public static K parseEnum(string? value) where TEnum : struct where M : Alternative => Parse(Enum.TryParse, value); [Pure] public static Option parseEnumIgnoreCase(string? value) where TEnum : struct => ParseIgnoreCase(Enum.TryParse, value); [Pure] public static K parseEnumIgnoreCase(string? value) where TEnum : struct where M : Alternative => ParseIgnoreCase(Enum.TryParse, value); [Pure] public static Option parseIPAddress(string? value) => Parse(IPAddress.TryParse, value); [Pure] public static K parseIPAddress(string? value) where M : Alternative => Parse(IPAddress.TryParse, value); delegate bool TryParse(string value, [NotNullWhen(true)] out T? result); delegate bool TryParseIgnoreCase(string value, bool ignoreCase, [NotNullWhen(true)] out T? result); static Option Parse(TryParse tryParse, string? value) => value is not null && tryParse(value, out var result) ? Some(result) : None; static K Parse(TryParse tryParse, string? value) where M : Alternative => value is not null && tryParse(value, out var result) ? M.Pure(result) : M.Empty(); static Option ParseIgnoreCase(TryParseIgnoreCase tryParse, string? value) => value is not null && tryParse(value, true, out var result) ? Some(result) : None; static K ParseIgnoreCase(TryParseIgnoreCase tryParse, string? value) where M : Alternative => value is not null && tryParse(value, true, out var result) ? M.Pure(result) : M.Empty(); } ================================================ FILE: LanguageExt.Core/Pretty/Doc.cs ================================================ using System; using System.Text; using LanguageExt; using LanguageExt.ClassInstances; using static LanguageExt.Prelude; namespace LanguageExt.Pretty { /// /// Document building functions /// /// /// No annotation (unit only), use `DocAnn` for annotated documents /// public static class Doc { public static readonly Doc Fail = DocAnn.Fail(); public static readonly Doc Empty = DocAnn.Empty(); public static Doc Char(char c) => DocAnn.Char(c); public static Doc Text(string text) => DocAnn.Text(text); public static Doc FlatAlt(Doc da, Doc db) => DocAnn.FlatAlt(da, db); public static Doc Union(Doc da, Doc db) => DocAnn.Union(da, db); public static Doc Cat(Doc da, Doc db) => DocAnn.Cat(da, db); public static Doc Nest(int indent, Doc doc) => DocAnn.Nest(indent, doc); public static Doc Column(Func> f) => DocAnn.Column(f); public static Doc PageWidth(Func> f) => DocAnn.PageWidth(f); public static Doc Nesting(Func> f) => DocAnn.Nesting(f); /// /// A hardline is always laid out as a line break, even when 'grouped or /// when there is plenty of space. Note that it might still be simply discarded /// if it is part of a 'FlatAlt' inside a 'Group'. /// public static readonly Doc HardLine = DocLine.Default; /// /// LineOrSpace is a line-break, but behaves like Char(' ') if the line break /// is undone by Group /// public static readonly Doc LineOrSpace = DocAnn.LineOrSpace(); /// /// LineOrEmpty is a line-break, but behaves like Empty if the line break /// is undone by Group /// public static readonly Doc LineOrEmpty = DocAnn.LineOrEmpty(); /// /// softline behaves like space if the resulting output fits the page, /// otherwise like line /// public static readonly Doc SoftLineOrSpace = DocAnn.SoftLineOrSpace(); /// /// softline behaves like Empty if the resulting output fits the page, /// otherwise like line /// public static readonly Doc SoftLineOrEmpty = DocAnn.SoftLineOrEmpty(); /// /// Group tries laying out doc into a single line by removing the /// contained line breaks; if this does not fit the page, or when a 'hardline' /// within doc prevents it from being flattened, doc is laid out without any /// changes. /// /// The 'group' function is key to layouts that adapt to available space nicely. /// public static Doc Group(Doc doc) => DocAnn.Group(doc); /// /// Align lays out the document with the nesting level set to the /// current column. It is used for example to implement 'hang'. /// /// As an example, we will put a document right above another one, regardless of /// the current nesting level. Without alignment, the second line is put simply /// below everything we've had so far, /// /// Text("lorem") + VertSep(["ipsum", "dolor"]) /// /// lorem ipsum /// dolor /// /// If we add an Align to the mix, the VertSep contents all start in the /// same column, /// /// >>> Text("lorem") + Align (VertSep(["ipsum", "dolor"])) /// lorem ipsum /// dolor /// public static Doc Align(Doc doc) => DocAnn.Align(doc); /// /// Hang lays out the document with a nesting level set to the /// /current column/ plus offset. Negative values are allowed, and decrease the /// nesting level accordingly. /// /// >>> var doc = Reflow("Indenting these words with hang") /// >>> PutDocW(24, ("prefix" + Hang(4, doc))) /// prefix Indenting these /// words with /// hang /// /// This differs from Nest, which is based on the /current nesting level/ plus /// offset. When you're not sure, try the more efficient 'nest' first. In our /// example, this would yield /// /// >>> var doc = Reflow("Indenting these words with nest") /// >>> PutDocW(24, "prefix" + Nest(4, doc)) /// prefix Indenting these /// words with nest /// public static Doc Hang(int offset, Doc doc) => DocAnn.Hang(offset, doc); /// /// Indents document `indent` columns, starting from the /// current cursor position. /// /// >>> var doc = Reflow("The indent function indents these words!") /// >>> PutDocW(24, ("prefix" + Indent(4, doc)) /// prefix The indent /// function /// indents these /// words! /// /// public static Doc Indent(int indent, Doc doc) => DocAnn.Indent(indent, doc); /// /// Delimit and intersperse the documents with a separator /// public static Doc EncloseSep(Doc leftDelim, Doc rightDelim, Doc sep, Seq> docs) => DocAnn.BetweenSep(leftDelim, rightDelim, sep, docs); /// /// Delimit and intersperse the documents with a separator /// public static Doc EncloseSep(Doc leftDelim, Doc rightDelim, Doc sep, params Doc[] docs) => DocAnn.BetweenSep(leftDelim, rightDelim, sep, docs); /// /// Haskell-inspired array/list formatting /// public static Doc List(Seq> docs) => DocAnn.List(docs); /// /// Haskell-inspired tuple formatting /// public static Doc Tuple(Seq> docs) => DocAnn.Tuple(docs); /// /// Haskell-inspired array/list formatting /// public static Doc List(params Doc[] docs) => DocAnn.List(docs); /// /// Haskell-inspired tuple formatting /// public static Doc Tuple(params Doc[] docs) => DocAnn.Tuple(docs); /// /// Insert a number of spaces. Negative values count as 0. /// public static Doc Spaces(int n) => DocAnn.Spaces(n); /// /// HorizSep concatenates all documents horizontally with `+` /// i.e. it puts a space between all entries. /// /// HorizSep does not introduce line breaks on its own, even when the page is too /// narrow: /// /// For automatic line breaks, consider using 'fillSep' instead. /// public static Doc HorizSep(Seq> docs) => DocAnn.HorizSep(docs); /// /// VertSep concatenates all documents above each other. If a /// Group undoes the line breaks inserted by VertSep, the documents are /// separated with a 'space' instead. /// /// Grouping a VertSep separates the documents with a 'space' if it fits the /// page (and does nothing otherwise). See the Sep convenience function for /// this use case. /// /// The Align function can be used to align the documents under their first /// element: /// /// Since Grouping a VertSep is rather common, Sep is a built-in for doing /// that. /// public static Doc VertSep(Seq> docs) => DocAnn.VertSep(docs); /// /// Intersperse the documents with a separator /// public static Doc Sep(Doc sep, Seq> docs) => DocAnn.Sep(sep, docs); /// /// Sep tries laying out the documents separated with 'space's, /// and if this does not fit the page, separates them with newlines. This is what /// differentiates it from VerSep, which always lays out its contents beneath /// each other. /// public static Doc Sep(Seq> docs) => DocAnn.Sep(docs); /// /// FillSep concatenates the documents horizontally with `+` (inserting a space) /// as long as it fits the page, then inserts a Line and continues doing that /// for all documents in (Line means that if Grouped, the documents /// are separated with a Space instead of newlines. Use 'FillCat' if you do not /// want a 'space'.) /// public static Doc FillSep(Seq> docs) => DocAnn.FillSep(docs); /// /// Hard line separator /// public static Doc HardSep(Seq> docs) => DocAnn.HardSep(docs); /// /// HorizCat concatenates all documents horizontally with | /// (i.e. without any spacing). /// /// It is provided only for consistency, since it is identical to 'Cat'. /// public static Doc HorizCat(Seq> docs) => DocAnn.HorizSep(docs); /// /// VertCat vertically concatenates the documents. If it is /// Grouped, the line breaks are removed. /// /// In other words VertCat is like VertSep, with newlines removed instead of /// replaced by spaces. /// public static Doc VertCat(Seq> docs) => DocAnn.VertSep(docs); /// /// Width lays out the document 'doc', and makes the column width /// of it available to a function. /// public static Doc Width(Doc doc, Func> f) => DocAnn.Width(doc, f); /// /// Fill lays out the document. It then appends spaces until /// the width is equal to `width`. If the width is already larger, nothing is /// appended. /// public static Doc Fill(int width, Doc doc) => DocAnn.Fill(width, doc); /// /// Enclose /// public static Doc Between(Doc left, Doc right, Doc middle) => DocAnn.Between(left, middle, right); } } ================================================ FILE: LanguageExt.Core/Pretty/DocAnn.cs ================================================ using System; using System.Text; using static LanguageExt.Prelude; namespace LanguageExt.Pretty; /// /// Document building functions /// /// /// Carries annotation, for unit only annotations, use `Doc` /// public static class DocAnn { public static Doc Annotate(A annotation, Doc doc) => new DocAnnotate(annotation, doc); public static Doc Fail() => DocFail.Default; public static Doc Empty() => DocEmpty.Default; public static Doc Char(char c) => c == '\n' ? LineOrSpace() : new DocChar(c); public static Doc Text(string text) => string.IsNullOrEmpty(text) ? DocEmpty.Default : text.Length == 1 ? Char(text[0]) : text.Contains("\n") ? text.Split('\n') .AsIterable() .Map(Text) .Intersperse(DocLine.Default) .Reduce(Cat) : new DocText(text); public static Doc FlatAlt(Doc da, Doc db) => new DocFlatAlt(da, db); public static Doc Union(Doc da, Doc db) => new DocFlatAlt(da, db); public static Doc Cat(Doc da, Doc db) => (da, db) switch { (DocEmpty, DocEmpty) => da, (DocEmpty, _) => db, (_, DocEmpty) => da, _ => new DocCat(da, db) }; public static Doc Nest(int indent, Doc doc) => indent == 0 ? doc : new DocNest(indent, doc); public static Doc Column(Func> f) => new DocColumn(f); public static Doc PageWidth(Func> f) => new DocPageWidth(f); public static Doc Nesting(Func> f) => new DocNesting(f); /// /// A hardline is always laid out as a line break, even when 'grouped or /// when there is plenty of space. Note that it might still be simply discarded /// if it is part of a 'FlatAlt' inside a 'Group'. /// public static Doc HardLine() => DocLine.Default; /// /// LineOrSpace is a line-break, but behaves like space if the line break /// is undone by Group /// public static Doc LineOrSpace() => FlatAlt(DocLine.Default, Char(' ')); /// /// LineOrEmpty is a line-break, but behaves like Empty if the line break /// is undone by Group /// public static Doc LineOrEmpty() => FlatAlt(DocLine.Default, Empty()); /// /// softline behaves like space if the resulting output fits the page, /// otherwise like line /// public static Doc SoftLineOrSpace() => Union(Char(' '), DocLine.Default); /// /// softline behaves like Empty if the resulting output fits the page, /// otherwise like line /// public static Doc SoftLineOrEmpty() => Union(Empty(), DocLine.Default); /// /// Group tries laying out doc into a single line by removing the /// contained line breaks; if this does not fit the page, or when a 'hardline' /// within doc prevents it from being flattened, doc is laid out without any /// changes. /// /// The 'group' function is key to layouts that adapt to available space nicely. /// public static Doc Group(Doc doc) => doc switch { DocUnion _ => doc, DocFlatAlt (var a, var b) => b.ChangesUponFlattening() switch { Flattened> (var b1) => Union(b1, a), AlreadyFlat> => Union(b, a), _ => a }, _ => doc.ChangesUponFlattening() switch { Flattened> (var x1) => Union(x1, doc), _ => doc }, }; /// /// Align lays out the document with the nesting level set to the /// current column. It is used for example to implement 'hang'. /// /// As an example, we will put a document right above another one, regardless of /// the current nesting level. Without alignment, the second line is put simply /// below everything we've had so far, /// /// Text("lorem") + VertSep(["ipsum", "dolor"]) /// /// lorem ipsum /// dolor /// /// If we add an 'Align' to the mix, the VertSep's contents all start in the /// same column, /// /// >>> Text("lorem") + Align (VertSep(["ipsum", "dolor"])) /// lorem ipsum /// dolor /// public static Doc Align(Doc doc) => Column(k => Nesting(i => Nest(k - i, doc))); /// /// Hang lays out the document with a nesting level set to the /// /current column/ plus offset. Negative values are allowed, and decrease the /// nesting level accordingly. /// /// >>> var doc = Reflow("Indenting these words with hang") /// >>> PutDocW(24, ("prefix" + Hang(4, doc))) /// prefix Indenting these /// words with /// hang /// /// This differs from Nest, which is based on the /current nesting level/ plus /// offset. When you're not sure, try the more efficient 'nest' first. In our /// example, this would yield /// /// >>> var doc = Reflow("Indenting these words with nest") /// >>> PutDocW(24, "prefix" + Nest(4, doc)) /// prefix Indenting these /// words with nest /// public static Doc Hang(int offset, Doc doc) => Align(Nest(offset, doc)); /// /// Indents document `indent` columns, starting from the /// current cursor position. /// /// >>> var doc = Reflow("The indent function indents these words!") /// >>> PutDocW(24, ("prefix" + Indent(4, doc)) /// prefix The indent /// function /// indents these /// words! /// /// public static Doc Indent(int indent, Doc doc) => Hang(indent, Spaces(indent) | doc); /// /// Intersperse the documents with a separator /// public static Doc Sep(Doc sep, Seq> docs) { if (docs.IsEmpty) return Empty(); var d = docs.Head.Value!; foreach (var doc in docs.Tail) { d = d | sep | doc; } return d; } /// /// Delimit and intersperse the documents with a separator /// public static Doc BetweenSep(Doc leftDelim, Doc rightDelim, Doc sep, Seq> docs) => leftDelim | Sep(sep, docs) | rightDelim; /// /// Delimit and intersperse the documents with a separator /// public static Doc BetweenSep(Doc leftDelim, Doc rightDelim, Doc sep, params Doc[] docs) => BetweenSep(leftDelim, rightDelim, sep, toSeq(docs)); /// /// Haskell-inspired array/list formatting /// public static Doc List(Seq> docs) => Group(BetweenSep( FlatAlt("[ ", "["), FlatAlt(" ]", "]"), ", ", docs)); /// /// Haskell-inspired tuple formatting /// public static Doc Tuple(Seq> docs) => Group(BetweenSep( FlatAlt("( ", "("), FlatAlt(" )", ")"), ", ", docs)); /// /// Haskell-inspired array/list formatting /// public static Doc List(params Doc[] docs) => Group(BetweenSep( FlatAlt("[ ", "["), FlatAlt(" ]", "]"), ", ", docs)); /// /// Haskell-inspired tuple formatting /// public static Doc Tuple(params Doc[] docs) => Group(BetweenSep( FlatAlt("( ", "("), FlatAlt(" )", ")"), ", ", docs)); /// /// Insert a number of spaces. Negative values count as 0. /// public static Doc Spaces(int n) { return (n < 0 ? 0 : n) switch { 0 => Empty(), 1 => Char(' '), 2 => Text(" "), 3 => Text(" "), 4 => Text(" "), 5 => Text(" "), 6 => Text(" "), 7 => Text(" "), 8 => Text(" "), _ => Text(FastSpace(n - 8)) }; string FastSpace(int x) { var sb = new StringBuilder(); sb.Append(" "); for (var i = 0; i < x; i++) { sb.Append(' '); } return sb.ToString(); } } /// /// HorizSep concatenates all documents horizontally with `+` /// i.e. it puts a space between all entries. /// /// HorizSep does not introduce line breaks on its own, even when the page is too /// narrow: /// /// For automatic line breaks, consider using 'fillSep' instead. /// public static Doc HorizSep(Seq> docs) => docs.IsEmpty ? DocEmpty.Default : docs.Tail.Fold(docs.Head.Value!, (s, d) => s + d); /// /// VertSep concatenates all documents above each other. If a /// Group undoes the line breaks inserted by VertSep, the documents are /// separated with a 'space' instead. /// /// Grouping a VertSep separates the documents with a 'space' if it fits the /// page (and does nothing otherwise). See the Sep convenience function for /// this use case. /// /// The Align function can be used to align the documents under their first /// element: /// /// Since Grouping a VertSep is rather common, Sep is a built-in for doing /// that. /// public static Doc VertSep(Seq> docs) => docs.IsEmpty ? DocEmpty.Default : docs.Tail.Fold(docs.Head.Value!, (s, d) => s | LineOrSpace() | d); /// /// Sep tries laying out the documents separated with 'space's, /// and if this does not fit the page, separates them with newlines. This is what /// differentiates it from VerSep, which always lays out its contents beneath /// each other. /// public static Doc Sep(Seq> docs) => Group(VertSep(docs)); /// /// FillSep concatenates the documents horizontally with `+` (inserting a space) /// as long as it fits the page, then inserts a Line and continues doing that /// for all documents in (Line means that if Grouped, the documents /// are separated with a Space instead of newlines. Use 'FillCat' if you do not /// want a 'space'.) /// public static Doc FillSep(Seq> docs) => docs.IsEmpty ? DocEmpty.Default : docs.Tail.Fold(docs.Head.Value!, (s, d) => s | SoftLineOrSpace() | d); /// /// Hard line separator /// public static Doc HardSep(Seq> docs) => docs.IsEmpty ? DocEmpty.Default : docs.Tail.Fold(docs.Head.Value!, (s, d) => (s, d) switch { (DocLine, DocLine) => s, (DocLine, _) => d, (_, DocLine) => s, _ => s | HardLine() | d }); /// /// HorizCat concatenates all documents horizontally with | /// (i.e. without any spacing). /// /// It is provided only for consistency, since it is identical to 'Cat'. /// public static Doc HorizCat(Seq> docs) => docs.IsEmpty ? DocEmpty.Default : docs.Tail.Fold(docs.Head.Value!, (s, d) => s | d); /// /// VertCat vertically concatenates the documents. If it is /// Grouped, the line breaks are removed. /// /// In other words VertCat is like VertSep, with newlines removed instead of /// replaced by spaces. /// public static Doc VertCat(Seq> docs) => docs.IsEmpty ? DocEmpty.Default : docs.Tail.Fold(docs.Head.Value!, (s, d) => s | LineOrEmpty() | d); /// /// Width lays out the document 'doc', and makes the column width /// of it available to a function. /// public static Doc Width(Doc doc, Func> f) => Column(colStart => doc | Column(colEnd => f(colEnd - colStart))); /// /// Fill lays out the document. It then appends spaces until /// the width is equal to `width`. If the width is already larger, nothing is /// appended. /// public static Doc Fill(int width, Doc doc) => Width(doc, w => Spaces(width - w)); /// /// Enclose /// public static Doc Between(Doc left, Doc middle, Doc right) => left | middle | right; } ================================================ FILE: LanguageExt.Core/Pretty/DocStream.cs ================================================ using System; using System.Text; using LanguageExt; using LanguageExt.ClassInstances; using static LanguageExt.Prelude; namespace LanguageExt.Pretty { public abstract record DocStream { public DocStream Select(Func f) => ReAnnotate(f); public DocStream Map(Func f) => ReAnnotate(f); public abstract DocStream ReAnnotate(Func f); public string Show() { var doc = this; var sb = new StringBuilder(); while (true) { switch (doc) { case SFail: sb.Append("[FAIL]"); return sb.ToString();//throw new Exception("Doc Fail"); case SEmpty: return sb.ToString(); case SChar (var c, var next): sb.Append(c); doc = next; break; case SText (var t, var next): sb.Append(t); doc = next; break; case SLine(var i, var next): sb.AppendLine(); sb.Append(FastSpace.Show(i)); doc = next; break; case SAnnPush(var _, var next): doc = next; break; case SAnnPop(var next): doc = next; break; } } } } public record SFail : DocStream { public static readonly DocStream Default = new SFail(); public override DocStream ReAnnotate(Func f) => SFail.Default; } public record SEmpty : DocStream { public static readonly DocStream Default = new SEmpty(); public override DocStream ReAnnotate(Func f) => SEmpty.Default; } public record SChar(char Value, DocStream Next) : DocStream { public override DocStream ReAnnotate(Func f) => new SChar(Value, Next.ReAnnotate(f)); } public record SText(string Value, DocStream Next) : DocStream { public override DocStream ReAnnotate(Func f) => new SText(Value, Next.ReAnnotate(f)); } public record SLine(int Indent, DocStream Next) : DocStream { public override DocStream ReAnnotate(Func f) => new SLine(Indent, Next.ReAnnotate(f)); } public record SAnnPush(A Ann, DocStream Next) : DocStream { public override DocStream ReAnnotate(Func f) => new SAnnPush(f(Ann), Next.ReAnnotate(f)); } public record SAnnPop(DocStream Next) : DocStream { public override DocStream ReAnnotate(Func f) => new SAnnPop(Next.ReAnnotate(f)); } } ================================================ FILE: LanguageExt.Core/Pretty/Doc_A.cs ================================================ using System; using System.Text; using LanguageExt; using LanguageExt.ClassInstances; using static LanguageExt.Prelude; namespace LanguageExt.Pretty { /// /// Base document record /// /// /// Construct documents using the Doc and DocAnn static types /// public abstract record Doc { public virtual Doc Append(Doc d) => DocAnn.Cat(this, d); public readonly Doc Empty = DocEmpty.Default; public static Doc operator +(Doc x, Doc y) => x | DocAnn.Char(' ') | y; public static Doc operator +(string x, Doc y) => x | DocAnn.Char(' ') | y; public static Doc operator +(Doc x, string y) => x | DocAnn.Char(' ') | y; public static Doc operator |(Doc x, Doc y) => x.Append(y); public static Doc operator |(string x, Doc y) => DocAnn.Text(x).Append(y); public static Doc operator |(Doc x, string y) => x.Append(DocAnn.Text(y)); public Doc Select(Func f) => ReAnnotate(f); public Doc Map(Func f) => ReAnnotate(f); public abstract Doc ReAnnotate(Func f); public abstract FlattenResult> ChangesUponFlattening(); public abstract Doc Flatten(); public static implicit operator Doc(string x) => DocAnn.Text(x); public string Show() => Layout.smart(LayoutOptions.Default, this).Show(); } public record DocFail : Doc { public readonly static Doc Default = new DocFail(); public override Doc ReAnnotate(Func f) => DocFail.Default; public override FlattenResult> ChangesUponFlattening() => NeverFlat>.Default; public override Doc Flatten() => this; } public record DocEmpty : Doc { public readonly static Doc Default = new DocEmpty(); public override Doc ReAnnotate(Func f) => DocEmpty.Default; public override FlattenResult> ChangesUponFlattening() => AlreadyFlat>.Default; public override Doc Flatten() => this; public override Doc Append(Doc d) => d switch { DocText(var ts) => d, DocChar(var c) => d, DocEmpty => this, _ => base.Append(d) }; } /// /// Invariant: not '\n' /// /// public record DocChar(char Value) : Doc { public override Doc ReAnnotate(Func f) => DocAnn.Char(Value); public override FlattenResult> ChangesUponFlattening() => AlreadyFlat>.Default; public override Doc Flatten() => this; } /// /// At least two characters long, does not contain '\n'. For /// empty documents, there is `DocEmpty`; for singleton documents, there is /// `DocChar`; newlines should be replaced by e.g. `DocLine`. /// /// public record DocText(string Text) : Doc { public override Doc ReAnnotate(Func f) => DocAnn.Text(Text); public override FlattenResult> ChangesUponFlattening() => AlreadyFlat>.Default; public override Doc Flatten() => this; public int Length => Text.Length; } /// /// Hard line break /// public record DocLine : Doc { public readonly static Doc Default = new DocLine(); public override Doc ReAnnotate(Func f) => DocLine.Default; public override FlattenResult> ChangesUponFlattening() => NeverFlat>.Default; public override Doc Flatten() => DocFail.Default; } /// /// Lay out the first 'Doc', but when flattened (via 'group'), prefer /// the second. /// /// The layout algorithms work under the assumption that the first /// alternative is less wide than the flattened second alternative. /// public record DocFlatAlt(Doc Primary, Doc Alt) : Doc { public override Doc ReAnnotate(Func f) => DocAnn.FlatAlt(Primary.ReAnnotate(f), Alt.ReAnnotate(f)); public override FlattenResult> ChangesUponFlattening() => new Flattened>(Alt.Flatten()); public override Doc Flatten() => Alt.Flatten(); } /// /// The first lines of first document should be longer than the /// first lines of the second one, so the layout algorithm can pick the one /// that fits best. Used to implement layout alternatives for 'group'. /// public record DocUnion(Doc DocA, Doc DocB) : Doc { public override Doc ReAnnotate(Func f) => DocAnn.Union(DocA.ReAnnotate(f), DocB.ReAnnotate(f)); public override FlattenResult> ChangesUponFlattening() => new Flattened>(DocA.Flatten()); public override Doc Flatten() => DocA.Flatten(); } /// /// Concat two documents /// public record DocCat(Doc DocA, Doc DocB) : Doc { public override Doc ReAnnotate(Func f) => DocAnn.Cat(DocA.ReAnnotate(f), DocB.ReAnnotate(f)); public override FlattenResult> ChangesUponFlattening() => (DocA.ChangesUponFlattening(), DocB.ChangesUponFlattening()) switch { (NeverFlat>, _) => NeverFlat>.Default, (_, NeverFlat>) => NeverFlat>.Default, (Flattened> (var x1), Flattened> (var y1)) => new Flattened>(DocAnn.Cat(x1, y1)), (Flattened> (var x1), AlreadyFlat>) => new Flattened>(DocAnn.Cat(x1, DocB)), (AlreadyFlat>, Flattened> (var y1)) => new Flattened>(DocAnn.Cat(DocA, y1)), _ => AlreadyFlat>.Default }; public override Doc Flatten() => DocAnn.Cat(DocA.Flatten(), DocB.Flatten()); } /// /// Document indented by a number of columns /// public record DocNest(int Indent, Doc Doc) : Doc { public override Doc ReAnnotate(Func f) => DocAnn.Nest(Indent, Doc.ReAnnotate(f)); public override FlattenResult> ChangesUponFlattening() => Doc.ChangesUponFlattening().Map(d => DocAnn.Nest(Indent, d) as Doc); public override Doc Flatten() => DocAnn.Nest(Indent, Doc.Flatten()); public override string ToString() => $"(nest {Indent} {Doc})"; } /// /// React on the current cursor position, /// public record DocColumn(Func> React) : Doc { public override Doc ReAnnotate(Func f) => DocAnn.Column(a => React(a).ReAnnotate(f)); public override FlattenResult> ChangesUponFlattening() => new Flattened>(DocAnn.Column(x => React(x).Flatten())); public override Doc Flatten() => DocAnn.Column(x => React(x).Flatten()); } /// /// React on the document's page width /// public record DocPageWidth(Func> React) : Doc { public override Doc ReAnnotate(Func f) => DocAnn.PageWidth(a => React(a).ReAnnotate(f)); public override FlattenResult> ChangesUponFlattening() => new Flattened>(DocAnn.PageWidth(x => React(x).Flatten())); public override Doc Flatten() => DocAnn.PageWidth(x => React(x).Flatten()); } /// /// React on the current nesting level /// public record DocNesting(Func> React) : Doc { public override Doc ReAnnotate(Func f) => DocAnn.Nesting(a => React(a).ReAnnotate(f)); public override FlattenResult> ChangesUponFlattening() => new Flattened>(DocAnn.Nesting(x => React(x).Flatten())); public override Doc Flatten() => DocAnn.Nesting(x => React(x).Flatten()); } /// /// dd an annotation to the enclosed 'Doc'. Can be used for example to add /// styling directives or alt texts that can then be used by the renderer. /// public record DocAnnotate(A Annotation, Doc Doc) : Doc { public override Doc ReAnnotate(Func f) => DocAnn.Annotate(f(Annotation), Doc.ReAnnotate(f)); public override FlattenResult> ChangesUponFlattening() => Doc.ChangesUponFlattening().Map(d => DocAnn.Annotate(Annotation, Doc) as Doc); public override Doc Flatten() => DocAnn.Annotate(Annotation, Doc.Flatten()); } } ================================================ FILE: LanguageExt.Core/Pretty/FastSpace.cs ================================================ using System.Text; namespace LanguageExt.Pretty { internal class FastSpace { public static string Show(int n) => (n < 0 ? 0 : n) switch { 0 => "", 1 => " ", 2 => " ", 3 => " ", 4 => " ", 5 => " ", 6 => " ", 7 => " ", 8 => " ", _ => FastSpaceInternal(n - 8) }; static string FastSpaceInternal(int x) { var sb = new StringBuilder(); sb.Append(" "); for (var i = 0; i < x; i++) { sb.Append(' '); } return sb.ToString(); } } } ================================================ FILE: LanguageExt.Core/Pretty/FittingPredicate.cs ================================================ using System; namespace LanguageExt.Pretty { /// /// FittingPredicate /// /// /// /// FP: PageWidth, Nesting Level, Width for fist line (None is unbounded( /// /// public record FittingPredicate(Func, DocStream, bool> FP); } ================================================ FILE: LanguageExt.Core/Pretty/FlattenResult.cs ================================================ using System; namespace LanguageExt.Pretty { public abstract record FlattenResult { public abstract FlattenResult Map(Func f); public FlattenResult Select(Func f) => Map(f); } public record Flattened(A Value) : FlattenResult { public override FlattenResult Map(Func f) => new Flattened(f(Value)); } public record AlreadyFlat : FlattenResult { public static readonly FlattenResult Default = new AlreadyFlat(); public override FlattenResult Map(Func f) => AlreadyFlat.Default; } public record NeverFlat : FlattenResult { public static readonly FlattenResult Default = new NeverFlat(); public override FlattenResult Map(Func f) => NeverFlat.Default; } } ================================================ FILE: LanguageExt.Core/Pretty/Layout.cs ================================================ #nullable enable using System; using static LanguageExt.Prelude; namespace LanguageExt.Pretty { public static class Layout { public static DocStream pretty(LayoutOptions options, Doc doc) { return layout(new FittingPredicate(static (p, m, w, d) => go(w, d)), options, doc); static bool go(Option mx, DocStream doc) { while (true) { switch ((mx.Case, doc)) { case (null, _): return true; case (int w, _) when w < 0: return false; case (_, SFail): return false; case (_, SEmpty): return false; case (int w, SChar (_, var x)): mx = Some(w - 1); doc = x; break; case (int w, SText (var t, var x)): mx = Some(w - t.Length); doc = x; break; case (_, SLine): return true; case (int w, SAnnPush (_, var x)): mx = Some(w); doc = x; break; case (int w, SAnnPop (var x)): mx = Some(w); doc = x; break; default: throw new NotSupportedException(); } } } } public static DocStream smart(LayoutOptions options, Doc doc) { return layout(new FittingPredicate(go), options, doc); static bool go(PageWidth pageWidth, int minNest, Option mx, DocStream doc) { while (true) { switch ((pageWidth, minNest, mx.Case, doc)) { case (_, _, null, _): return false; case (_, _, int w, _) when w < 0: return false; case (_, _, _, SFail): return false; case (_, _, _, SEmpty): return true; case (var pw, var m, int w, SChar (_, var x)): mx = Some(w - 1); doc = x; break; case (var pw, var m, int w, SText (var t, var x)): mx = Some(w - t.Length); doc = x; break; case (AvailablePerLine (var cpl, _), var m, _, SLine(var i, var x)) when m < i : mx = Some(cpl - i); doc = x; break; case (_, _, _, SLine): return true; case (var pw, var m, int w, SAnnPush (_, var x)): mx = Some(w); doc = x; break; case (var pw, var m, int w, SAnnPop (var x)): mx = Some(w); doc = x; break; default: throw new NotSupportedException(); } } } } public static DocStream layout( FittingPredicate fittingPredicate, LayoutOptions options, Doc doc) { return best(0, 0, new Cons(0, doc, Nil.Default)); DocStream best(int nest, int col, LayoutPipeline pipeline) => (nest, col, pipeline) switch { (_, _, Nil) => SEmpty.Default, (var nl, var cc, UndoAnn (var ds)) => new SAnnPop(best(nl, cc, ds)), (var nl, var cc, Cons (var i, var d, var ds)) => d switch { DocFail => SFail.Default, DocEmpty => best(nl, cc, ds), DocChar (var c) => new SChar(c, best(nl, cc + 1, ds)), DocText (var t) => new SText(t, best(nl, cc + t.Length, ds)), DocLine => new SLine(i, best(i, i, ds)), DocFlatAlt(var x, _) => best(nl, cc, new Cons(i, x, ds)), DocCat(var x, var y) => best(nl, cc, new Cons(i, x, new Cons(i, y, ds))), DocNest(var j, var x) => best(nl, cc, new Cons(i + j, x, ds)), DocUnion(var x, var y) => selectNicer(fittingPredicate, nl, cc, best(nl, cc, new Cons(i, x, ds)), best(nl, cc, new Cons(i, y, ds))), DocColumn(var f) => best(nl, cc, new Cons(i, f(cc), ds)), DocPageWidth(var f) => best(nl, cc, new Cons(i, f(options.PageWidth), ds)), DocNesting(var f) => best(nl, cc, new Cons(i, f(i), ds)), DocAnnotate(var ann, var x) => new SAnnPush(ann, best(nl, cc, new Cons(i, x, new UndoAnn(ds)))), _ => throw new NotSupportedException() }, _ => throw new NotSupportedException() }; DocStream selectNicer(FittingPredicate fits, int lineIndent, int currentColumn, DocStream x, DocStream y) { var minNestingLevel = Math.Min(lineIndent, currentColumn); var ribbonWidth = options.PageWidth switch { AvailablePerLine (var lineLength, var ribbonFraction) => Some(Math.Max(0, Math.Min(lineLength, (int) (((double) lineLength) * ribbonFraction)))), Unbounded => None, _ => throw new NotSupportedException() }; var availableWidth = from columnsLeftInLine in options.PageWidth switch { AvailablePerLine(var cpl, var ribbonFrac) => Some(cpl - currentColumn), Unbounded => None, _ => throw new NotSupportedException() } from columnsLeftInRibbon in from li in Some(lineIndent) from rw in ribbonWidth from cc in Some(currentColumn) select li + rw - cc select Math.Min(columnsLeftInLine, columnsLeftInRibbon); return fits.FP(options.PageWidth, minNestingLevel, availableWidth, x) ? x : y; } } } } #nullable disable ================================================ FILE: LanguageExt.Core/Pretty/LayoutOptions.cs ================================================ using System; using System.Text; using LanguageExt; using LanguageExt.ClassInstances; using static LanguageExt.Prelude; namespace LanguageExt.Pretty { public record LayoutOptions(PageWidth PageWidth) { public static readonly LayoutOptions Default = new LayoutOptions(PageWidth.Default); } } ================================================ FILE: LanguageExt.Core/Pretty/LayoutPipeline.cs ================================================ namespace LanguageExt.Pretty { /// /// List of nesting level/document pairs yet to be laid out. /// public abstract record LayoutPipeline; public record Nil : LayoutPipeline { public static readonly LayoutPipeline Default = new Nil(); } public record Cons(int Value, Doc Doc, LayoutPipeline Pipeline): LayoutPipeline; public record UndoAnn(LayoutPipeline Pipeline): LayoutPipeline; } ================================================ FILE: LanguageExt.Core/Pretty/PageWidth.cs ================================================ using System; using System.Text; using LanguageExt; using LanguageExt.ClassInstances; using static LanguageExt.Prelude; namespace LanguageExt.Pretty { public abstract record PageWidth { public static readonly PageWidth Default = new AvailablePerLine(); } /// /// - The 'Int' is the number of characters, including whitespace, that /// fit in a line. A typical value is 80. /// /// - The 'Double' is the ribbon with, i.e. the fraction of the total /// page width that can be printed on. This allows limiting the length /// of printable text per line. Values must be between 0 and 1, and /// 0.4 to 1 is typical. /// public record AvailablePerLine(int LineLength = 120, double RibbonFraction = 0.4) : PageWidth; public record Unbounded() : PageWidth; } ================================================ FILE: LanguageExt.Core/Prism/Prelude_Prism.cs ================================================ namespace LanguageExt { public static partial class Prelude { /// /// Convert a lens to a prism /// public static Prism prism(Lens la) => Prism.New(la); /// /// Convert a lens to a prism /// public static Prism prism(Lens> la) => Prism.New(la); /// /// Sequentially composes two prisms /// public static Prism prism(Prism pa, Prism pb) => Prism.New( Get: a => pa.Get(a).Bind(pb.Get), Set: v => pa.Update(pb.SetF(v))); /// /// Sequentially composes three prisms /// public static Prism prism(Prism pa, Prism pb, Prism pc) => Prism.New( Get: a => pa.Get(a).Bind(pb.Get).Bind(pc.Get), Set: v => pa.Update(pb.Update(pc.SetF(v)))); /// /// Sequentially composes four prisms /// public static Prism prism(Prism pa, Prism pb, Prism pc, Prism pd) => Prism.New( Get: a => pa.Get(a).Bind(pb.Get).Bind(pc.Get).Bind(pd.Get), Set: v => pa.Update(pb.Update(pc.Update(pd.SetF(v))))); /// /// Sequentially composes five prisms /// public static Prism prism(Prism pa, Prism pb, Prism pc, Prism pd, Prism pe) => Prism.New( Get: a => pa.Get(a).Bind(pb.Get).Bind(pc.Get).Bind(pd.Get).Bind(pe.Get), Set: v => pa.Update(pb.Update(pc.Update(pd.Update(pe.SetF(v)))))); /// /// Sequentially composes six prisms /// public static Prism prism(Prism pa, Prism pb, Prism pc, Prism pd, Prism pe, Prism pf) => Prism.New( Get: a => pa.Get(a).Bind(pb.Get).Bind(pc.Get).Bind(pd.Get).Bind(pe.Get).Bind(pf.Get), Set: v => pa.Update(pb.Update(pc.Update(pd.Update(pe.Update(pf.SetF(v))))))); /// /// Sequentially composes seven prisms /// public static Prism prism(Prism pa, Prism pb, Prism pc, Prism pd, Prism pe, Prism pf, Prism pg) => Prism.New( Get: a => pa.Get(a).Bind(pb.Get).Bind(pc.Get).Bind(pd.Get).Bind(pe.Get).Bind(pf.Get).Bind(pg.Get), Set: v => pa.Update(pb.Update(pc.Update(pd.Update(pe.Update(pf.Update(pg.SetF(v)))))))); /// /// Sequentially composes eight prisms /// public static Prism prism(Prism pa, Prism pb, Prism pc, Prism pd, Prism pe, Prism pf, Prism pg, Prism ph) => Prism.New( Get: a => pa.Get(a).Bind(pb.Get).Bind(pc.Get).Bind(pd.Get).Bind(pe.Get).Bind(pf.Get).Bind(pg.Get).Bind(ph.Get), Set: v => pa.Update(pb.Update(pc.Update(pd.Update(pe.Update(pf.Update(pg.Update(ph.SetF(v))))))))); /// /// Sequentially composes nine prisms /// public static Prism prism(Prism pa, Prism pb, Prism pc, Prism pd, Prism pe, Prism pf, Prism pg, Prism ph, Prism pi) => Prism.New( Get: a => pa.Get(a).Bind(pb.Get).Bind(pc.Get).Bind(pd.Get).Bind(pe.Get).Bind(pf.Get).Bind(pg.Get).Bind(ph.Get).Bind(pi.Get), Set: v => pa.Update(pb.Update(pc.Update(pd.Update(pe.Update(pf.Update(pg.Update(ph.Update(pi.SetF(v)))))))))); /// /// Sequentially composes ten prisms /// public static Prism prism(Prism pa, Prism pb, Prism pc, Prism pd, Prism pe, Prism pf, Prism pg, Prism ph, Prism pi, Prism pj) => Prism.New( Get: a => pa.Get(a).Bind(pb.Get).Bind(pc.Get).Bind(pd.Get).Bind(pe.Get).Bind(pf.Get).Bind(pg.Get).Bind(ph.Get).Bind(pi.Get).Bind(pj.Get), Set: v => pa.Update(pb.Update(pc.Update(pd.Update(pe.Update(pf.Update(pg.Update(ph.Update(pi.Update(pj.SetF(v))))))))))); } } ================================================ FILE: LanguageExt.Core/Prism/Prism.cs ================================================ namespace LanguageExt { public static class Prism { /// /// Identity lens /// public static Prism identity() => Prism.New( Get: a => a, Set: a => _ => a); } } ================================================ FILE: LanguageExt.Core/Prism/PrismAB.cs ================================================ using System; using System.Diagnostics.Contracts; namespace LanguageExt { /// /// Primitive prism type for creating transformations through Options /// public readonly struct Prism { public readonly Func> Get; public readonly Func> SetF; private Prism(Func> get, Func> set) { Get = get; SetF = set; } public A Set(B value, A cont) => SetF(value)(cont); public static Prism New(Func> Get, Func> Set) => new Prism(Get, Set); public static Prism New(Lens lens) => Prism.New( Get: a => lens.Get(a), Set: v => lens.SetF(v) ); public static Prism New(Lens> lens) => Prism.New( Get: a => lens.Get(a), Set: v => lens.SetF(v) ); public Func Update(Func f) { var self = this; return a => self.Get(a) .Map(v => self.Set(f(v), a)) .IfNone(a); } public A Update(Func f, A value) { var self = this; return Get(value).Map(v => self.Set(f(v), value)) .IfNone(value); } /// /// Implicit conversion operator from Lens〈A, B〉 to Prism〈A, B〉 /// /// Value [Pure] public static implicit operator Prism(Lens value) => New(value); /// /// Implicit conversion operator from Option〈A〉 to Result〈A〉 /// /// Value [Pure] public static implicit operator Prism(Lens> value) => New(value); } } ================================================ FILE: LanguageExt.Core/Prism/README.md ================================================ Prisms are lenses that allow you to look inside a structure to access its value and then subsequently update that value. But because we don't want to do mutation, the updaters create new versions of the underlying structure. The difference between lenses and prisms is that a prism returns an optional value for its getter. ================================================ FILE: LanguageExt.Core/README.md ================================================ If you're new to this library, you may need a few pointers of where to look for features: * [`Prelude`](Prelude) is a `static partial class`, this type is loaded with functions for constructing the key data types, as well as many of the things you'd expect in a functional programming language's prelude. Note, the `Prelude` type extends into many other parts of the source-tree. It's the same type, but spread all over the code-base. And so, you may see `Prelude` in other areas of the documentation: it's the same type. Because it's so fundamental, you'll want to add this to the top of every code file (or in your global usings): using static LanguageExt.Prelude; This makes all functions in the `Prelude` available as though they were local. * [`Traits`](Traits) are the powerhouse of this library and allow for true higher-kinded abstract behviours to be leveraged throughout. The topic of traits is huge, so if you're looking for an introduction, take a look at [Paul Louth's Higher Kinds series on his blog](https://paullouth.com/higher-kinds-in-c-with-language-ext/). * [`Monads`](Monads) contains the common monads like `Option` and `Either`, as well as state-managing monads like `Reader`, `Writer`, and `State`. It also is home to many monad-transformers (types with a `T` suffix, like `OptionT`). Transformers allow 'stacking' of monadic effects into 'super monads'. * [`Immutable Collections`](Immutable%20Collections) contains the high-performance functional collection types this library is famous for. * [`Effects`](Effects) is where the pure IO functionality of language-ext resides. It is also where you'll find the `StreamT` for 'effectful' streaming. To understand more about how to deal with side-effects, [check the wiki](https://github.com/louthy/language-ext/wiki/How-to-deal-with-side-effects). * [`Concurrency`](Concurrency) is where you'll find lots of help in atomically managing shared data without locks. ================================================ FILE: LanguageExt.Core/README.nuget.md ================================================ # LanguageExt.Core `LanguageExt.Core` is the primary library in the [language-ext functional programming framework](https://github.com/louthy/language-ext). The framework uses and abuses the features of C# to provide a pure functional-programming 'Base Class Library' that, if you squint, can look like extensions to the language itself. The desire here is to make programming in C# much more robust by helping the engineer's inertia flow in the direction of declarative and pure functional code rather than imperative. Using these techniques for large code-bases can bring tangible benefits to long-term maintenance by removing hidden complexity and by easing the engineer's cognitive load. ## Features ### [Functional effects and IO](https://louthy.github.io/language-ext/LanguageExt.Core/Effects/index.html) | Location | Feature | Description | |----------|--------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `Core` | `IO` | [A synchronous and asynchronous side-effect: an IO monad](https://louthy.github.io/language-ext/LanguageExt.Core/Effects/IO/index.html) | | `Core` | `Eff` | [A synchronous and asynchronous side-effect with error handling](https://louthy.github.io/language-ext/LanguageExt.Core/Effects/Eff/Eff%20no%20runtime/index.html) | | `Core` | `Eff` | [Same as `Eff` but with an injectable runtime for dependency-injection: a unit testable IO monad](https://louthy.github.io/language-ext/LanguageExt.Core/Effects/Eff/Eff%20with%20runtime/index.html) | | `Core` | Pipes | [A clean and powerful stream processing system that lets you build and connect reusable streaming components](https://louthy.github.io/language-ext/LanguageExt.Core/Effects/Pipes/index.html) | | `Core` | StreamT | [less powerful (than Pipes), but easier to use streaming effects transformer](https://louthy.github.io/language-ext/LanguageExt.Core/Effects/StreamT/index.html) | ### [Atomic concurrency and collections](https://louthy.github.io/language-ext/LanguageExt.Core/Concurrency/index.html) | Location | Feature | Description | |----------|------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------| | `Core` | `Atom` | [A lock-free atomically mutable reference for working with shared state](https://louthy.github.io/language-ext/LanguageExt.Core/Concurrency/Atom) | | `Core` | `Ref` | [An atomic reference to be used in the transactional memory system](https://louthy.github.io/language-ext/LanguageExt.Core/Concurrency/STM) | | `Core` | `AtomHashMap` | [An immutable `HashMap` with a lock-free atomically mutable reference](https://louthy.github.io/language-ext/LanguageExt.Core/Concurrency/AtomHashMap) | | `Core` | `AtomSeq` | [An immutable `Seq` with a lock-free atomically mutable reference](https://louthy.github.io/language-ext/LanguageExt.Core/Concurrency/AtomSeq) | | `Core` | `VectorClock` | [Understand distributed causality](https://louthy.github.io/language-ext/LanguageExt.Core/Concurrency/VectorClock) | | `Core` | `VersionVector` | [A vector clock with some versioned data](https://louthy.github.io/language-ext/LanguageExt.Core/Concurrency/VersionVector) | | `Core` | `VersionHashMap ` | [Distrubuted atomic versioning of keys in a hash-map](https://louthy.github.io/language-ext/LanguageExt.Core/Concurrency/VersionHashMap) | ### [Immutable collections](https://louthy.github.io/language-ext/LanguageExt.Core/Immutable%20Collections/index.html) | Location | Feature | Description | |----------|----------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `Core` | `Arr` | [Immutable array](https://louthy.github.io/language-ext/LanguageExt.Core/Immutable%20Collections/Arr/index.html) | | `Core` | `Seq` | [Lazy immutable list, evaluate at-most-once](https://louthy.github.io/language-ext/LanguageExt.Core/Immutable%20Collections/Seq/index.html) - very, very fast! | | `Core` | `Iterable` | [Wrapper around `IEnumerable` with support for traits](https://louthy.github.io/language-ext/LanguageExt.Core/Immutable%20Collections/Iterable/index.html) - enables the higher-kinded traits to work with enumerables. | | `Core` | `Lst` | [Immutable list](https://louthy.github.io/language-ext/LanguageExt.Core/Immutable%20Collections/List/index.html) - use `Seq` over `Lst` unless you need `InsertAt` | | `Core` | `Map` | [Immutable map](https://louthy.github.io/language-ext/LanguageExt.Core/Immutable%20Collections/Map/index.html) | | `Core` | `Map` | [Immutable map with Ord constraint on `K`](https://louthy.github.io/language-ext/LanguageExt.Core/Immutable%20Collections/Map/index.html) | | `Core` | `HashMap` | [Immutable hash-map](https://louthy.github.io/language-ext/LanguageExt.Core/Immutable%20Collections/HashMap/index.html) | | `Core` | `HashMap` | [Immutable hash-map with Eq constraint on `K`](https://louthy.github.io/language-ext/LanguageExt.Core/Immutable%20Collections/HashMap/index.html) | | `Core` | `Set` | [Immutable set](https://louthy.github.io/language-ext/LanguageExt.Core/Immutable%20Collections/Set/index.html) | | `Core` | `Set` | [Immutable set with Ord constraint on `A`](https://louthy.github.io/language-ext/LanguageExt.Core/Immutable%20Collections/Set/index.html) | | `Core` | `HashSet` | [Immutable hash-set](https://louthy.github.io/language-ext/LanguageExt.Core/Immutable%20Collections/HashSet/index.html) | | `Core` | `HashSet` | [Immutable hash-set with Eq constraint on `A`](https://louthy.github.io/language-ext/LanguageExt.Core/Immutable%20Collections/HashSet/index.html) | | `Core` | `Que` | [Immutable queue](https://louthy.github.io/language-ext/LanguageExt.Core/Immutable%20Collections/Queue/index.html) | | `Core` | `Stck` | [Immutable stack](https://louthy.github.io/language-ext/LanguageExt.Core/Immutable%20Collections/Stack/index.html) | ### [Optional and alternative value monads](https://louthy.github.io/language-ext/LanguageExt.Core/Monads/Alternative%20Monads/index.html) | Location | Feature | Description | |----------|---------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `Core` | `Option` | [Option monad](https://louthy.github.io/language-ext/LanguageExt.Core/Monads/Alternative%20Monads/Option/index.html) | | `Core` | `OptionT` | [Option monad-transformer](https://louthy.github.io/language-ext/LanguageExt.Core/Monads/Alternative%20Monads/OptionT/index.html) | | `Core` | `Either` | [Right/Left choice monad](https://louthy.github.io/language-ext/LanguageExt.Core/Monads/Alternative%20Monads/Either/index.html) | | `Core` | `EitherT` | [Right/Left choice monad-transformer](https://louthy.github.io/language-ext/LanguageExt.Core/Monads/Alternative%20Monads/EitherT/index.html) | | `Core` | `Fin` | [`Error` handling monad, like `Either`](https://louthy.github.io/language-ext/LanguageExt.Core/Monads/Alternative%20Monads/Fin/index.html) | | `Core` | `FinT` | [`Error` handling monad-transformer](https://louthy.github.io/language-ext/LanguageExt.Core/Monads/Alternative%20Monads/FinT/index.html) | | `Core` | `Try` | [Exception handling monad](https://louthy.github.io/language-ext/LanguageExt.Core/Monads/Alternative%20Monads/Try/index.html) | | `Core` | `TryT` | [Exception handling monad-transformer](https://louthy.github.io/language-ext/LanguageExt.Core/Monads/Alternative%20Monads/TryT/index.html) | | `Core` | `Validation` | [Validation applicative and monad](https://louthy.github.io/language-ext/LanguageExt.Core/Monads/Alternative%20Monads/Validation/index.html) for collecting multiple errors before aborting an operation | | `Core` | `ValidationT` | [Validation applicative and monad-transformer](https://louthy.github.io/language-ext/LanguageExt.Core/Monads/Alternative%20Monads/ValidationT/index.html) | ### [State managing monads](https://louthy.github.io/language-ext/LanguageExt.Core/Monads/State%20and%20Environment%20Monads/index.html) | Location | Feature | Description | |----------|--------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `Core` | `Reader` | [Reader monad](https://louthy.github.io/language-ext/LanguageExt.Core/Monads/State%20and%20Environment%20Monads/Reader/Reader/index.html) | | `Core` | `ReaderT` | [Reader monad-transformer](https://louthy.github.io/language-ext/LanguageExt.Core/Monads/State%20and%20Environment%20Monads/Reader/ReaderT/index.html) | | `Core` | `Writer` | [Writer monad that logs to a `W` constrained to be a Monoid](https://louthy.github.io/language-ext/LanguageExt.Core/Monads/State%20and%20Environment%20Monads/Writer/Writer/index.html) | | `Core` | `WriterT` | [Writer monad-transformer](https://louthy.github.io/language-ext/LanguageExt.Core/Monads/State%20and%20Environment%20Monads/Writer/WriterT/index.html) | | `Core` | `State` | [State monad](https://louthy.github.io/language-ext/LanguageExt.Core/Monads/State%20and%20Environment%20Monads/State/State/index.html) | | `Core` | `StateT` | [State monad-transformer](https://louthy.github.io/language-ext/LanguageExt.Core/Monads/State%20and%20Environment%20Monads/State/StateT/index.html) | ### [Parser combinators](https://louthy.github.io/language-ext/LanguageExt.Parsec/index.html) | Location | Feature | Description | |----------|----------------|--------------------------------------------------------------------------------------------------------------------------------| | `Parsec` | `Parser` | [String parser monad and full parser combinators library](https://louthy.github.io/language-ext/LanguageExt.Parsec/index.html) | | `Parsec` | `Parser` | [Parser monad that can work with any input stream type](https://louthy.github.io/language-ext/LanguageExt.Parsec/index.html) | ### [Pretty](https://louthy.github.io/language-ext/LanguageExt.Core/Pretty/index.html) | Location | Feature | Description | |----------|----------|--------------------------------------------------| | `Core` | `Doc` | Produce nicely formatted text with smart layouts | ### [Differencing](https://louthy.github.io/language-ext/LanguageExt.Core/DataTypes/Patch/index.html) | Location | Feature | Description | |----------|-----------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `Core` | `Patch` | Uses patch-theory to efficiently calculate the difference (`Patch.diff(list1, list2)`) between two collections of `A` and build a patch which can be applied (`Patch.apply(patch, list)`) to one to make the other (think git diff). | ### [Traits](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/index.html) The traits are major feature of `v5`+ language-ext that makes generic programming with higher-kinds a reality. Check out Paul's [series on Higher Kinds](https://paullouth.com/higher-kinds-in-c-with-language-ext/) to get a deeper insight. | Location | Feature | Description | |----------|----------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `Core` | `MonoidK` | [A monoid on applicative functors](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Alternative/index.html) | | `Core` | `Applicative` | [Applicative functor](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Applicative/index.html) | | `Core` | `Eq` | [Ad-hoc equality trait](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Eq/index.html) | | `Core` | `Fallible` | [Trait that describes types that can fail](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Fallible/index.html) | | `Core` | `Foldable` | [Aggregation over a structure](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Foldable/index.html) | | `Core` | `Functor` | [Functor `Map`](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Functor/index.html) | | `Core` | `Has` | [Used in runtimes to enable DI-like capabilities](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Has/index.html) | | `Core` | `Hashable` | [Ad-hoc has-a-hash-code trait](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Hashable/index.html) | | `Core` | `Local` | [Creates a local environment to run a computation ](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Local/index.html) | | `Core` | `Monad` | [Monad trait](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Monads/Monad/index.html) | | `Core` | `MonadT` | [Monad transformer trait](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Monads/MonadT/index.html) | | `Core` | `Monoid` | [A monoid is a type with an identity `Empty` and an associative binary operation `+`](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Monoid/index.html) | | `Core` | `MonoidK` | [Equivalent of monoids for working on higher-kinded types](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/MonoidK/index.html) | | `Core` | `Mutates` | [Used in runtimes to enable stateful operations](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Mutates/index.html) | | `Core` | `Ord` | [Ad-hoc ordering / comparisons](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Ord/index.html) | | `Core` | `Range` | [Abstraction of a range of values](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Range/index.html) | | `Core` | `Readable` | [Generalised Reader monad abstraction](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Readable/index.html) | | `Core` | `SemigroupK` | [A semigroup on functors](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/SemigroupK/index.html) | | `Core` | `Semigroup` | [Provides an associative binary operation `+`](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Semigroup/index.html) | | `Core` | `SemigroupK` | [Equivalent of semigroups for working with higher-kinded types](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/SemigroupK/index.html) | | `Core` | `Stateful` | [Generalised State monad abstraction](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Stateful/index.html) | | `Core` | `Traversable` | [Traversable structures support element-wise sequencing of Applicative effects](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Traversable/index.html) | | `Core` | `Writable` | [Generalised Writer monad abstraction](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Writable/index.html) | ### [Value traits](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Domain/index.html) These work a little like `NewType` but they impart semantic meaning and some common operators for the underlying value. | Location | Feature | Description | |----------|--------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `Core` | `DomainType` | Provides a mapping from `SELF` to an underlying representation: `REPR` | | `Core` | `Identifier ` | Identifiers (like IDs in databases: `PersonId` for example), they are equivalent to `DomaintType` with equality. | | `Core` | `VectorSpace` | Scalable values; can add and subtract self, but can only multiply and divide by a scalar. Can also negate. | | `Core` | `Amount ` | Quantities, such as the amount of money in USD on a bank account or a file size in bytes. Derives `VectorSpace`, `IdentifierLike`, `DomainType`, and is orderable (comparable). | | `Core` | `LocusLike ` | Works with space-like structures. Spaces have absolute and relative distances. Has an origin/zero point and derives `DomainType`, `IdentifierLike`, `AmountLike` and `VectorSpace`. `DISTANCE` must also be an `AmountLike`. | ================================================ FILE: LanguageExt.Core/Traits/Alternative/Alternative.Extensions.cs ================================================ using LanguageExt.Traits; namespace LanguageExt; /// /// A monoid on applicative functors /// /// Applicative functor public static class AlternativeExtensions { /// /// Given a set of applicative functors, return the first one to succeed. /// /// /// If none succeed, the last applicative functor will be returned. /// public static K OneOf(this Seq> ms) where F : Alternative => Alternative.choice(ms); } ================================================ FILE: LanguageExt.Core/Traits/Alternative/Alternative.Laws.cs ================================================ using System; using LanguageExt.Common; using static LanguageExt.Prelude; namespace LanguageExt.Traits; /// /// Functions that test that Alternative laws hold for the `F` Alternative provided. /// /// /// choose(pure(a), pure(b)) = pure(a) /// choose(empty(), pure(b)) = pure(b) /// choose(pure(a), empty()) = pure(a) /// /// /// NOTE: `Equals` must be implemented for the `K〈F, *〉` derived-type, so that the laws /// can be proven to be true. If your Alternative structure doesn't have `Equals` then you /// must provide the optional `equals` parameter so that the equality of outcomes can be tested. /// /// Alternative type public static class AlternativeLaw where F : Alternative, Applicative { /// /// Assert that the Alternative laws hold /// /// /// NOTE: `Equals` must be implemented for the `K〈F, *〉` derived-type, so that the laws /// can be proven to be true. If your Alternative structure doesn't have `Equals` then /// you must provide the optional `equals` parameter so that the equality of outcomes can /// be tested. /// public static Unit assert(Func, K, bool>? equals = null) => validate(equals) .IfFail(errors => errors.Throw()); /// /// Validate that the Alternative laws hold /// /// /// NOTE: `Equals` must be implemented for the `K〈F, *〉` derived-type, so that the laws /// can be proven to be true. If your Alternative structure doesn't have `Equals` then /// you must provide the optional `equals` parameter so that the equality of outcomes can /// be tested. /// public static Validation validate(Func, K, bool>? equals = null) { equals ??= (fa, fb) => fa.Equals(fb); return ApplicativeLaw.validate(equals) >> leftCatchLaw(equals) >> leftZeroLaw(equals) >> rightZeroLaw(equals); } /// /// Left-zero law /// /// /// choose(empty, pure(x)) = pure(x) /// /// /// NOTE: `Equals` must be implemented for the `K〈F, *〉` derived-type, so that the laws /// can be proven to be true. If your Alternative structure doesn't have `Equals` then /// you must provide the optional `equals` parameter so that the equality of outcomes can /// be tested. /// public static Validation leftZeroLaw(Func, K, bool> equals) { var fa = F.Empty(); var fb = F.Pure(100); var fr = choose(fa, fb); return equals(fr, fb) ? unit : Error.New($"Alternative left-zero law does not hold for {typeof(F).Name}"); } /// /// Right-zero law /// /// /// choose(pure(x), empty) = pure(x) /// /// /// NOTE: `Equals` must be implemented for the `K〈F, *〉` derived-type, so that the laws /// can be proven to be true. If your Alternative structure doesn't have `Equals` then /// you must provide the optional `equals` parameter so that the equality of outcomes can /// be tested. /// public static Validation rightZeroLaw(Func, K, bool> equals) { var fa = F.Pure(100); var fb = F.Empty(); var fr = choose(fa, fb); return equals(fr, fa) ? unit : Error.New($"Alternative right-zero law does not hold for {typeof(F).Name}"); } /// /// Left catch law /// /// /// choose(pure(x), pure(y)) = pure(x) /// /// /// NOTE: `Equals` must be implemented for the `K〈F, *〉` derived-type, so that the laws /// can be proven to be true. If your Alternative structure doesn't have `Equals` then /// you must provide the optional `equals` parameter so that the equality of outcomes can /// be tested. /// public static Validation leftCatchLaw(Func, K, bool> equals) { var fa = F.Pure(100); var fb = F.Pure(200); var fr = choose(fa, fb); return equals(fr, fa) ? unit : Error.New($"Choice left-catch law does not hold for {typeof(F).Name}"); } } ================================================ FILE: LanguageExt.Core/Traits/Alternative/Alternative.Module.cs ================================================ using System; using System.Diagnostics.Contracts; using static LanguageExt.Prelude; namespace LanguageExt.Traits; public static class Alternative { /// /// Empty / none state for the `F` structure /// /// Alternative trait implementation /// Bound value type /// Empty public static K empty() where F : Alternative => F.Empty(); /// /// Given a set of applicative functors, return the first one to succeed. /// /// /// If none succeed, the last applicative functor will be returned. /// [Pure] public static K choice(Seq> ms) where F : Alternative => F.Choice(ms); /// /// Given a set of applicative functors, return the first one to succeed. /// /// /// If none succeed, the last applicative functor will be returned. /// [Pure] public static K choice(params ReadOnlySpan> ms) where F : Alternative => F.Choice(ms); /// /// One or more... /// /// /// Run the applicative functor repeatedly, collecting the results, until failure. /// /// Will always succeed if at least one item has been yielded. /// /// /// NOTE: It is important that the `F` applicative-type overrides `Apply` (the one with `Func` laziness) in its /// trait-implementations otherwise this will likely result in a stack-overflow. /// /// Applicative functor /// One or more values [Pure] public static K> some(K fa) where F : Alternative => F.Some(fa); /// /// Zero or more... /// /// /// Run the applicative functor repeatedly, collecting the results, until failure. /// Will always succeed. /// /// /// NOTE: It is important that the `F` applicative-type overrides `ApplyLazy` in its trait-implementations /// otherwise this will likely result in a stack-overflow. /// /// Applicative functor /// Zero or more values [Pure] public static K> many(K fa) where F : Alternative => F.Many(fa); /// /// `endBy(p, sep)` parses zero-or-more occurrences of `p`, separated and ended by /// `sep`. Returns a list of values returned by `p`. /// /// Value parser /// Separator parser /// Value type /// Separator type /// [Pure] public static K> endBy(K p, K sep) where F : Alternative => F.EndBy(p, sep); /// /// `endBy1(p, sep)` parses one-or-more occurrences of `p`, separated and ended by /// `sep`. Returns a list of values returned by `p`. /// /// Value parser /// Separator parser /// Value type /// Separator type /// [Pure] public static K> endBy1(K p, K sep) where F : Alternative => F.EndBy1(p, sep); /// /// Combine two alternatives /// /// Left alternative /// Right alternative /// Left value type /// Right value type /// Alternative structure with an `Either` lifted into it [Pure] public static K> either(K ma, K mb) where F : Alternative => F.Either(ma, mb); /// /// `manyUntil(fa, end)` applies `fa` _zero_ or more times until `fend` succeeds. /// Returns the list of values returned by`fa`. `fend` result is consumed and /// lost. Use `manyUntil2` if you wish to keep it. /// /// Structure to consume /// Terminating structure /// Value type /// End value type /// [Pure] public static K> manyUntil(K fa, K fend) where F : Alternative => F.ManyUntil(fa, fend); /// /// `manyUntil2(fa, end)` applies `fa` _zero_ or more times until `fend` succeeds. /// Returns the list of values returned by`fa` plus the `fend` result. /// /// Use `manyUntil` if you don't wish to keep the `end` result. /// /// Structure to consume /// Terminating structure /// Value type /// End value type /// [Pure] public static K Items, END End)> manyUntil2(K fa, K fend) where F : Alternative => F.ManyUntil2(fa, fend); /// /// `someUntil(fa, end)` applies `fa` _one_ or more times until `fend` succeeds. /// Returns the list of values returned by`fa`. `fend` result is consumed and /// lost. Use `someUntil2` if you wish to keep it. /// /// Structure to consume /// Terminating structure /// Value type /// End value type /// [Pure] public static K> someUntil(K fa, K fend) where F : Alternative => F.SomeUntil(fa, fend); /// /// `someUntil2(fa, end)` applies `fa` _one_ or more times until `fend` succeeds. /// Returns the list of values returned by`fa` plus the `fend` result. /// /// Use `someUntil` if you don't wish to keep the `end` result. /// /// Structure to consume /// Terminating structure /// Value type /// End value type /// [Pure] public static K Items, END End)> someUntil2(K fa, K fend) where F : Alternative => F.SomeUntil2(fa, fend); /// /// `option(x, fa)` tries to apply `fa`. If `fa` fails without 'consuming' anything, it /// returns `value`, otherwise the value returned by `fa`. /// /// /// The word 'consuming' is used here because this feature started life as a parser combinator, but it /// can be applied to any `Alternative` structure. Critically, most combinators only have a single flavour /// of failure. So, `option` just results in a default value being returned if `fa` fails. /// /// Default value to use if `fa` fails without 'consuming' anything /// /// /// [Pure] public static K option(A value, K fa) where F : Alternative => F.Option(value, fa); /// /// `sepBy(fa, sep) processes _zero_ or more occurrences of `fa`, separated by `sep`. /// /// Structure to yield return values /// Separator structure /// Value type /// Separator type /// List of values returned by `fa` [Pure] public static K> sepBy(K fa, K sep) where F : Alternative => F.SepBy(fa, sep); /// /// `sepBy(fa, sep) processes _one_ or more occurrences of `fa`, separated by `sep`. /// /// Structure to yield return values /// Separator structure /// Value type /// Separator type /// List of values returned by `fa` [Pure] public static K> sepBy1(K fa, K sep) where F : Alternative => F.SepBy1(fa, sep); /// /// `sepEndBy(fa, sep) processes _zero_ or more occurrences of `fa`, separated /// and optionally ended by `sep`. Returns a list of values returned by `fa`. /// /// Structure to yield return values /// Separator structure /// Value type /// Separator type /// List of values returned by `fa` [Pure] public static K> sepByEnd(K fa, K sep) where F : Alternative => F.SepByEnd(fa, sep); /// /// `sepEndBy1(fa, sep) processes _one_ or more occurrences of `fa`, separated /// and optionally ended by `sep`. Returns a list of values returned by `fa`. /// /// Structure to yield return values /// Separator structure /// Value type /// Separator type /// List of values returned by `fa` [Pure] public static K> sepByEnd1(K fa, K sep) where F : Alternative => F.SepByEnd1(fa, sep); /// /// Process `fa` _zero_ or more times and drop all yielded values. /// /// /// Run the applicative functor repeatedly until failure. /// Will always succeed. /// /// Applicative functor /// Unit [Pure] public static K skipMany(K fa) where F : Alternative => F.SkipMany(fa); /// /// Process `fa` _one_ or more times and drop all yielded values. /// /// /// Run the applicative functor repeatedly until failure. At least one item must be yielded for overall success. /// /// Applicative functor /// Unit [Pure] public static K skipSome(K fa) where F : Alternative => F.SkipSome(fa); /// /// `skip(n, fa)` processes `n` occurrences of `fa`, skipping its result. /// If `n` is not positive, the process equates to `Pure(unit)`. /// /// Number of occurrences of `fa` to skip /// Applicative functor /// Value type /// [Pure] public static K skip(int n, K fa) where F : Alternative => F.Skip(n, fa); /// /// `skipManyUntil(fa, fend)` applies the process `fa` _zero_ or more times /// skipping results until process `fend` succeeds. The resulting value from /// `fend` is then returned. /// /// Value type /// End value type /// [Pure] public static K skipManyUntil(K fa, K fend) where F : Alternative => F.SkipManyUntil(fa, fend); /// /// `skipManyUntil(fa, fend)` applies the process `fa` _one_ or more times /// skipping results until process `fend` succeeds. The resulting value from /// `fend` is then returned. /// /// Value type /// End value type /// [Pure] public static K skipSomeUntil(K fa, K fend) where F : Alternative => F.SkipSomeUntil(fa, fend); } ================================================ FILE: LanguageExt.Core/Traits/Alternative/Alternative.Prelude.cs ================================================ using System; using System.Diagnostics.Contracts; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; public static partial class Prelude { /// /// Given a set of applicative functors, return the first one to succeed. /// /// /// If none succeed, the last applicative functor will be returned. /// [Pure] public static K oneOf(params K[] ms) where F : Alternative => Alternative.choice(ms); /// /// Given a set of applicative functors, return the first one to succeed. /// /// /// If none succeed, the last applicative functor will be returned. /// [Pure] public static K oneOf(Seq> ms) where F : Alternative => Alternative.choice(ms); } ================================================ FILE: LanguageExt.Core/Traits/Alternative/Alternative.Trait.cs ================================================ using System; using System.Diagnostics.Contracts; using static LanguageExt.Prelude; namespace LanguageExt.Traits; public interface Alternative : Choice, Applicative where F : Alternative { /// /// Identity /// [Pure] public static abstract K Empty(); /// /// Given a set of applicative functors, return the first one to succeed. /// /// /// If none succeed, the last applicative functor will be returned. /// [Pure] public static virtual K Choice(in Seq> ms) { if(ms.IsEmpty) return F.Empty(); var r = ms[0]; foreach (var m in ms.Tail) { r |= m; } return r; } /// /// Given a set of applicative functors, return the first one to succeed. /// /// /// If none succeed, the last applicative functor will be returned. /// [Pure] public static virtual K Choice(in ReadOnlySpan> ms) { if(ms.Length == 0) return F.Empty(); var r = ms[0]; foreach (var m in ms) { r |= m; } return r; } /// /// One or more... /// /// /// Run the applicative functor repeatedly, collecting the results, until failure. /// /// Will always succeed if at least one item has been yielded. /// /// Applicative functor /// One or more values [Pure] public static virtual K> Some(K fa) { return some(); K> many() => F.Choose(some(), F.Pure(Seq())); K> some() => Cached.cons * fa * memoK(many); } /// /// Zero or more... /// /// /// Run the applicative functor repeatedly, collecting the results, until failure. /// Will always succeed. /// /// Applicative functor /// Zero or more values [Pure] public static virtual K> Many(K fa) { return many(); K> many() => some() | F.Pure(Seq()); K> some() => Cached.cons * fa * memoK(many); } /// /// `endBy(p, sep)` parses zero-or-more occurrences of `p`, separated and ended by /// `sep`. Returns a list of values returned by `p`. /// /// Value parser /// Separator parser /// Value type /// Separator type /// [Pure] public static virtual K> EndBy(K p, K sep) => F.Many(p.BackAction(sep)); /// /// `endBy1(p, sep)` parses one-or-more occurrences of `p`, separated and ended by /// `sep`. Returns a list of values returned by `p`. /// /// Value parser /// Separator parser /// Value type /// Separator type /// [Pure] public static virtual K> EndBy1(K p, K sep) => F.Some(p.BackAction(sep)); /// /// Combine two alternatives /// /// Left alternative /// Right alternative /// Left value type /// Right value type /// Alternative structure with an `Either` lifted into it [Pure] public static virtual K> Either(K fa, K fb) => (Left) * fa | (Right) * fb; /// /// `manyUntil(fa, end)` applies `fa` _zero_ or more times until `fend` succeeds. /// Returns the list of values returned by`fa`. `fend` result is consumed and /// lost. Use `manyUntil2` if you wish to keep it. /// /// Structure to consume /// Terminating structure /// Value type /// End value type /// [Pure] public static virtual K> ManyUntil(K fa, K fend) { var empty = Prelude.Pure(Seq.Empty); return go(); K> go() => empty * fend | Applicative.lift(Seq.cons, fa, memoK(go)); } /// /// `manyUntil2(fa, end)` applies `fa` _zero_ or more times until `fend` succeeds. /// Returns the list of values returned by`fa` plus the `fend` result. /// /// Use `manyUntil` if you don't wish to keep the `end` result. /// /// Structure to consume /// Terminating structure /// Value type /// End value type /// [Pure] public static virtual K Items, END End)> ManyUntil2(K fa, K fend) { var empty = (END e) => (Seq.Empty, e); return go(); K Items, END End)> go() => empty * fend | Applicative.lift((x, p) => (x.Cons(p.Items), p.End), fa, memoK(go)); } /// /// `someUntil(fa, end)` applies `fa` _one_ or more times until `fend` succeeds. /// Returns the list of values returned by`fa`. `fend` result is consumed and /// lost. Use `someUntil2` if you wish to keep it. /// /// Structure to consume /// Terminating structure /// Value type /// End value type /// [Pure] public static virtual K> SomeUntil(K fa, K fend) => Applicative.lift(Seq.cons, fa, F.ManyUntil(fa, fend)); /// /// `someUntil2(fa, end)` applies `fa` _one_ or more times until `fend` succeeds. /// Returns the list of values returned by`fa` plus the `fend` result. /// /// Use `someUntil` if you don't wish to keep the `end` result. /// /// Structure to consume /// Terminating structure /// Value type /// End value type /// [Pure] public static virtual K Items, END End)> SomeUntil2(K fa, K fend) => Applicative.lift((x, p) => (x.Cons(p.Items), p.End), fa, F.ManyUntil2(fa, fend)); /// /// `option(x, fa)` tries to apply `fa`. If `fa` fails without 'consuming' anything, it /// returns `value`, otherwise the value returned by `fa`. /// /// /// The word 'consuming' is used here because this feature started life as a parser combinator, but it /// can be applied to any `Alternative` structure. Critically, most combinators only have a single flavour /// of failure. So, `option` just results in a default value being returned if `fa` fails. /// /// Default value to use if `fa` fails without 'consuming' anything /// /// /// [Pure] public static virtual K Option(A value, K fa) => fa | F.Pure(value); /// /// `sepBy(fa, sep) processes _zero_ or more occurrences of `fa`, separated by `sep`. /// /// Structure to yield return values /// Separator structure /// Value type /// Separator type /// List of values returned by `fa` [Pure] public static virtual K> SepBy(K fa, K sep) => F.SepBy1(fa, sep) | F.Pure(Seq.Empty); /// /// `sepBy(fa, sep) processes _one_ or more occurrences of `fa`, separated by `sep`. /// /// Structure to yield return values /// Separator structure /// Value type /// Separator type /// List of values returned by `fa` [Pure] public static virtual K> SepBy1(K fa, K sep) => Applicative.lift(Seq.cons, fa, F.Many(sep >>> fa)); /// /// `sepEndBy(fa, sep) processes _zero_ or more occurrences of `fa`, separated /// and optionally ended by `sep`. Returns a list of values returned by `fa`. /// /// Structure to yield return values /// Separator structure /// Value type /// Separator type /// List of values returned by `fa` [Pure] public static virtual K> SepByEnd(K fa, K sep) => F.SepByEnd1(fa, sep) | F.Pure(Seq.Empty); /// /// `sepEndBy1(fa, sep) processes _one_ or more occurrences of `fa`, separated /// and optionally ended by `sep`. Returns a list of values returned by `fa`. /// /// Structure to yield return values /// Separator structure /// Value type /// Separator type /// List of values returned by `fa` [Pure] public static virtual K> SepByEnd1(K fa, K sep) => Applicative.lift(Seq.cons, fa, sep >>> F.SepByEnd(fa, sep) | F.Pure(Seq.Empty)); /// /// Process `fa` _zero_ or more times and drop all yielded values. /// /// /// Run the applicative functor repeatedly until failure. /// Will always succeed. /// /// Applicative functor /// Unit [Pure] public static virtual K SkipMany(K fa) { return go(); K go() => fa >> memoK(go) | F.Pure(unit); } /// /// Process `fa` _one_ or more times and drop all yielded values. /// /// /// Run the applicative functor repeatedly until failure. At least one item must be yielded for overall success. /// /// Applicative functor /// Unit [Pure] public static virtual K SkipSome(K fa) => fa >>> F.SkipMany(fa); /// /// `skip(n, fa)` processes `n` occurrences of `fa`, skipping its result. /// If `n` is not positive, the process equates to `Pure(unit)`. /// /// Number of occurrences of `fa` to skip /// Applicative functor /// Value type /// [Pure] public static virtual K Skip(int n, K fa) => n switch { <= 0 => F.Pure(unit), _ => Applicative.lift((_, _) => unit, fa, F.Replicate(n - 1, fa)) }; /// /// `skipManyUntil(fa, fend)` applies the process `fa` _zero_ or more times /// skipping results until process `fend` succeeds. The resulting value from /// `fend` is then returned. /// /// Number of occurrences of `fa` to skip /// Applicative functor /// Value type /// [Pure] public static virtual K SkipManyUntil(K fa, K fend) { return go(); K go() => fend | fa >> memoK(go); } /// /// `skipManyUntil(fa, fend)` applies the process `fa` _one_ or more times /// skipping results until process `fend` succeeds. The resulting value from /// `fend` is then returned. /// /// Number of occurrences of `fa` to skip /// Applicative functor /// Value type /// [Pure] public static virtual K SkipSomeUntil(K fa, K fend) => fa >>> F.SkipManyUntil(fa, fend); static class Cached { public static readonly Func, Seq>> cons = static x => xs => x.Cons(xs); } } ================================================ FILE: LanguageExt.Core/Traits/Alternative/README.md ================================================ `Alternative` allows for propagation of 'failure' and 'choice' (in some appropriate sense, depending on the type), as well as provision of a unit/identity value (`Empty`). `Alternative` is a `Choice` and `MonoidK`, which means it has a `Choose` method, a `Combine` method (which defaults to calling the `Choose` method), and an `Empty` method. That creates a semantic meaning for `Choose`, which is about choice propagation rather than the broader meaning of `SemigroupK.Combine`. It also allows for `Choose` and `Combine` to have separate implementations depending on the type. The way to think about `Choose` and the inherited `SemigroupK.Combine` methods is: * `Choose` is the failure/choice propagation operator: `|` * `Combine` is the concatenation/combination/addition operator: `+` Any type that supports the `Alternative` trait should also implement the `|` operator, to enable easy choice/failure propagation. If there is a different implementation of `Combine` (rather than accepting the default), then the type should also implement the `+` operator. `AlternativeLaw` can help you test your implementation: choose(Pure(a), Pure(b)) = Pure(a) choose(Empty , Pure(b)) = Pure(b) choose(Pure(a), Empty ) = Pure(a) choose(Empty , Empty ) = Empty It also tests the `Applicative` and `Functor` laws. ================================================ FILE: LanguageExt.Core/Traits/Applicative/Act.cs ================================================ using System; namespace LanguageExt.Traits; public static class Act { public static readonly Func> fun = _ => y => y; } ================================================ FILE: LanguageExt.Core/Traits/Applicative/Applicative.Laws.cs ================================================ using System; using LanguageExt.Common; using static LanguageExt.Prelude; namespace LanguageExt.Traits; /// /// Functions that test that Applicative-functor laws hold for the `F` applicative-functor provided. /// /// /// * Homomorphism /// * Identity /// * Interchange /// * Applicative-Functor /// * Composition /// /// /// NOTE: `Equals` must be implemented for the `K〈F, *〉` derived-type, so that the laws /// can be proven to be true. If your applicative-functor doesn't have `Equals` then you must provide /// the optional `equals` parameter so that the equality of outcomes can be tested. /// /// Applicative functor type public static class ApplicativeLaw where F : Applicative { /// /// Assert that the applicative-functor laws hold /// /// /// NOTE: `Equals` must be implemented for the `K〈F, *〉` derived-type, so that the laws /// can be proven to be true. If your applicative-functor doesn't have `Equals` then you must provide /// the optional `equals` parameter so that the equality of outcomes can be tested. /// public static Unit assert(Func, K, bool>? equals = null) => validate(equals) .IfFail(errors => errors.Throw()); /// /// Validate that the applicative-functor laws hold /// /// /// NOTE: `Equals` must be implemented for the `K〈F, *〉` derived-type, so that the laws /// can be proven to be true. If your applicative-functor doesn't have `Equals` then you must provide /// the optional `equals` parameter so that the equality of outcomes can be tested. /// public static Validation validate(Func, K, bool>? equals = null) { equals ??= (fa, fb) => fa.Equals(fb); var fa = F.Pure(1); return FunctorLaw.validate(fa, equals) >> homomorphismLaw(equals) >> interchangeLaw(equals) >> compositionLaw(equals) >> functorLaw(equals) >> identityLaw(equals); } /// /// Validate the homomorphism law /// /// /// Homomorphism /// /// /// NOTE: `Equals` must be implemented for the `K〈F, *〉` derived-type, so that the laws /// can be proven to be true. If your applicative-functor doesn't have `Equals` then you must provide /// the optional `equals` parameter so that the equality of outcomes can be tested. /// public static Validation homomorphismLaw(Func, K, bool> equals) { var f = (int x) => x * 2; var a = 100; var ff = F.Pure(f); var fa = F.Pure(a); var lhs = ff.Apply(fa); var rhs = F.Pure(f(a)); return equals(lhs, rhs) ? unit : Error.New($"Applicative homomorphism law does not hold for {typeof(F).Name}"); } /// /// Validate the interchange law /// /// /// Interchange /// /// /// NOTE: `Equals` must be implemented for the `K〈F, *〉` derived-type, so that the laws /// can be proven to be true. If your applicative-functor doesn't have `Equals` then you must provide /// the optional `equals` parameter so that the equality of outcomes can be tested. /// public static Validation interchangeLaw(Func, K, bool> equals) { var ap = (int x) => (Func f) => f(x); var f = (int x) => x * 2; var a = 100; var ff = F.Pure(f); var fa = F.Pure(a); var lhs = ff.Apply(fa); var rhs = F.Pure(ap(a)).Apply(ff); return equals(lhs, rhs) ? unit : Error.New($"Applicative interchange law does not hold for {typeof(F).Name}"); } /// /// Validate the identity law /// /// /// Identity /// /// /// NOTE: `Equals` must be implemented for the `K〈F, *〉` derived-type, so that the laws /// can be proven to be true. If your applicative-functor doesn't have `Equals` then you must provide /// the optional `equals` parameter so that the equality of outcomes can be tested. /// public static Validation identityLaw(Func, K, bool> equals) { var lhs = F.Pure(100); var rhs = F.Pure>(identity).Apply(lhs); return equals(lhs, rhs) ? unit : Error.New($"Applicative identity law does not hold for {typeof(F).Name}"); } /// /// Validate the composition law /// /// /// Composition /// /// /// NOTE: `Equals` must be implemented for the `K〈F, *〉` derived-type, so that the laws /// can be proven to be true. If your functor doesn't have `Equals` then you must provide /// the optional `equals` parameter so that the equality of outcomes can be tested. /// public static Validation compositionLaw(Func, K, bool> equals) { // (b -> c) -> (a -> b) -> a -> c Func, Func, Func>> compose = g => f => a => g(f(a)); var xs = F.Pure(10); var us = F.Pure((int x) => x + 1); var vs = F.Pure((int x) => x * 2); var lhs = compose.Map(us).Apply(vs).Apply(xs); var rhs = us.Apply(vs.Apply(xs)); return equals(lhs, rhs) ? unit : Error.New($"Applicative composition law does not hold for {typeof(F).Name}"); } /// /// Validate the functor law /// /// /// Applicative-Functor /// /// /// NOTE: `Equals` must be implemented for the `K〈F, *〉` derived-type, so that the laws /// can be proven to be true. If your functor doesn't have `Equals` then you must provide /// the optional `equals` parameter so that the equality of outcomes can be tested. /// public static Validation functorLaw(Func, K, bool> equals) { var g = (int x) => x * 1; var x = F.Pure(10); var lhs = g.Map(x); var rhs = F.Pure(g).Apply(x); return equals(lhs, rhs) ? unit : Error.New($"Applicative functor law does not hold for {typeof(F).Name}"); } } ================================================ FILE: LanguageExt.Core/Traits/Applicative/Applicative.Trait.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics.Contracts; using System.Linq; using System.Threading.Tasks; using LanguageExt.Common; using static LanguageExt.Prelude; namespace LanguageExt.Traits; /// /// Applicative functor /// /// Functor trait type /// Bound value type public interface Applicative : Functor where F : Applicative { //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Abstract members // /// /// Lift a pure value into the applicative structure /// /// Value to lift /// Value type /// Constructed applicative structure [Pure] public static abstract K Pure(A value); /// /// Apply the function to the argument. /// /// /// This is like `delegate.Invoke` for lifted functions and lifted arguments. /// /// Lifted function /// Lifted argument /// Argument type /// Return type /// Applicative structure that represents the result of invoking the lifted function with /// the lifted argument [Pure] public static abstract K Apply(K> mf, K ma); /// /// Apply the function to the argument. /// This is like `delegate.Invoke` for lifted functions and lifted arguments. /// /// /// Uses memoisation for lazy and then cached evaluation of the argument. /// /// Lifted function /// Lifted argument /// Argument type /// Return type /// Applicative structure that represents the result of invoking the lifted function with /// the lifted argument [Pure] public static abstract K Apply(K> mf, Memo ma); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Default implementations // /// /// Apply the function to the argument. /// This is like `delegate.Invoke` for lifted functions and lifted arguments. /// /// /// Uses memoisation for lazy and then cached evaluation of the argument. /// /// Lifted function /// Lifted argument /// Argument type /// Return type /// Applicative structure that represents the result of invoking the lifted function with /// the lifted argument [Pure] public static virtual K Apply(Memo> mf, Memo ma) => mf.Value.Apply(ma); /// /// Apply the function to the argument. /// This is like `delegate.Invoke` for lifted functions and lifted arguments. /// /// /// Uses memoisation for lazy and then cached evaluation of the argument. /// /// Lifted function /// Lifted argument /// Argument type /// Return type /// Applicative structure that represents the result of invoking the lifted function with /// the lifted argument [Pure] public static virtual K Apply(Memo> mf, K ma) => mf.Value.Apply(ma); /// /// Applicative action. Computes the first applicative action and then computes the second. /// /// First applicative structure /// Second applicative structure /// First applicative structure bound value type /// Second applicative structure bound value type /// The result of the second applicative action (if there wasn't a failure beforehand) [Pure] public static virtual K Action(K ma, K mb) => Applicative.lift(_ => y => y, ma, mb); /// /// Applicative action. Computes the first applicative action and then computes the second. /// /// First applicative structure /// Second applicative structure /// First applicative structure bound value type /// Second applicative structure bound value type /// The result of the second applicative action (if there wasn't a failure beforehand) [Pure] public static virtual K BackAction(K ma, K mb) => Applicative.lift(x => _ => x, ma, mb); /// /// Applicative action. Computes the first applicative action and then computes the second. /// /// First applicative structure /// Second applicative structure /// First applicative structure bound value type /// Second applicative structure bound value type /// The result of the second applicative action (if there wasn't a failure beforehand) [Pure] public static virtual K Action(K ma, Memo mb) => Applicative.lift(_ => y => y, memoK(ma), mb); /// /// Applicative action. Computes the first applicative action and then computes the second. /// /// First applicative structure /// Second applicative structure /// First applicative structure bound value type /// Second applicative structure bound value type /// The result of the second applicative action (if there wasn't a failure beforehand) [Pure] public static virtual K BackAction(K ma, Memo mb) => Applicative.lift(x => _ => x, memoK(ma), mb); /// /// Applicative action. Computes the first applicative action and then computes the second. /// /// First applicative structure /// Second applicative structure /// First applicative structure bound value type /// Second applicative structure bound value type /// The result of the second applicative action (if there wasn't a failure beforehand) [Pure] public static virtual K Action(Memo ma, Memo mb) => Applicative.lift(_ => y => y, ma, mb); /// /// Applicative action. Computes the first applicative action and then computes the second. /// /// First applicative structure /// Second applicative structure /// First applicative structure bound value type /// Second applicative structure bound value type /// The result of the second applicative action (if there wasn't a failure beforehand) [Pure] public static virtual K BackAction(Memo ma, Memo mb) => Applicative.lift(x => _ => x, ma, mb); /// /// Applicative action. Computes the first applicative action and then computes the second. /// /// First applicative structure /// Second applicative structure /// First applicative structure bound value type /// Second applicative structure bound value type /// The result of the second applicative action (if there wasn't a failure beforehand) [Pure] public static virtual K Action(Memo ma, K mb) => Applicative.lift(_ => y => y, ma, memoK(mb)); /// /// Applicative action. Computes the first applicative action and then computes the second. /// /// First applicative structure /// Second applicative structure /// First applicative structure bound value type /// Second applicative structure bound value type /// The result of the second applicative action (if there wasn't a failure beforehand) [Pure] public static virtual K BackAction(Memo ma, K mb) => Applicative.lift(x => _ => x, ma, memoK(mb)); /// /// Chains a sequence of applicative actions /// /// /// Because this is an abstract chaining of actions, it can't actually run anything, and so if your /// actions are expected to have side effects (IO effects, for example), then you won't see them until /// the resulting `K〈F, A〉` is 'run'. /// /// This matters for infinite streams, where the result of `Actions` isn't realised at all, and so to /// avoid nothing happening (no side effects), you should override this function and unpack the IO /// type within, then run that enumerable of IOs. /// /// A good example is with the `Eff` type. It's a `ReaderT〈IO, A〉` internally: /// /// static K〈Eff〈RT〉, A〉 Actions〈A〉(IEnumerable〈K〈Eff〈RT〉, A〉〉 fas) =〉 /// new Eff〈RT, A〉( /// new ReaderT〈RT, IO, A〉( /// rt =〉fas.Select(fa =〉fa.RunIO(rt)).Actions())); /// /// /// Actions to chain /// Bound value type /// /// Sequence is empty [Pure] public static virtual K Actions(IterableNE> fas) => // TODO: Consider ways to make this not be blocking as a sensible default implementation fas.Tail.Fold(fas.Head, (h, t) => h.Action(t)); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Supplementary members // /// /// `between(open, close, p) parses `open`, followed by `p` and `close`. /// /// Open computation /// Close computation /// Between computation /// Return value type /// OPEN value type /// CLOSE value type /// The value returned by `p` [Pure] public static virtual K Between( K open, K close, K p) => open.Action(p).BackAction(close); /// /// Construct a sequence of `count` repetitions of `fa` /// /// Number of repetitions /// Applicative computation to run /// Value type /// Applicative structure of `count` items [Pure] public static virtual K> Replicate(int count, K fa) => count switch { <= 0 => F.Pure>([]), _ => Applicative.lift(Seq.cons, fa, F.Replicate(count - 1, fa)) }; } ================================================ FILE: LanguageExt.Core/Traits/Applicative/Extensions/Applicative.Extensions.Action.cs ================================================ using System.Diagnostics.Contracts; using LanguageExt.Traits; namespace LanguageExt; public static partial class ApplicativeExtensions { extension(K ma) where F : Applicative { [Pure] public K Action(K mb) => F.Action(ma, mb); public K BackAction(K mb) => F.BackAction(ma, mb); } extension(Memo ma) where F : Applicative { [Pure] public K Action(K mb) => F.Action(ma, mb); public K BackAction(K mb) => F.BackAction(ma, mb); } [Pure] public static K Actions(this IterableNE> ma) where F : Applicative => F.Actions(ma); } ================================================ FILE: LanguageExt.Core/Traits/Applicative/Extensions/Applicative.Extensions.Apply.cs ================================================ using System; using LanguageExt.Traits; using System.Diagnostics.Contracts; using static LanguageExt.Prelude; namespace LanguageExt; public static partial class ApplicativeExtensions { extension(K> mf) where AF : Applicative { [Pure] public K Apply(K ma) => AF.Apply(mf, ma); } extension(K> mf) where AF : Applicative { [Pure] public K> Apply(K ma) => AF.Apply(AF.Map(curry, mf), ma); } extension(K> mf) where AF : Applicative { [Pure] public K>> Apply(K ma) => AF.Apply(AF.Map(curry, mf), ma); } extension(K> mf) where AF : Applicative { [Pure] public K>>> Apply(K ma) => AF.Apply(AF.Map(curry, mf), ma); } extension(K> mf) where AF : Applicative { [Pure] public K>>>> Apply(K ma) => AF.Apply(AF.Map(curry, mf), ma); } extension(K> mf) where AF : Applicative { [Pure] public K>>>>> Apply(K ma) => AF.Apply(AF.Map(curry, mf), ma); } extension(K> mf) where AF : Applicative { [Pure] public K>>>>>> Apply(K ma) => AF.Apply(AF.Map(curry, mf), ma); } extension(K> mf) where AF : Applicative { [Pure] public K>>>>>>> Apply(K ma) => AF.Apply(AF.Map(curry, mf), ma); } extension(K> mf) where AF : Applicative { [Pure] public K>>>>>>>> Apply(K ma) => AF.Apply(AF.Map(curry, mf), ma); } extension(K> mf) where AF : Applicative { [Pure] public K>>>>>>>>> Apply(K ma) => AF.Apply(AF.Map(curry, mf), ma); } [Pure] public static K Apply( this (K, K) items, Func f) where Fnctr : Applicative => f.Map(items.Item1) .Apply(items.Item2); [Pure] public static K Apply( this (K, K, K) items, Func f) where Fnctr : Applicative => f.Map(items.Item1) .Apply(items.Item2) .Apply(items.Item3); [Pure] public static K Apply( this (K, K, K, K) items, Func f) where Fnctr : Applicative => f.Map(items.Item1) .Apply(items.Item2) .Apply(items.Item3) .Apply(items.Item4); [Pure] public static K Apply( this (K, K, K, K, K) items, Func f) where Fnctr : Applicative => f.Map(items.Item1) .Apply(items.Item2) .Apply(items.Item3) .Apply(items.Item4) .Apply(items.Item5); [Pure] public static K Apply( this (K, K, K, K, K, K) items, Func f) where Fnctr : Applicative => f.Map(items.Item1) .Apply(items.Item2) .Apply(items.Item3) .Apply(items.Item4) .Apply(items.Item5) .Apply(items.Item6); [Pure] public static K Apply( this (K, K, K, K, K, K, K) items, Func f) where Fnctr : Applicative => f.Map(items.Item1) .Apply(items.Item2) .Apply(items.Item3) .Apply(items.Item4) .Apply(items.Item5) .Apply(items.Item6) .Apply(items.Item7); [Pure] public static K Apply( this (K, K, K, K, K, K, K, K) items, Func f) where Fnctr : Applicative => f.Map(items.Item1) .Apply(items.Item2) .Apply(items.Item3) .Apply(items.Item4) .Apply(items.Item5) .Apply(items.Item6) .Apply(items.Item7) .Apply(items.Item8); [Pure] public static K Apply( this (K, K, K, K, K, K, K, K, K) items, Func f) where Fnctr : Applicative => f.Map(items.Item1) .Apply(items.Item2) .Apply(items.Item3) .Apply(items.Item4) .Apply(items.Item5) .Apply(items.Item6) .Apply(items.Item7) .Apply(items.Item8) .Apply(items.Item9); [Pure] public static K Apply( this (K, K, K, K, K, K, K, K, K, K) items, Func f) where Fnctr : Applicative => f.Map(items.Item1) .Apply(items.Item2) .Apply(items.Item3) .Apply(items.Item4) .Apply(items.Item5) .Apply(items.Item6) .Apply(items.Item7) .Apply(items.Item8) .Apply(items.Item9) .Apply(items.Item10); } ================================================ FILE: LanguageExt.Core/Traits/Applicative/Extensions/Applicative.Extensions.ApplyM.cs ================================================ using System; using System.Diagnostics.Contracts; using LanguageExt.Traits; namespace LanguageExt; public static partial class ApplicativeExtensions { extension((K, K) items) where M : Monad { [Pure] public K ApplyM(Func> f) => items.Apply(f).Flatten(); } extension((K, K, K) items) where M : Monad { [Pure] public K ApplyM(Func> f) => items.Apply(f).Flatten(); } extension((K, K, K, K) items) where M : Monad { [Pure] public K ApplyM(Func> f) => items.Apply(f).Flatten(); } extension((K, K, K, K, K) items) where M : Monad { [Pure] public K ApplyM(Func> f) => items.Apply(f).Flatten(); } extension((K, K, K, K, K, K) items) where M : Monad { [Pure] public K ApplyM(Func> f) => items.Apply(f).Flatten(); } extension((K, K, K, K, K, K, K) items) where M : Monad { [Pure] public K ApplyM(Func> f) => items.Apply(f).Flatten(); } extension((K, K, K, K, K, K, K, K) items) where M : Monad { [Pure] public K ApplyM(Func> f) => items.Apply(f).Flatten(); } extension((K, K, K, K, K, K, K, K, K) items) where M : Monad { [Pure] public K ApplyM(Func> f) => items.Apply(f).Flatten(); } extension((K, K, K, K, K, K, K, K, K, K) items) where M : Monad { [Pure] public K ApplyM(Func> f) => items.Apply(f).Flatten(); } } ================================================ FILE: LanguageExt.Core/Traits/Applicative/Extensions/Applicative.Extensions.Arithmetic.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics.Contracts; using System.Numerics; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; public static partial class ApplicativeExtensions { /// /// Sum the bound values of the applicative structures provided /// /// Num of A /// Bound value type /// Left hand side of the operation /// Right hand side of the operation /// An applicative structure with the arithmetic operation applied to the bound values. [Pure] public static K Add(this K fa, K fb) where F : Applicative where NumA : Num => (fa, fb).Apply(NumA.Add); /// /// Sum the bound values of the applicative structures provided /// /// Num of A /// Bound value type /// Left hand side of the operation /// Right hand side of the operation /// An applicative structure with the arithmetic operation applied to the bound values. [Pure] public static K Add(this K fa, K fb) where F : Applicative where A : IAdditionOperators => (fa, fb).Apply((x, y) => x + y); /// /// Subtract the bound values of the applicative structures provided /// /// Num of A /// Bound value type /// Left hand side of the operation /// Right hand side of the operation /// An applicative structure with the arithmetic operation applied to the bound values. [Pure] public static K Subtract(this K fa, K fb) where F : Applicative where NumA : Arithmetic => (fa, fb).Apply(NumA.Subtract); /// /// Subtract the bound values of the applicative structures provided /// /// Num of A /// Bound value type /// Left hand side of the operation /// Right hand side of the operation /// An applicative structure with the arithmetic operation applied to the bound values. [Pure] public static K Subtract(this K fa, K fb) where F : Applicative where A : ISubtractionOperators => (fa, fb).Apply((x, y) => x - y); /// /// Multiply the bound values of the applicative structures provided /// /// Num of A /// Bound value type /// Left hand side of the operation /// Right hand side of the operation /// An applicative structure with the arithmetic operation applied to the bound values. [Pure] public static K Multiply(this K fa, K fb) where F : Applicative where NumA : Arithmetic => (fa, fb).Apply(NumA.Multiply); /// /// Multiply the bound values of the applicative structures provided /// /// Num of A /// Bound value type /// Left hand side of the operation /// Right hand side of the operation /// An applicative structure with the arithmetic operation applied to the bound values. [Pure] public static K Multiply(this K fa, K fb) where F : Applicative where A : IMultiplyOperators => (fa, fb).Apply((x, y) => x * y); /// /// Multiply the bound values of the applicative structures provided /// /// Num of A /// Bound value type /// Left hand side of the operation /// Right hand side of the operation /// An applicative structure with the arithmetic operation applied to the bound values. [Pure] public static K Divide(this K fa, K fb) where F : Applicative where NumA : Num => (fa, fb).Apply(NumA.Divide); /// /// Multiply the bound values of the applicative structures provided /// /// Num of A /// Bound value type /// Left hand side of the operation /// Right hand side of the operation /// An applicative structure with the arithmetic operation applied to the bound values. [Pure] public static K Divide(this K fa, K fb) where F : Applicative where A : IDivisionOperators => (fa, fb).Apply((x, y) => x / y); } ================================================ FILE: LanguageExt.Core/Traits/Applicative/Extensions/Applicative.Extensions.Lift.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics.Contracts; using System.Numerics; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; public static partial class ApplicativeExtensions { [Pure] public static K Lift(this Func f, K fa) where F : Applicative => F.Pure(f).Apply(fa); [Pure] public static K Lift(this Func f, K fa, K fb) where F : Applicative => F.Pure(f).Apply(fa).Apply(fb); [Pure] public static K Lift(this Func> f, K fa, K fb) where F : Applicative => F.Pure(f).Apply(fa).Apply(fb); [Pure] public static K Lift(this Func f, K fa, K fb, K fc) where F : Applicative => F.Pure(f).Apply(fa).Apply(fb).Apply(fc); [Pure] public static K Lift(this Func>> f, K fa, K fb, K fc) where F : Applicative => F.Pure(f).Apply(fa).Apply(fb).Apply(fc); } ================================================ FILE: LanguageExt.Core/Traits/Applicative/Extensions/Applicative.Extensions.Memo.Action.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics.Contracts; using System.Numerics; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; public static partial class ApplicativeExtensions { extension(K ma) where F : Applicative { [Pure] public K Action(Memo mb) => F.Action(ma, mb); [Pure] public K BackAction(Memo mb) => F.BackAction(ma, mb); } extension(Memo ma) where F : Applicative { [Pure] public K Action(Memo mb) => F.Action(ma, mb); [Pure] public K BackAction(Memo mb) => F.BackAction(ma, mb); } } ================================================ FILE: LanguageExt.Core/Traits/Applicative/Extensions/Applicative.Extensions.Memo.Apply.Tuple.cs ================================================ using System; using LanguageExt.Traits; using System.Diagnostics.Contracts; namespace LanguageExt; public static partial class ApplicativeExtensions { extension((Memo, Memo) items) where AF : Applicative { [Pure] public K Apply(Func f) => f.Map(items.Item1) .Apply(items.Item2); } extension((Memo, Memo, Memo) items) where AF : Applicative { [Pure] public K Apply(Func f) => f.Map(items.Item1) .Apply(items.Item2) .Apply(items.Item3); } extension((Memo, Memo, Memo, Memo) items) where AF : Applicative { [Pure] public K Apply(Func f) => f.Map(items.Item1) .Apply(items.Item2) .Apply(items.Item3) .Apply(items.Item4); } extension((Memo, Memo, Memo, Memo, Memo) items) where AF : Applicative { [Pure] public K Apply(Func f) => f.Map(items.Item1) .Apply(items.Item2) .Apply(items.Item3) .Apply(items.Item4) .Apply(items.Item5); } extension((Memo, Memo, Memo, Memo, Memo, Memo) items) where AF : Applicative { [Pure] public K Apply(Func f) => f.Map(items.Item1) .Apply(items.Item2) .Apply(items.Item3) .Apply(items.Item4) .Apply(items.Item5) .Apply(items.Item6); } extension((Memo, Memo, Memo, Memo, Memo, Memo, Memo) items) where AF : Applicative { [Pure] public K Apply(Func f) => f.Map(items.Item1) .Apply(items.Item2) .Apply(items.Item3) .Apply(items.Item4) .Apply(items.Item5) .Apply(items.Item6) .Apply(items.Item7); } extension((Memo, Memo, Memo, Memo, Memo, Memo, Memo, Memo) items) where AF : Applicative { [Pure] public K Apply(Func f) => f.Map(items.Item1) .Apply(items.Item2) .Apply(items.Item3) .Apply(items.Item4) .Apply(items.Item5) .Apply(items.Item6) .Apply(items.Item7) .Apply(items.Item8); } extension((Memo, Memo, Memo, Memo, Memo, Memo, Memo, Memo, Memo) items) where AF : Applicative { [Pure] public K Apply(Func f) => f.Map(items.Item1) .Apply(items.Item2) .Apply(items.Item3) .Apply(items.Item4) .Apply(items.Item5) .Apply(items.Item6) .Apply(items.Item7) .Apply(items.Item8) .Apply(items.Item9); } extension((Memo, Memo, Memo, Memo, Memo, Memo, Memo, Memo, Memo, Memo) items) where AF : Applicative { [Pure] public K Apply(Func f) => f.Map(items.Item1) .Apply(items.Item2) .Apply(items.Item3) .Apply(items.Item4) .Apply(items.Item5) .Apply(items.Item6) .Apply(items.Item7) .Apply(items.Item8) .Apply(items.Item9) .Apply(items.Item10); } } ================================================ FILE: LanguageExt.Core/Traits/Applicative/Extensions/Applicative.Extensions.Memo.Apply.cs ================================================ using System; using LanguageExt.Traits; using System.Diagnostics.Contracts; using static LanguageExt.Prelude; namespace LanguageExt; public static partial class ApplicativeExtensions { extension(K> mf) where AF : Applicative { [Pure] public K Apply(Memo ma) => AF.Apply(mf, ma); } extension(Memo> mf) where AF : Applicative { [Pure] public K Apply(Memo ma) => AF.Apply(mf, ma); } extension(K> mf) where AF : Applicative { [Pure] public K> Apply(Memo ma) => AF.Apply(AF.Map(curry, mf), ma); } extension(Memo> mf) where AF : Applicative { [Pure] public K> Apply(Memo ma) => AF.Apply(AF.Map(curry, mf), ma); } extension(K> mf) where AF : Applicative { [Pure] public K>> Apply(Memo ma) => AF.Apply(AF.Map(curry, mf), ma); } extension(Memo> mf) where AF : Applicative { [Pure] public K>> Apply(Memo ma) => AF.Apply(AF.Map(curry, mf), ma); } extension(K> mf) where AF : Applicative { [Pure] public K>>> Apply(Memo ma) => AF.Apply(AF.Map(curry, mf), ma); } extension(Memo> mf) where AF : Applicative { [Pure] public K>>> Apply(Memo ma) => AF.Apply(AF.Map(curry, mf), ma); } extension(K> mf) where AF : Applicative { [Pure] public K>>>> Apply(Memo ma) => AF.Apply(AF.Map(curry, mf), ma); } extension(Memo> mf) where AF : Applicative { [Pure] public K>>>> Apply(Memo ma) => AF.Apply(AF.Map(curry, mf), ma); } extension(K> mf) where AF : Applicative { [Pure] public K>>>>> Apply(Memo ma) => AF.Apply(AF.Map(curry, mf), ma); } extension(Memo> mf) where AF : Applicative { [Pure] public K>>>>> Apply(Memo ma) => AF.Apply(AF.Map(curry, mf), ma); } extension(K> mf) where AF : Applicative { [Pure] public K>>>>>> Apply(Memo ma) => AF.Apply(AF.Map(curry, mf), ma); } extension(Memo> mf) where AF : Applicative { [Pure] public K>>>>>> Apply(Memo ma) => AF.Apply(AF.Map(curry, mf), ma); } extension(K> mf) where AF : Applicative { [Pure] public K>>>>>>> Apply(Memo ma) => AF.Apply(AF.Map(curry, mf), ma); } extension(Memo> mf) where AF : Applicative { [Pure] public K>>>>>>> Apply(Memo ma) => AF.Apply(AF.Map(curry, mf), ma); } extension(K> mf) where AF : Applicative { [Pure] public K>>>>>>>> Apply(Memo ma) => AF.Apply(AF.Map(curry, mf), ma); } extension(Memo> mf) where AF : Applicative { [Pure] public K>>>>>>>> Apply(Memo ma) => AF.Apply(AF.Map(curry, mf), ma); } extension(K> mf) where AF : Applicative { [Pure] public K>>>>>>>>> Apply(Memo ma) => AF.Apply(AF.Map(curry, mf), ma); } extension(Memo> mf) where AF : Applicative { [Pure] public K>>>>>>>>> Apply(Memo ma) => AF.Apply(AF.Map(curry, mf), ma); } } ================================================ FILE: LanguageExt.Core/Traits/Applicative/Extensions/Applicative.Extensions.Memo.Arithmetic.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics.Contracts; using System.Numerics; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; public static partial class ApplicativeExtensions { /// /// Sum the bound values of the applicative structures provided /// /// Num of A /// Bound value type /// Left hand side of the operation /// Right hand side of the operation /// An applicative structure with the arithmetic operation applied to the bound values. [Pure] public static K Add(this Memo fa, Memo fb) where F : Applicative where NumA : Num => (fa, fb).Apply(NumA.Add); /// /// Sum the bound values of the applicative structures provided /// /// Num of A /// Bound value type /// Left hand side of the operation /// Right hand side of the operation /// An applicative structure with the arithmetic operation applied to the bound values. [Pure] public static K Add(this Memo fa, Memo fb) where F : Applicative where A : IAdditionOperators => (fa, fb).Apply((x, y) => x + y); /// /// Subtract the bound values of the applicative structures provided /// /// Num of A /// Bound value type /// Left hand side of the operation /// Right hand side of the operation /// An applicative structure with the arithmetic operation applied to the bound values. [Pure] public static K Subtract(this Memo fa, Memo fb) where F : Applicative where NumA : Arithmetic => (fa, fb).Apply(NumA.Subtract); /// /// Subtract the bound values of the applicative structures provided /// /// Num of A /// Bound value type /// Left hand side of the operation /// Right hand side of the operation /// An applicative structure with the arithmetic operation applied to the bound values. [Pure] public static K Subtract(this Memo fa, Memo fb) where F : Applicative where A : ISubtractionOperators => (fa, fb).Apply((x, y) => x - y); /// /// Multiply the bound values of the applicative structures provided /// /// Num of A /// Bound value type /// Left hand side of the operation /// Right hand side of the operation /// An applicative structure with the arithmetic operation applied to the bound values. [Pure] public static K Multiply(this Memo fa, Memo fb) where F : Applicative where NumA : Arithmetic => (fa, fb).Apply(NumA.Multiply); /// /// Multiply the bound values of the applicative structures provided /// /// Num of A /// Bound value type /// Left hand side of the operation /// Right hand side of the operation /// An applicative structure with the arithmetic operation applied to the bound values. [Pure] public static K Multiply(this Memo fa, Memo fb) where F : Applicative where A : IMultiplyOperators => (fa, fb).Apply((x, y) => x * y); /// /// Multiply the bound values of the applicative structures provided /// /// Num of A /// Bound value type /// Left hand side of the operation /// Right hand side of the operation /// An applicative structure with the arithmetic operation applied to the bound values. [Pure] public static K Divide(this Memo fa, Memo fb) where F : Applicative where NumA : Num => (fa, fb).Apply(NumA.Divide); /// /// Multiply the bound values of the applicative structures provided /// /// Num of A /// Bound value type /// Left hand side of the operation /// Right hand side of the operation /// An applicative structure with the arithmetic operation applied to the bound values. [Pure] public static K Divide(this Memo fa, Memo fb) where F : Applicative where A : IDivisionOperators => (fa, fb).Apply((x, y) => x / y); } ================================================ FILE: LanguageExt.Core/Traits/Applicative/Extensions/Applicative.Extensions.Memo.Lift.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics.Contracts; using System.Numerics; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; public static partial class ApplicativeExtensions { [Pure] public static K Lift(this Func f, Memo fa) where F : Applicative => F.Pure(f).Apply(fa); [Pure] public static K Lift(this Func f, Memo fa, Memo fb) where F : Applicative => F.Pure(f).Apply(fa).Apply(fb); [Pure] public static K Lift(this Func> f, Memo fa, Memo fb) where F : Applicative => F.Pure(f).Apply(fa).Apply(fb); [Pure] public static K Lift(this Func f, Memo fa, Memo fb, Memo fc) where F : Applicative => F.Pure(f).Apply(fa).Apply(fb).Apply(fc); [Pure] public static K Lift(this Func>> f, Memo fa, Memo fb, Memo fc) where F : Applicative => F.Pure(f).Apply(fa).Apply(fb).Apply(fc); } ================================================ FILE: LanguageExt.Core/Traits/Applicative/Extensions/Applicative.Extensions.Memo.Zip.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics.Contracts; using System.Numerics; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; public static partial class ApplicativeExtensions { /// Tuple of applicatives to run /// Applicative trait type /// First applicative's bound value type /// Second applicative's bound value type extension((Memo First, Memo Second) tuple) where F : Applicative { /// /// Zips applicatives into a tuple /// /// Zipped applicative public K Zip() => map((A a, B b) => (a, b), tuple.First).Apply(tuple.Second); } /// Tuple of applicatives to run /// Applicative trait type /// First applicative's bound value type /// Second applicative's bound value type /// Third applicative's bound value type extension((Memo First, Memo Second, Memo Third) tuple) where F : Applicative { /// /// Zips applicatives into a tuple /// /// Zipped applicative public K Zip() => map((A a, B b, C c) => (a, b, c), tuple.First) .Apply(tuple.Second) .Apply(tuple.Third); } /// Tuple of applicatives to run /// Applicative trait type /// First applicative's bound value type /// Second applicative's bound value type /// Third applicative's bound value type /// Fourth applicative's bound value type extension((Memo First, Memo Second, Memo Third, Memo Fourth) tuple) where F : Applicative { /// /// Zips applicatives into a tuple /// /// Zipped applicative public K Zip() => map((A a, B b, C c, D d) => (a, b, c, d), tuple.First) .Apply(tuple.Second) .Apply(tuple.Third) .Apply(tuple.Fourth); } /// Tuple of applicatives to run /// Applicative trait type /// First applicative's bound value type /// Second applicative's bound value type /// Third applicative's bound value type /// Fourth applicative's bound value type extension((Memo First, Memo Second, Memo Third, Memo Fourth, Memo Fifth) tuple) where F : Applicative { /// /// Zips applicatives into a tuple /// /// Zipped applicative public K Zip() => map((A a, B b, C c, D d, E e) => (a, b, c, d, e), tuple.First) .Apply(tuple.Second) .Apply(tuple.Third) .Apply(tuple.Fourth) .Apply(tuple.Fifth); } /// First applicative /// Applicative trait type /// First applicative's bound value type extension(Memo First) where F : Applicative { /// /// Zips applicatives into a tuple /// /// Second applicative /// Second applicative's bound value type /// Zipped applicative public K Zip(Memo Second) => map((A a, B b) => (a, b), First).Apply(Second); /// /// Zips applicatives into a tuple /// /// Second applicative /// Third applicative /// Second applicative's bound value type /// Third applicative's bound value type /// Zipped applicative public K Zip(Memo Second, Memo Third) => map((A a, B b, C c) => (a, b, c), First) .Apply(Second) .Apply(Third); /// /// Zips applicatives into a tuple /// /// Second applicative /// Third applicative /// Fourth applicative /// Second applicative's bound value type /// Third applicative's bound value type /// Fourth applicative's bound value type /// Zipped applicative public K Zip(Memo Second, Memo Third, Memo Fourth) => map((A a, B b, C c, D d) => (a, b, c, d), First) .Apply(Second) .Apply(Third) .Apply(Fourth); /// /// Zips applicatives into a tuple /// /// Second applicative /// Third applicative /// Fourth applicative /// Fifth applicative /// Second applicative's bound value type /// Third applicative's bound value type /// Fourth applicative's bound value type /// Zipped applicative public K Zip(Memo Second, Memo Third, Memo Fourth, Memo Fifth) => map((A a, B b, C c, D d, E e) => (a, b, c, d, e), First) .Apply(Second) .Apply(Third) .Apply(Fourth) .Apply(Fifth); } } ================================================ FILE: LanguageExt.Core/Traits/Applicative/Extensions/Applicative.Extensions.Zip.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics.Contracts; using System.Numerics; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; public static partial class ApplicativeExtensions { /// Tuple of applicatives to run /// Applicative trait type /// First applicative's bound value type /// Second applicative's bound value type extension((K First, K Second) tuple) where F : Applicative { /// /// Zips applicatives into a tuple /// /// Zipped applicative public K Zip() => map((A a, B b) => (a, b), tuple.First).Apply(tuple.Second); } /// Tuple of applicatives to run /// Applicative trait type /// First applicative's bound value type /// Second applicative's bound value type /// Third applicative's bound value type extension((K First, K Second, K Third) tuple) where F : Applicative { /// /// Zips applicatives into a tuple /// /// Zipped applicative public K Zip() => map((A a, B b, C c) => (a, b, c), tuple.First) .Apply(tuple.Second) .Apply(tuple.Third); } /// Tuple of applicatives to run /// Applicative trait type /// First applicative's bound value type /// Second applicative's bound value type /// Third applicative's bound value type /// Fourth applicative's bound value type extension((K First, K Second, K Third, K Fourth) tuple) where F : Applicative { /// /// Zips applicatives into a tuple /// /// Zipped applicative public K Zip() => map((A a, B b, C c, D d) => (a, b, c, d), tuple.First) .Apply(tuple.Second) .Apply(tuple.Third) .Apply(tuple.Fourth); } /// Tuple of applicatives to run /// Applicative trait type /// First applicative's bound value type /// Second applicative's bound value type /// Third applicative's bound value type /// Fourth applicative's bound value type extension((K First, K Second, K Third, K Fourth, K Fifth) tuple) where F : Applicative { /// /// Zips applicatives into a tuple /// /// Zipped applicative public K Zip() => map((A a, B b, C c, D d, E e) => (a, b, c, d, e), tuple.First) .Apply(tuple.Second) .Apply(tuple.Third) .Apply(tuple.Fourth) .Apply(tuple.Fifth); } /// First applicative /// Applicative trait type /// First applicative's bound value type extension(K First) where F : Applicative { /// /// Zips applicatives into a tuple /// /// Second applicative /// Second applicative's bound value type /// Zipped applicative public K Zip(K Second) => map((A a, B b) => (a, b), First).Apply(Second); /// /// Zips applicatives into a tuple /// /// Second applicative /// Third applicative /// Second applicative's bound value type /// Third applicative's bound value type /// Zipped applicative public K Zip(K Second, K Third) => map((A a, B b, C c) => (a, b, c), First) .Apply(Second) .Apply(Third); /// /// Zips applicatives into a tuple /// /// Second applicative /// Third applicative /// Fourth applicative /// Second applicative's bound value type /// Third applicative's bound value type /// Fourth applicative's bound value type /// Zipped applicative public K Zip(K Second, K Third, K Fourth) => map((A a, B b, C c, D d) => (a, b, c, d), First) .Apply(Second) .Apply(Third) .Apply(Fourth); /// /// Zips applicatives into a tuple /// /// Second applicative /// Third applicative /// Fourth applicative /// Fifth applicative /// Second applicative's bound value type /// Third applicative's bound value type /// Fourth applicative's bound value type /// Zipped applicative public K Zip(K Second, K Third, K Fourth, K Fifth) => map((A a, B b, C c, D d, E e) => (a, b, c, d, e), First) .Apply(Second) .Apply(Third) .Apply(Fourth) .Apply(Fifth); } } ================================================ FILE: LanguageExt.Core/Traits/Applicative/Extensions/Applicative.Extensions.cs ================================================ using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; public static partial class ApplicativeExtensions { } ================================================ FILE: LanguageExt.Core/Traits/Applicative/Module/Applicative.Module.Action.cs ================================================ using System.Collections.Generic; using System.Diagnostics.Contracts; namespace LanguageExt.Traits; public static partial class Applicative { [Pure] public static K action(K ma, K mb) where F : Applicative => F.Action(ma, mb); [Pure] public static K actions(IterableNE> ma) where F : Applicative => F.Actions(ma); } ================================================ FILE: LanguageExt.Core/Traits/Applicative/Module/Applicative.Module.Apply.cs ================================================ using System; using System.Diagnostics.Contracts; using static LanguageExt.Prelude; namespace LanguageExt.Traits; public static partial class Applicative { [Pure] public static K apply(K> mf, K ma) where AF : Applicative => AF.Apply(mf, ma); [Pure] public static K> apply(K> mf, K ma) where AF : Applicative => AF.Apply(AF.Map(curry, mf), ma); [Pure] public static K>> apply(K> mf, K ma) where AF : Applicative => AF.Apply(AF.Map(curry, mf), ma); [Pure] public static K>>> apply(K> mf, K ma) where AF : Applicative => AF.Apply(AF.Map(curry, mf), ma); [Pure] public static K>>>> apply(K> mf, K ma) where AF : Applicative => AF.Apply(AF.Map(curry, mf), ma); [Pure] public static K>>>>> apply(K> mf, K ma) where AF : Applicative => AF.Apply(AF.Map(curry, mf), ma); [Pure] public static K>>>>>> apply(K> mf, K ma) where AF : Applicative => AF.Apply(AF.Map(curry, mf), ma); [Pure] public static K>>>>>>> apply(K> mf, K ma) where AF : Applicative => AF.Apply(AF.Map(curry, mf), ma); [Pure] public static K>>>>>>>> apply(K> mf, K ma) where AF : Applicative => AF.Apply(AF.Map(curry, mf), ma); [Pure] public static K>>>>>>>>> apply(K> mf, K ma) where AF : Applicative => AF.Apply(AF.Map(curry, mf), ma); [Pure] public static K apply( (K, K) items, Func f) where AF : Applicative => f.Map(items.Item1) .Apply(items.Item2); [Pure] public static K apply( (K, K, K) items, Func f) where AF : Applicative => f.Map(items.Item1) .Apply(items.Item2) .Apply(items.Item3); [Pure] public static K apply( (K, K, K, K) items, Func f) where AF : Applicative => f.Map(items.Item1) .Apply(items.Item2) .Apply(items.Item3) .Apply(items.Item4); [Pure] public static K apply( (K, K, K, K, K) items, Func f) where AF : Applicative => f.Map(items.Item1) .Apply(items.Item2) .Apply(items.Item3) .Apply(items.Item4) .Apply(items.Item5); [Pure] public static K apply( (K, K, K, K, K, K) items, Func f) where AF : Applicative => f.Map(items.Item1) .Apply(items.Item2) .Apply(items.Item3) .Apply(items.Item4) .Apply(items.Item5) .Apply(items.Item6); [Pure] public static K apply( (K, K, K, K, K, K, K) items, Func f) where AF : Applicative => f.Map(items.Item1) .Apply(items.Item2) .Apply(items.Item3) .Apply(items.Item4) .Apply(items.Item5) .Apply(items.Item6) .Apply(items.Item7); [Pure] public static K apply( (K, K, K, K, K, K, K, K) items, Func f) where AF : Applicative => f.Map(items.Item1) .Apply(items.Item2) .Apply(items.Item3) .Apply(items.Item4) .Apply(items.Item5) .Apply(items.Item6) .Apply(items.Item7) .Apply(items.Item8); [Pure] public static K apply( (K, K, K, K, K, K, K, K, K) items, Func f) where AF : Applicative => f.Map(items.Item1) .Apply(items.Item2) .Apply(items.Item3) .Apply(items.Item4) .Apply(items.Item5) .Apply(items.Item6) .Apply(items.Item7) .Apply(items.Item8) .Apply(items.Item9); [Pure] public static K apply( (K, K, K, K, K, K, K, K, K, K) items, Func f) where AF : Applicative => f.Map(items.Item1) .Apply(items.Item2) .Apply(items.Item3) .Apply(items.Item4) .Apply(items.Item5) .Apply(items.Item6) .Apply(items.Item7) .Apply(items.Item8) .Apply(items.Item9) .Apply(items.Item10); } ================================================ FILE: LanguageExt.Core/Traits/Applicative/Module/Applicative.Module.ApplyM.cs ================================================ using System; using System.Diagnostics.Contracts; namespace LanguageExt.Traits; public static partial class Applicative { [Pure] public static K applyM((K, K) items, Func> f) where M : Monad => items.Apply(f).Flatten(); [Pure] public static K applyM((K, K, K) items, Func> f) where M : Monad => items.Apply(f).Flatten(); [Pure] public static K applyM((K, K, K, K) items, Func> f) where M : Monad => items.Apply(f).Flatten(); [Pure] public static K applyM((K, K, K, K, K) items, Func> f) where M : Monad => items.Apply(f).Flatten(); [Pure] public static K applyM((K, K, K, K, K, K) items, Func> f) where M : Monad => items.Apply(f).Flatten(); [Pure] public static K applyM((K, K, K, K, K, K, K) items, Func> f) where M : Monad => items.Apply(f).Flatten(); [Pure] public static K applyM((K, K, K, K, K, K, K, K) items, Func> f) where M : Monad => items.Apply(f).Flatten(); [Pure] public static K applyM((K, K, K, K, K, K, K, K, K) items, Func> f) where M : Monad => items.Apply(f).Flatten(); [Pure] public static K applyM((K, K, K, K, K, K, K, K, K, K) items, Func> f) where M : Monad => items.Apply(f).Flatten(); } ================================================ FILE: LanguageExt.Core/Traits/Applicative/Module/Applicative.Module.Arithmetic.cs ================================================ using System.Diagnostics.Contracts; using System.Numerics; namespace LanguageExt.Traits; public static partial class Applicative { /// /// Sum the bound values of the applicative structures provided /// /// Num of A /// Bound value type /// Left hand side of the operation /// Right hand side of the operation /// An applicative structure with the arithmetic operation applied to the bound values. [Pure] public static K add(K fa, K fb) where F : Applicative where NumA : Num => (fa, fb).Apply(NumA.Add); /// /// Sum the bound values of the applicative structures provided /// /// Num of A /// Bound value type /// Left hand side of the operation /// Right hand side of the operation /// An applicative structure with the arithmetic operation applied to the bound values. [Pure] public static K add(K fa, K fb) where F : Applicative where A : IAdditionOperators => (fa, fb).Apply((x, y) => x + y); /// /// Subtract the bound values of the applicative structures provided /// /// Num of A /// Bound value type /// Left hand side of the operation /// Right hand side of the operation /// An applicative structure with the arithmetic operation applied to the bound values. [Pure] public static K subtract(K fa, K fb) where F : Applicative where NumA : Arithmetic => (fa, fb).Apply(NumA.Subtract); /// /// Subtract the bound values of the applicative structures provided /// /// Num of A /// Bound value type /// Left hand side of the operation /// Right hand side of the operation /// An applicative structure with the arithmetic operation applied to the bound values. [Pure] public static K subtract(K fa, K fb) where F : Applicative where A : ISubtractionOperators => (fa, fb).Apply((x, y) => x - y); /// /// Multiply the bound values of the applicative structures provided /// /// Num of A /// Bound value type /// Left hand side of the operation /// Right hand side of the operation /// An applicative structure with the arithmetic operation applied to the bound values. [Pure] public static K multiply(K fa, K fb) where F : Applicative where NumA : Arithmetic => (fa, fb).Apply(NumA.Multiply); /// /// Multiply the bound values of the applicative structures provided /// /// Num of A /// Bound value type /// Left hand side of the operation /// Right hand side of the operation /// An applicative structure with the arithmetic operation applied to the bound values. [Pure] public static K multiply(K fa, K fb) where F : Applicative where A : IMultiplyOperators => (fa, fb).Apply((x, y) => x * y); /// /// Multiply the bound values of the applicative structures provided /// /// Num of A /// Bound value type /// Left hand side of the operation /// Right hand side of the operation /// An applicative structure with the arithmetic operation applied to the bound values. [Pure] public static K divide(K fa, K fb) where F : Applicative where NumA : Num => (fa, fb).Apply(NumA.Divide); /// /// Multiply the bound values of the applicative structures provided /// /// Num of A /// Bound value type /// Left hand side of the operation /// Right hand side of the operation /// An applicative structure with the arithmetic operation applied to the bound values. [Pure] public static K divide(K fa, K fb) where F : Applicative where A : IDivisionOperators => (fa, fb).Apply((x, y) => x / y); } ================================================ FILE: LanguageExt.Core/Traits/Applicative/Module/Applicative.Module.Lift.cs ================================================ using System; using System.Diagnostics.Contracts; namespace LanguageExt.Traits; public static partial class Applicative { [Pure] public static K lift(Func f, K fa) where F : Applicative => F.Pure(f).Apply(fa); [Pure] public static K lift(Func f, K fa, K fb) where F : Applicative => F.Pure(f).Apply(fa).Apply(fb); [Pure] public static K lift(Func> f, K fa, K fb) where F : Applicative => F.Pure(f).Apply(fa).Apply(fb); [Pure] public static K lift(Func f, K fa, K fb, K fc) where F : Applicative => F.Pure(f).Apply(fa).Apply(fb).Apply(fc); [Pure] public static K lift(Func>> f, K fa, K fb, K fc) where F : Applicative => F.Pure(f).Apply(fa).Apply(fb).Apply(fc); } ================================================ FILE: LanguageExt.Core/Traits/Applicative/Module/Applicative.Module.Memo.Action.cs ================================================ using System.Diagnostics.Contracts; namespace LanguageExt.Traits; public static partial class Applicative { [Pure] public static K action(K ma, Memo mb) where F : Applicative => F.Action(ma, mb); [Pure] public static K action(Memo ma, Memo mb) where F : Applicative => F.Action(ma, mb); [Pure] public static K action(Memo ma, K mb) where F : Applicative => F.Action(ma, mb); } ================================================ FILE: LanguageExt.Core/Traits/Applicative/Module/Applicative.Module.Memo.Apply.cs ================================================ using System; using System.Diagnostics.Contracts; using static LanguageExt.Prelude; namespace LanguageExt.Traits; public static partial class Applicative { [Pure] public static K apply(K> mf, Memo ma) where AF : Applicative => AF.Apply(mf, ma); [Pure] public static K> apply(K> mf, Memo ma) where AF : Applicative => AF.Apply(AF.Map(curry, mf), ma); [Pure] public static K>> apply(K> mf, Memo ma) where AF : Applicative => AF.Apply(AF.Map(curry, mf), ma); [Pure] public static K>>> apply(K> mf, Memo ma) where AF : Applicative => AF.Apply(AF.Map(curry, mf), ma); [Pure] public static K>>>> apply(K> mf, Memo ma) where AF : Applicative => AF.Apply(AF.Map(curry, mf), ma); [Pure] public static K>>>>> apply(K> mf, Memo ma) where AF : Applicative => AF.Apply(AF.Map(curry, mf), ma); [Pure] public static K>>>>>> apply(K> mf, Memo ma) where AF : Applicative => AF.Apply(AF.Map(curry, mf), ma); [Pure] public static K>>>>>>> apply(K> mf, Memo ma) where AF : Applicative => AF.Apply(AF.Map(curry, mf), ma); [Pure] public static K>>>>>>>> apply(K> mf, Memo ma) where AF : Applicative => AF.Apply(AF.Map(curry, mf), ma); [Pure] public static K>>>>>>>>> apply(K> mf, Memo ma) where AF : Applicative => AF.Apply(AF.Map(curry, mf), ma); [Pure] public static K apply( (Memo, Memo) items, Func f) where AF : Applicative => f.Map(items.Item1) .Apply(items.Item2); [Pure] public static K apply( (Memo, Memo, Memo) items, Func f) where AF : Applicative => f.Map(items.Item1) .Apply(items.Item2) .Apply(items.Item3); [Pure] public static K apply( (Memo, Memo, Memo, Memo) items, Func f) where AF : Applicative => f.Map(items.Item1) .Apply(items.Item2) .Apply(items.Item3) .Apply(items.Item4); [Pure] public static K apply( (Memo, Memo, Memo, Memo, Memo) items, Func f) where AF : Applicative => f.Map(items.Item1) .Apply(items.Item2) .Apply(items.Item3) .Apply(items.Item4) .Apply(items.Item5); [Pure] public static K apply( (Memo, Memo, Memo, Memo, Memo, Memo) items, Func f) where AF : Applicative => f.Map(items.Item1) .Apply(items.Item2) .Apply(items.Item3) .Apply(items.Item4) .Apply(items.Item5) .Apply(items.Item6); [Pure] public static K apply( (Memo, Memo, Memo, Memo, Memo, Memo, Memo) items, Func f) where AF : Applicative => f.Map(items.Item1) .Apply(items.Item2) .Apply(items.Item3) .Apply(items.Item4) .Apply(items.Item5) .Apply(items.Item6) .Apply(items.Item7); [Pure] public static K apply( (Memo, Memo, Memo, Memo, Memo, Memo, Memo, Memo) items, Func f) where AF : Applicative => f.Map(items.Item1) .Apply(items.Item2) .Apply(items.Item3) .Apply(items.Item4) .Apply(items.Item5) .Apply(items.Item6) .Apply(items.Item7) .Apply(items.Item8); [Pure] public static K apply( (Memo, Memo, Memo, Memo, Memo, Memo, Memo, Memo, Memo) items, Func f) where AF : Applicative => f.Map(items.Item1) .Apply(items.Item2) .Apply(items.Item3) .Apply(items.Item4) .Apply(items.Item5) .Apply(items.Item6) .Apply(items.Item7) .Apply(items.Item8) .Apply(items.Item9); [Pure] public static K apply( (Memo, Memo, Memo, Memo, Memo, Memo, Memo, Memo, Memo, Memo) items, Func f) where AF : Applicative => f.Map(items.Item1) .Apply(items.Item2) .Apply(items.Item3) .Apply(items.Item4) .Apply(items.Item5) .Apply(items.Item6) .Apply(items.Item7) .Apply(items.Item8) .Apply(items.Item9) .Apply(items.Item10); } ================================================ FILE: LanguageExt.Core/Traits/Applicative/Module/Applicative.Module.Memo.ApplyM.cs ================================================ using System; using System.Diagnostics.Contracts; namespace LanguageExt.Traits; public static partial class Applicative { [Pure] public static K applyM((Memo, Memo) items, Func> f) where M : Monad => items.Apply(f).Flatten(); [Pure] public static K applyM((Memo, Memo, Memo) items, Func> f) where M : Monad => items.Apply(f).Flatten(); [Pure] public static K applyM((Memo, Memo, Memo, Memo) items, Func> f) where M : Monad => items.Apply(f).Flatten(); [Pure] public static K applyM( (Memo, Memo, Memo, Memo, Memo) items, Func> f) where M : Monad => items.Apply(f).Flatten(); [Pure] public static K applyM( (Memo, Memo, Memo, Memo, Memo, Memo) items, Func> f) where M : Monad => items.Apply(f).Flatten(); [Pure] public static K applyM( (Memo, Memo, Memo, Memo, Memo, Memo, Memo) items, Func> f) where M : Monad => items.Apply(f).Flatten(); [Pure] public static K applyM( (Memo, Memo, Memo, Memo, Memo, Memo, Memo, Memo) items, Func> f) where M : Monad => items.Apply(f).Flatten(); [Pure] public static K applyM( (Memo, Memo, Memo, Memo, Memo, Memo, Memo, Memo, Memo) items, Func> f) where M : Monad => items.Apply(f).Flatten(); [Pure] public static K applyM( (Memo, Memo, Memo, Memo, Memo, Memo, Memo, Memo, Memo, Memo) items, Func> f) where M : Monad => items.Apply(f).Flatten(); } ================================================ FILE: LanguageExt.Core/Traits/Applicative/Module/Applicative.Module.Memo.Arithmetic.cs ================================================ using System.Diagnostics.Contracts; using System.Numerics; namespace LanguageExt.Traits; public static partial class Applicative { /// /// Sum the bound values of the applicative structures provided /// /// Num of A /// Bound value type /// Left hand side of the operation /// Right hand side of the operation /// An applicative structure with the arithmetic operation applied to the bound values. [Pure] public static K add(Memo fa, Memo fb) where F : Applicative where NumA : Num => (fa, fb).Apply(NumA.Add); /// /// Sum the bound values of the applicative structures provided /// /// Num of A /// Bound value type /// Left hand side of the operation /// Right hand side of the operation /// An applicative structure with the arithmetic operation applied to the bound values. [Pure] public static K add(Memo fa, Memo fb) where F : Applicative where A : IAdditionOperators => (fa, fb).Apply((x, y) => x + y); /// /// Subtract the bound values of the applicative structures provided /// /// Num of A /// Bound value type /// Left hand side of the operation /// Right hand side of the operation /// An applicative structure with the arithmetic operation applied to the bound values. [Pure] public static K subtract(Memo fa, Memo fb) where F : Applicative where NumA : Arithmetic => (fa, fb).Apply(NumA.Subtract); /// /// Subtract the bound values of the applicative structures provided /// /// Num of A /// Bound value type /// Left hand side of the operation /// Right hand side of the operation /// An applicative structure with the arithmetic operation applied to the bound values. [Pure] public static K subtract(Memo fa, Memo fb) where F : Applicative where A : ISubtractionOperators => (fa, fb).Apply((x, y) => x - y); /// /// Multiply the bound values of the applicative structures provided /// /// Num of A /// Bound value type /// Left hand side of the operation /// Right hand side of the operation /// An applicative structure with the arithmetic operation applied to the bound values. [Pure] public static K multiply(Memo fa, Memo fb) where F : Applicative where NumA : Arithmetic => (fa, fb).Apply(NumA.Multiply); /// /// Multiply the bound values of the applicative structures provided /// /// Num of A /// Bound value type /// Left hand side of the operation /// Right hand side of the operation /// An applicative structure with the arithmetic operation applied to the bound values. [Pure] public static K multiply(Memo fa, Memo fb) where F : Applicative where A : IMultiplyOperators => (fa, fb).Apply((x, y) => x * y); /// /// Multiply the bound values of the applicative structures provided /// /// Num of A /// Bound value type /// Left hand side of the operation /// Right hand side of the operation /// An applicative structure with the arithmetic operation applied to the bound values. [Pure] public static K divide(Memo fa, Memo fb) where F : Applicative where NumA : Num => (fa, fb).Apply(NumA.Divide); /// /// Multiply the bound values of the applicative structures provided /// /// Num of A /// Bound value type /// Left hand side of the operation /// Right hand side of the operation /// An applicative structure with the arithmetic operation applied to the bound values. [Pure] public static K divide(Memo fa, Memo fb) where F : Applicative where A : IDivisionOperators => (fa, fb).Apply((x, y) => x / y); } ================================================ FILE: LanguageExt.Core/Traits/Applicative/Module/Applicative.Module.Memo.Lift.cs ================================================ using System; using System.Diagnostics.Contracts; namespace LanguageExt.Traits; public static partial class Applicative { [Pure] public static K lift(Func f, Memo fa) where F : Applicative => F.Pure(f).Apply(fa); [Pure] public static K lift(Func f, Memo fa, Memo fb) where F : Applicative => F.Pure(f).Apply(fa).Apply(fb); [Pure] public static K lift(Func f, K fa, Memo fb) where F : Applicative => F.Pure(f).Apply(fa).Apply(fb); [Pure] public static K lift(Func> f, Memo fa, Memo fb) where F : Applicative => F.Pure(f).Apply(fa).Apply(fb); [Pure] public static K lift(Func> f, K fa, Memo fb) where F : Applicative => F.Pure(f).Apply(fa).Apply(fb); [Pure] public static K lift(Func f, Memo fa, Memo fb, Memo fc) where F : Applicative => F.Pure(f).Apply(fa).Apply(fb).Apply(fc); [Pure] public static K lift(Func f, K fa, Memo fb, Memo fc) where F : Applicative => F.Pure(f).Apply(fa).Apply(fb).Apply(fc); [Pure] public static K lift(Func f, Memo fa, K fb, Memo fc) where F : Applicative => F.Pure(f).Apply(fa).Apply(fb).Apply(fc); [Pure] public static K lift(Func f, Memo fa, Memo fb, K fc) where F : Applicative => F.Pure(f).Apply(fa).Apply(fb).Apply(fc); [Pure] public static K lift(Func f, K fa, K fb, Memo fc) where F : Applicative => F.Pure(f).Apply(fa).Apply(fb).Apply(fc); [Pure] public static K lift(Func f, Memo fa, K fb, K fc) where F : Applicative => F.Pure(f).Apply(fa).Apply(fb).Apply(fc); [Pure] public static K lift(Func>> f, Memo fa, Memo fb, Memo fc) where F : Applicative => F.Pure(f).Apply(fa).Apply(fb).Apply(fc); [Pure] public static K lift(Func>> f, K fa, Memo fb, Memo fc) where F : Applicative => F.Pure(f).Apply(fa).Apply(fb).Apply(fc); [Pure] public static K lift(Func>> f, Memo fa, K fb, Memo fc) where F : Applicative => F.Pure(f).Apply(fa).Apply(fb).Apply(fc); [Pure] public static K lift(Func>> f, Memo fa, Memo fb, K fc) where F : Applicative => F.Pure(f).Apply(fa).Apply(fb).Apply(fc); [Pure] public static K lift(Func>> f, K fa, K fb, Memo fc) where F : Applicative => F.Pure(f).Apply(fa).Apply(fb).Apply(fc); [Pure] public static K lift(Func>> f, Memo fa, K fb, K fc) where F : Applicative => F.Pure(f).Apply(fa).Apply(fb).Apply(fc); } ================================================ FILE: LanguageExt.Core/Traits/Applicative/Module/Applicative.Module.Zip.cs ================================================ using static LanguageExt.Prelude; namespace LanguageExt.Traits; public static partial class Applicative { /// /// Zips applicatives into a tuple /// /// Tuple of applicatives to run /// Applicative trait type /// First applicative's bound value type /// Second applicative's bound value type /// Zipped applicative public static K zip((K First, K Second) tuple) where F : Applicative => map((A a, B b) => (a, b), tuple.First).Apply(tuple.Second); /// /// Zips applicatives into a tuple /// /// Tuple of applicatives to run /// Applicative trait type /// First applicative's bound value type /// Second applicative's bound value type /// Third applicative's bound value type /// Zipped applicative public static K zip((K First, K Second, K Third) tuple) where F : Applicative => map((A a, B b, C c) => (a, b, c), tuple.First) .Apply(tuple.Second) .Apply(tuple.Third); /// /// Zips applicatives into a tuple /// /// Tuple of applicatives to run /// Applicative trait type /// First applicative's bound value type /// Second applicative's bound value type /// Third applicative's bound value type /// Fourth applicative's bound value type /// Zipped applicative public static K zip((K First, K Second, K Third, K Fourth) tuple) where F : Applicative => map((A a, B b, C c, D d) => (a, b, c, d), tuple.First) .Apply(tuple.Second) .Apply(tuple.Third) .Apply(tuple.Fourth); /// /// Zips applicatives into a tuple /// /// Tuple of applicatives to run /// Applicative trait type /// First applicative's bound value type /// Second applicative's bound value type /// Third applicative's bound value type /// Fourth applicative's bound value type /// Zipped applicative public static K zip((K First, K Second, K Third, K Fourth, K Fifth) tuple) where F : Applicative => map((A a, B b, C c, D d, E e) => (a, b, c, d, e), tuple.First) .Apply(tuple.Second) .Apply(tuple.Third) .Apply(tuple.Fourth) .Apply(tuple.Fifth); /// /// Zips applicatives into a tuple /// /// Second applicative /// First applicative /// Second applicative's bound value type /// Applicative trait type /// First applicative's bound value type /// Zipped applicative public static K zip(K First, K Second) where F : Applicative => map((A a, B b) => (a, b), First).Apply(Second); /// /// Zips applicatives into a tuple /// /// Second applicative /// Third applicative /// First applicative /// Second applicative's bound value type /// Third applicative's bound value type /// Applicative trait type /// First applicative's bound value type /// Zipped applicative public static K zip(K First, K Second, K Third) where F : Applicative => map((A a, B b, C c) => (a, b, c), First) .Apply(Second) .Apply(Third); /// /// Zips applicatives into a tuple /// /// Second applicative /// Third applicative /// Fourth applicative /// First applicative /// Second applicative's bound value type /// Third applicative's bound value type /// Fourth applicative's bound value type /// Applicative trait type /// First applicative's bound value type /// Zipped applicative public static K zip(K First, K Second, K Third, K Fourth) where F : Applicative => map((A a, B b, C c, D d) => (a, b, c, d), First) .Apply(Second) .Apply(Third) .Apply(Fourth); /// /// Zips applicatives into a tuple /// /// Second applicative /// Third applicative /// Fourth applicative /// Fifth applicative /// First applicative /// Second applicative's bound value type /// Third applicative's bound value type /// Fourth applicative's bound value type /// Applicative trait type /// First applicative's bound value type /// Zipped applicative public static K zip(K First, K Second, K Third, K Fourth, K Fifth) where F : Applicative => map((A a, B b, C c, D d, E e) => (a, b, c, d, e), First) .Apply(Second) .Apply(Third) .Apply(Fourth) .Apply(Fifth); } ================================================ FILE: LanguageExt.Core/Traits/Applicative/Module/Applicative.Module.cs ================================================ using System.Diagnostics.Contracts; namespace LanguageExt.Traits; public static partial class Applicative { [Pure] public static K pure(A value) where F : Applicative => F.Pure(value); [Pure] public static K when(bool flag, K fx) where F : Applicative => flag ? fx : F.Pure(default); [Pure] public static K unless(bool flag, K fx) where F : Applicative => when(!flag, fx); /// /// `between(open, close, p) parses `open`, followed by `p` and `close`. /// /// Open computation /// Close computation /// Between computation /// Return value type /// OPEN value type /// CLOSE value type /// The value returned by `p` [Pure] public static K between( K open, K close, K p) where F : Applicative => F.Between(open, close, p); /// /// Construct a sequence of `count` repetitions of `fa` /// /// Number of repetitions /// Applicative computation to run /// Value type /// Applicative structure of `count` items [Pure] public static K> replicate(int count, K fa) where F : Applicative => F.Replicate(count, fa); } ================================================ FILE: LanguageExt.Core/Traits/Applicative/Operators/Applicative.Memo.Operators.cs ================================================ using System; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; public static partial class ApplicativeExtensions { extension(K self) where M : Applicative { /// /// Applicative sequence operator /// public static K operator >> (K ma, Memo mb) => M.Action(ma, mb); /// /// Applicative sequence operator /// public static K operator << (K ma, Memo mb) => Applicative.lift((x, _) => x, memoK(ma), mb); /// /// Applicative apply operator /// public static K operator * (K ma, Memo> mf) => M.Apply(mf, ma); /// /// Applicative apply operator /// public static K operator * (Memo> mf, K ma) => M.Apply(mf, ma); } extension(Memo self) where M : Applicative { /// /// Applicative apply operator /// public static K operator * (Memo ma, K> mf) => M.Apply(mf, ma); /// /// Applicative apply operator /// public static K operator * (Memo ma, Memo> mf) => M.Apply(mf, ma); /// /// Applicative apply operator /// public static K operator * (K> mf, Memo ma) => M.Apply(mf, ma); /// /// Applicative apply operator /// public static K operator * (Memo> mf, Memo ma) => M.Apply(mf, ma); } extension(K self) where M : Applicative { /// /// Applicative apply operator /// public static K> operator * ( Memo> mf, K ma) => curry * mf * ma; /// /// Applicative apply operator /// public static K> operator * ( K ma, Memo> mf) => curry * mf * ma; } extension(Memo self) where M : Applicative { /// /// Applicative apply operator /// public static K> operator * ( Memo> mf, Memo ma) => curry * mf * ma; /// /// Applicative apply operator /// public static K> operator * ( K> mf, Memo ma) => curry * mf * ma; /// /// Applicative apply operator /// public static K> operator * ( Memo ma, Memo> mf) => curry * mf * ma; /// /// Applicative apply operator /// public static K> operator * ( Memo ma, K> mf) => curry * mf * ma; } extension(K self) where M : Applicative { /// /// Applicative apply operator /// public static K>> operator * ( Memo> mf, K ma) => curry * mf * ma; /// /// Applicative apply operator /// public static K>> operator * ( K ma, Memo> mf) => curry * mf * ma; } extension(Memo self) where M : Applicative { /// /// Applicative apply operator /// public static K>> operator * ( Memo> mf, Memo ma) => curry * mf * ma; /// /// Applicative apply operator /// public static K>> operator * ( K> mf, Memo ma) => curry * mf * ma; /// /// Applicative apply operator /// public static K>> operator * ( Memo ma, Memo> mf) => curry * mf * ma; /// /// Applicative apply operator /// public static K>> operator * ( Memo ma, K> mf) => curry * mf * ma; } extension(K self) where M : Applicative { /// /// Applicative apply operator /// public static K>>> operator * ( Memo> mf, K ma) => curry * mf * ma; /// /// Applicative apply operator /// public static K>>> operator * ( K ma, Memo> mf) => curry * mf * ma; } extension(Memo self) where M : Applicative { /// /// Applicative apply operator /// public static K>>> operator * ( Memo> mf, Memo ma) => curry * mf * ma; /// /// Applicative apply operator /// public static K>>> operator * ( K> mf, Memo ma) => curry * mf * ma; /// /// Applicative apply operator /// public static K>>> operator * ( Memo ma, Memo> mf) => curry * mf * ma; /// /// Applicative apply operator /// public static K>>> operator * ( Memo ma, K> mf) => curry * mf * ma; } extension(K self) where M : Applicative { /// /// Applicative apply operator /// public static K>>>> operator * ( Memo> mf, K ma) => curry * mf * ma; /// /// Applicative apply operator /// public static K>>>> operator * ( K ma, Memo> mf) => curry * mf * ma; } extension(Memo self) where M : Applicative { /// /// Applicative apply operator /// public static K>>>> operator * ( Memo> mf, Memo ma) => curry * mf * ma; /// /// Applicative apply operator /// public static K>>>> operator * ( K> mf, Memo ma) => curry * mf * ma; /// /// Applicative apply operator /// public static K>>>> operator * ( Memo ma, Memo> mf) => curry * mf * ma; /// /// Applicative apply operator /// public static K>>>> operator * ( Memo ma, K> mf) => curry * mf * ma; } extension(K self) where M : Applicative { /// /// Applicative apply operator /// public static K>>>>> operator * ( Memo> mf, K ma) => curry * mf * ma; /// /// Applicative apply operator /// public static K>>>>> operator * ( K ma, Memo> mf) => curry * mf * ma; } extension(Memo self) where M : Applicative { /// /// Applicative apply operator /// public static K>>>>> operator * ( Memo> mf, Memo ma) => curry * mf * ma; /// /// Applicative apply operator /// public static K>>>>> operator * ( K> mf, Memo ma) => curry * mf * ma; /// /// Applicative apply operator /// public static K>>>>> operator * ( Memo ma, Memo> mf) => curry * mf * ma; /// /// Applicative apply operator /// public static K>>>>> operator * ( Memo ma, K> mf) => curry * mf * ma; } extension(K self) where M : Applicative { /// /// Applicative apply operator /// public static K>>>>>> operator * ( Memo> mf, K ma) => curry * mf * ma; /// /// Applicative apply operator /// public static K>>>>>> operator * ( K ma, Memo> mf) => curry * mf * ma; } extension(Memo self) where M : Applicative { /// /// Applicative apply operator /// public static K>>>>>> operator * ( Memo> mf, Memo ma) => curry * mf * ma; /// /// Applicative apply operator /// public static K>>>>>> operator * ( K> mf, Memo ma) => curry * mf * ma; /// /// Applicative apply operator /// public static K>>>>>> operator * ( Memo ma, Memo> mf) => curry * mf * ma; /// /// Applicative apply operator /// public static K>>>>>> operator * ( Memo ma, K> mf) => curry * mf * ma; } extension(K self) where M : Applicative { /// /// Applicative apply operator /// public static K>>>>>>> operator * ( Memo> mf, K ma) => curry * mf * ma; /// /// Applicative apply operator /// public static K>>>>>>> operator * ( K ma, Memo> mf) => curry * mf * ma; } extension(Memo self) where M : Applicative { /// /// Applicative apply operator /// public static K>>>>>>> operator * ( Memo> mf, Memo ma) => curry * mf * ma; /// /// Applicative apply operator /// public static K>>>>>>> operator * ( K> mf, Memo ma) => curry * mf * ma; /// /// Applicative apply operator /// public static K>>>>>>> operator * ( Memo ma, Memo> mf) => curry * mf * ma; /// /// Applicative apply operator /// public static K>>>>>>> operator * ( Memo ma, K> mf) => curry * mf * ma; } extension(K self) where M : Applicative { /// /// Applicative apply operator /// public static K>>>>>>>> operator * ( Memo> mf, K ma) => curry * mf * ma; /// /// Applicative apply operator /// public static K>>>>>>>> operator * ( K ma, Memo> mf) => curry * mf * ma; } extension(Memo self) where M : Applicative { /// /// Applicative apply operator /// public static K>>>>>>>> operator * ( Memo> mf, Memo ma) => curry * mf * ma; /// /// Applicative apply operator /// public static K>>>>>>>> operator * ( K> mf, Memo ma) => curry * mf * ma; /// /// Applicative apply operator /// public static K>>>>>>>> operator * ( Memo ma, Memo> mf) => curry * mf * ma; /// /// Applicative apply operator /// public static K>>>>>>>> operator * ( Memo ma, K> mf) => curry * mf * ma; } extension(K self) where M : Applicative { /// /// Applicative apply operator /// public static K>>>>>>>>> operator * ( Memo> mf, K ma) => curry * mf * ma; /// /// Applicative apply operator /// public static K>>>>>>>>> operator * ( K ma, Memo> mf) => curry * mf * ma; } extension(Memo self) where M : Applicative { /// /// Applicative apply operator /// public static K>>>>>>>>> operator * ( Memo> mf, Memo ma) => curry * mf * ma; /// /// Applicative apply operator /// public static K>>>>>>>>> operator * ( K> mf, Memo ma) => curry * mf * ma; /// /// Applicative apply operator /// public static K>>>>>>>>> operator * ( Memo ma, Memo> mf) => curry * mf * ma; /// /// Applicative apply operator /// public static K>>>>>>>>> operator * ( Memo ma, K> mf) => curry * mf * ma; } } ================================================ FILE: LanguageExt.Core/Traits/Applicative/Operators/Applicative.Operators.cs ================================================ using System; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; public static partial class ApplicativeExtensions { extension(K self) where M : Applicative { /// /// Applicative sequence operator /// public static K operator >>> (K ma, K mb) => M.Action(ma, mb); /// /// Applicative apply operator /// public static K operator * (K> mf, K ma) => M.Apply(mf, ma); /// /// Applicative apply operator /// public static K operator * (K ma, K> mf) => M.Apply(mf, ma); } extension(K self) where M : Applicative { /// /// Applicative apply operator /// public static K> operator * ( K> mf, K ma) => curry * mf * ma; /// /// Applicative apply operator /// public static K> operator * ( K ma, K> mf) => curry * mf * ma; } extension(K self) where M : Applicative { /// /// Applicative apply operator /// public static K>> operator * ( K> mf, K ma) => curry * mf * ma; /// /// Applicative apply operator /// public static K>> operator * ( K ma, K> mf) => curry * mf * ma; } extension(K self) where M : Applicative { /// /// Applicative apply operator /// public static K>>> operator * ( K> mf, K ma) => curry * mf * ma; /// /// Applicative apply operator /// public static K>>> operator * ( K ma, K> mf) => curry * mf * ma; } extension(K self) where M : Applicative { /// /// Applicative apply operator /// public static K>>>> operator * ( K> mf, K ma) => curry * mf * ma; /// /// Applicative apply operator /// public static K>>>> operator * ( K ma, K> mf) => curry * mf * ma; } extension(K self) where M : Applicative { /// /// Applicative apply operator /// public static K>>>>> operator * ( K> mf, K ma) => curry * mf * ma; /// /// Applicative apply operator /// public static K>>>>> operator * ( K ma, K> mf) => curry * mf * ma; } extension(K self) where M : Applicative { /// /// Applicative apply operator /// public static K>>>>>> operator * ( K> mf, K ma) => curry * mf * ma; /// /// Applicative apply operator /// public static K>>>>>> operator * ( K ma, K> mf) => curry * mf * ma; } extension(K self) where M : Applicative { /// /// Applicative apply operator /// public static K>>>>>>> operator * ( K> mf, K ma) => curry * mf * ma; /// /// Applicative apply operator /// public static K>>>>>>> operator * ( K ma, K> mf) => curry * mf * ma; } extension(K self) where M : Applicative { /// /// Applicative apply operator /// public static K>>>>>>>> operator * ( K> mf, K ma) => curry * mf * ma; /// /// Applicative apply operator /// public static K>>>>>>>> operator * ( K ma, K> mf) => curry * mf * ma; } extension(K self) where M : Applicative { /// /// Applicative apply operator /// public static K>>>>>>>>> operator * ( K> mf, K ma) => curry * mf * ma; /// /// Applicative apply operator /// public static K>>>>>>>>> operator * ( K ma, K> mf) => curry * mf * ma; } } ================================================ FILE: LanguageExt.Core/Traits/Applicative/Prelude/Applicative.Prelude.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics.Contracts; using LanguageExt.Traits; namespace LanguageExt; public static partial class Prelude { /// /// Construct an applicative structure from a pure value /// /// Pure value to lift into the applicative structure /// Bound value type /// Applicative structure [Pure] public static K pure(A value) where F : Applicative => F.Pure(value); [Pure] public static K applyM(K>> mf, K ma) where M : Monad => M.Apply(mf, ma).Flatten(); [Pure] public static K apply(K> mf, K ma) where AF : Applicative => AF.Apply(mf, ma); [Pure] public static K> apply(K> mf, K ma) where AF : Applicative => AF.Apply(AF.Map(curry, mf), ma); [Pure] public static K>> apply(K> mf, K ma) where AF : Applicative => AF.Apply(AF.Map(curry, mf), ma); [Pure] public static K>>> apply(K> mf, K ma) where AF : Applicative => AF.Apply(AF.Map(curry, mf), ma); [Pure] public static K>>>> apply(K> mf, K ma) where AF : Applicative => AF.Apply(AF.Map(curry, mf), ma); [Pure] public static K>>>>> apply(K> mf, K ma) where AF : Applicative => AF.Apply(AF.Map(curry, mf), ma); [Pure] public static K>>>>>> apply(K> mf, K ma) where AF : Applicative => AF.Apply(AF.Map(curry, mf), ma); [Pure] public static K>>>>>>> apply(K> mf, K ma) where AF : Applicative => AF.Apply(AF.Map(curry, mf), ma); [Pure] public static K>>>>>>>> apply(K> mf, K ma) where AF : Applicative => AF.Apply(AF.Map(curry, mf), ma); [Pure] public static K>>>>>>>>> apply(K> mf, K ma) where AF : Applicative => AF.Apply(AF.Map(curry, mf), ma); [Pure] public static K action(K ma, K mb) where F : Applicative => F.Action(ma, mb); [Pure] public static K actions(IterableNE> ma) where F : Applicative => F.Actions(ma); //////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Zipping // /// /// Zips applicatives into a tuple /// /// Tuple of applicatives to run /// Applicative trait type /// First applicative's bound value type /// Second applicative's bound value type /// Zipped applicative public static K zip( (K First, K Second) tuple) where F : Applicative => map((A a, B b) => (a, b), tuple.First).Apply(tuple.Second); /// /// Zips applicatives into a tuple /// /// Tuple of applicatives to run /// Applicative trait type /// First applicative's bound value type /// Second applicative's bound value type /// Third applicative's bound value type /// Zipped applicative public static K zip( (K First, K Second, K Third) tuple) where F : Applicative => map((A a, B b, C c) => (a, b, c), tuple.First) .Apply(tuple.Second) .Apply(tuple.Third); /// /// Zips applicatives into a tuple /// /// Tuple of applicatives to run /// Applicative trait type /// First applicative's bound value type /// Second applicative's bound value type /// Third applicative's bound value type /// Fourth applicative's bound value type /// Zipped applicative public static K zip( (K First, K Second, K Third, K Fourth) tuple) where F : Applicative => map((A a, B b, C c, D d) => (a, b, c, d), tuple.First) .Apply(tuple.Second) .Apply(tuple.Third) .Apply(tuple.Fourth); /// /// Zips applicatives into a tuple /// /// Tuple of applicatives to run /// Applicative trait type /// First applicative's bound value type /// Second applicative's bound value type /// Third applicative's bound value type /// Fourth applicative's bound value type /// Zipped applicative public static K zip( (K First, K Second, K Third, K Fourth, K Fifth) tuple) where F : Applicative => map((A a, B b, C c, D d, E e) => (a, b, c, d, e), tuple.First) .Apply(tuple.Second) .Apply(tuple.Third) .Apply(tuple.Fourth) .Apply(tuple.Fifth); /// /// Zips applicatives into a tuple /// /// First applicative /// Second applicative /// Applicative trait type /// First applicative's bound value type /// Second applicative's bound value type /// Zipped applicative public static K zip( K First, K Second) where F : Applicative => map((A a, B b) => (a, b), First).Apply(Second); /// /// Zips applicatives into a tuple /// /// First applicative /// Second applicative /// Third applicative /// Applicative trait type /// First applicative's bound value type /// Second applicative's bound value type /// Third applicative's bound value type /// Zipped applicative public static K zip( K First, K Second, K Third) where F : Applicative => map((A a, B b, C c) => (a, b, c), First) .Apply(Second) .Apply(Third); /// /// Zips applicatives into a tuple /// /// First applicative /// Second applicative /// Third applicative /// Fourth applicative /// Applicative trait type /// First applicative's bound value type /// Second applicative's bound value type /// Third applicative's bound value type /// Fourth applicative's bound value type /// Zipped applicative public static K zip( K First, K Second, K Third, K Fourth) where F : Applicative => map((A a, B b, C c, D d) => (a, b, c, d), First) .Apply(Second) .Apply(Third) .Apply(Fourth); /// /// Zips applicatives into a tuple /// /// First applicative /// Second applicative /// Third applicative /// Fourth applicative /// Fifth applicative /// Applicative trait type /// First applicative's bound value type /// Second applicative's bound value type /// Third applicative's bound value type /// Fourth applicative's bound value type /// Zipped applicative public static K zip( K First, K Second, K Third, K Fourth, K Fifth) where F : Applicative => map((A a, B b, C c, D d, E e) => (a, b, c, d, e), First) .Apply(Second) .Apply(Third) .Apply(Fourth) .Apply(Fifth); } ================================================ FILE: LanguageExt.Core/Traits/Applicative/README.md ================================================ **Applicative Functors** are a _'stepping stone'_ between functors and monads – they're probably less well-known but have some interesting properties of their own when it comes to lifted expression evaluation. Two of the major uses in language-ext is to enable _automatic parallel processing of effectful computations_ and to _automatically collect multiple errors when validating_. Those aren't the only usages – all the higher-kinded-types, including the collection-types, have applicative traits. The topic is too large to cover here, so take a look at [Paul Louth's Higher-Kinds series](https://paullouth.com/higher-kinds-in-c-with-language-ext-part-4-applicatives/) for more information. ================================================ FILE: LanguageExt.Core/Traits/Arithmetic/Arithmetic.Prelude.cs ================================================ #nullable enable using LanguageExt.Traits; using System.Diagnostics.Contracts; namespace LanguageExt; public static partial class Trait { /// /// Find the sum of two numbers /// /// left hand side of the addition operation /// right hand side of the addition operation /// The sum of x and y [Pure] public static A plus(A x, A y) where ARITH : Arithmetic => ARITH.Add(x, y); /// /// Find the subtract between two numbers /// /// left hand side of the subtraction operation /// right hand side of the subtraction operation /// The sum subtract between x and y [Pure] public static A subtract(A x, A y) where ARITH : Arithmetic => ARITH.Subtract(x, y); /// /// Find the product of two numbers /// /// left hand side of the product operation /// right hand side of the product operation /// The product of x and y [Pure] public static A product(A x, A y) where ARITH : Arithmetic => ARITH.Multiply(x, y); /// /// Negate the value /// /// Value to negate /// The negated source value [Pure] public static A negate(A x) where ARITH : Arithmetic => ARITH.Negate(x); } ================================================ FILE: LanguageExt.Core/Traits/Arithmetic/Arithmetic.cs ================================================ using System.Diagnostics.Contracts; using LanguageExt.Attributes; namespace LanguageExt.Traits; [Trait("Num*")] public interface Arithmetic : Trait { /// /// Find the sum of two values /// /// left hand side of the addition operation /// right hand side of the addition operation /// The sum of x and y [Pure] public static abstract A Add(A x, A y); /// /// Find the difference between two values /// /// left hand side of the subtraction operation /// right hand side of the subtraction operation /// The difference between x and y [Pure] public static abstract A Subtract(A x, A y); /// /// Find the product of two values /// /// left hand side of the product operation /// right hand side of the product operation /// The product of x and y [Pure] public static abstract A Multiply(A x, A y); /// /// Negate the value /// /// Value to negate /// The negated source value [Pure] public static abstract A Negate(A x); } ================================================ FILE: LanguageExt.Core/Traits/Biapplicative/Biapplicative.Trait.cs ================================================ using System; using static LanguageExt.Prelude; namespace LanguageExt.Traits; /// /// Applicative functor /// /// Applicative functor type /// Bound value type public interface Biapplicative : Bifunctor where FF : Biapplicative { public static abstract K BiApply( K, Func> ff, K fab); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Default implementations // public static virtual K, Func> BiApply( K, Func> ff, K fab) => FF.BiApply(ff.BiMap(curry, curry), fab); public static virtual K BiApply( K, Func> ff, K fab, K fcd) => FF.BiApply(FF.BiApply(ff, fab), fcd); } ================================================ FILE: LanguageExt.Core/Traits/Bifunctor/Bifunctor.Extensions.cs ================================================ using System; using static LanguageExt.Prelude; namespace LanguageExt.Traits; /// /// Functor module /// public static class BifunctorExtensions { /// /// Functor bimap. Maps all contained values of `A` to values of `B` and every value of `L` to `M` /// /// Mapping function /// Mapping function /// Bifunctor structure /// Bifunctor trait /// Mapped bifunctor public static K BiMap(this K fab, Func First, Func Second) where F : Bifunctor => F.BiMap(First, Second, fab); /// /// Functor bimap (with function currying) /// public static K, Func> BiMap( this K ma, Func First, Func Second) where F : Bifunctor => ma.BiMap(curry(First), curry(Second)); /// /// Functor bimap (with function currying) /// public static K>, Func>> BiMap( this K ma, Func First, Func Second) where F : Bifunctor => ma.BiMap(curry(First), curry(Second)); /// /// Functor bimap (with function currying) /// public static K>>, Func>>> BiMap( this K ma, Func First, Func Second) where F : Bifunctor => ma.BiMap(curry(First), curry(Second)); /// /// Functor bimap (with function currying) /// public static K>>>, Func>>>> BiMap( this K ma, Func First, Func Second) where Fnctr : Bifunctor => ma.BiMap(curry(First), curry(Second)); /// /// Map covariantly over the first argument. /// /// Mapping function /// Bifunctor structure /// Bifunctor trait /// Mapped bifunctor public static K MapFirst(this K fab, Func first) where F : Bifunctor => F.MapFirst(first, fab); /// /// Map covariantly over the first argument. /// /// Mapping function /// Bifunctor structure /// Bifunctor trait /// Mapped bifunctor public static K, A> MapFirst(this K fab, Func first) where F : Bifunctor => F.MapFirst(curry(first), fab); /// /// Map covariantly over the first argument. /// /// Mapping function /// Bifunctor structure /// Bifunctor trait /// Mapped bifunctor public static K>, A> MapFirst(this K fab, Func first) where F : Bifunctor => F.MapFirst(curry(first), fab); /// /// Map covariantly over the first argument. /// /// Mapping function /// Bifunctor structure /// Bifunctor trait /// Mapped bifunctor public static K>>, A> MapFirst(this K fab, Func first) where F : Bifunctor => F.MapFirst(curry(first), fab); /// /// Map covariantly over the first argument. /// /// Mapping function /// Bifunctor structure /// Bifunctor trait /// Mapped bifunctor public static K>>>, A> MapFirst(this K fab, Func first) where F : Bifunctor => F.MapFirst(curry(first), fab); /// /// Map covariantly over the first argument. /// /// Mapping function /// Bifunctor structure /// Bifunctor trait /// Mapped bifunctor public static K MapFirst(this Func first, K fab) where F : Bifunctor => F.MapFirst(first, fab); /// /// Map covariantly over the first argument. /// /// Mapping function /// Bifunctor structure /// Bifunctor trait /// Mapped bifunctor public static K, A> MapFirst(this Func first, K fab) where F : Bifunctor => F.MapFirst(curry(first), fab); /// /// Map covariantly over the first argument. /// /// Mapping function /// Bifunctor structure /// Bifunctor trait /// Mapped bifunctor public static K>, A> MapFirst(this Func first, K fab) where F : Bifunctor => F.MapFirst(curry(first), fab); /// /// Map covariantly over the first argument. /// /// Mapping function /// Bifunctor structure /// Bifunctor trait /// Mapped bifunctor public static K>>, A> MapFirst(this Func first, K fab) where F : Bifunctor => F.MapFirst(curry(first), fab); /// /// Map covariantly over the first argument. /// /// Mapping function /// Bifunctor structure /// Bifunctor trait /// Mapped bifunctor public static K>>>, A> MapFirst(this Func first, K fab) where F : Bifunctor => F.MapFirst(curry(first), fab); /// /// Map covariantly over the second argument. /// /// Mapping function /// Bifunctor structure /// Bifunctor trait /// Mapped bifunctor public static K MapSecond(this K fab, Func second) where F : Bifunctor => F.MapSecond(second, fab); /// /// Map covariantly over the second argument. /// /// Mapping function /// Bifunctor structure /// Bifunctor trait /// Mapped bifunctor public static K> MapSecond(this K fab, Func second) where F : Bifunctor => F.MapSecond(curry(second), fab); /// /// Map covariantly over the second argument. /// /// Mapping function /// Bifunctor structure /// Bifunctor trait /// Mapped bifunctor public static K>> MapSecond(this K fab, Func second) where F : Bifunctor => F.MapSecond(curry(second), fab); /// /// Map covariantly over the second argument. /// /// Mapping function /// Bifunctor structure /// Bifunctor trait /// Mapped bifunctor public static K>>> MapSecond(this K fab, Func second) where F : Bifunctor => F.MapSecond(curry(second), fab); /// /// Map covariantly over the second argument. /// /// Mapping function /// Bifunctor structure /// Bifunctor trait /// Mapped bifunctor public static K>>>> MapSecond(this K fab, Func second) where BF : Bifunctor => BF.MapSecond(curry(second), fab); /// /// Map covariantly over the second argument. /// /// Mapping function /// Bifunctor structure /// Bifunctor trait /// Mapped bifunctor public static K MapSecond(this Func second, K fab) where F : Bifunctor => F.MapSecond(second, fab); /// /// Map covariantly over the second argument. /// /// Mapping function /// Bifunctor structure /// Bifunctor trait /// Mapped bifunctor public static K> MapSecond(this Func second, K fab) where F : Bifunctor => F.MapSecond(curry(second), fab); /// /// Map covariantly over the second argument. /// /// Mapping function /// Bifunctor structure /// Bifunctor trait /// Mapped bifunctor public static K>> MapSecond(this Func second, K fab) where F : Bifunctor => F.MapSecond(curry(second), fab); /// /// Map covariantly over the second argument. /// /// Mapping function /// Bifunctor structure /// Bifunctor trait /// Mapped bifunctor public static K>>> MapSecond(this Func second, K fab) where F : Bifunctor => F.MapSecond(curry(second), fab); /// /// Map covariantly over the second argument. /// /// Mapping function /// Bifunctor structure /// Bifunctor trait /// Mapped bifunctor public static K>>>> MapSecond(this Func second, K fab) where BF : Bifunctor => BF.MapSecond(curry(second), fab); } ================================================ FILE: LanguageExt.Core/Traits/Bifunctor/Bifunctor.Module.cs ================================================ using System; namespace LanguageExt.Traits; /// /// Functor module /// public static class Bifunctor { /// /// Functor bimap. Maps all contained values of `A` to values of `X` and every value of `B` to `Y` /// /// Mapping function /// Mapping function /// Bifunctor structure /// Bifunctor trait /// Mapped bifunctor public static K bimap(Func first, Func second, K fab) where F : Bifunctor => F.BiMap(first, second, fab); /// /// Map covariantly over the first argument. /// /// Mapping function /// Bifunctor structure /// Bifunctor trait /// Mapped bifunctor public static K first(Func first, K fab) where F : Bifunctor => F.MapFirst(first, fab); /// /// Map covariantly over the second argument. /// /// Mapping function /// Bifunctor structure /// Bifunctor trait /// Mapped bifunctor public static K second(Func second, K fab) where F : Bifunctor => F.MapSecond(second, fab); } ================================================ FILE: LanguageExt.Core/Traits/Bifunctor/Bifunctor.Trait.cs ================================================ using System; using static LanguageExt.Prelude; namespace LanguageExt.Traits; /// /// Bi-functor /// /// Bi-functor self-type public interface Bifunctor where F : Bifunctor { /// /// Functor bimap. Maps all contained values of `L` to values of `M` and every value of `A` to `B` /// /// Mapping function /// Mapping function /// Functor structure /// Functor trait /// Mapped functor public static abstract K BiMap(Func first, Func second, K fab); /// /// Map covariantly over the first argument. /// /// Mapping function /// Bifunctor structure /// Bifunctor trait /// Mapped bifunctor public static virtual K MapFirst(Func first, K fab) => F.BiMap(first, identity, fab); /// /// Map covariantly over the second argument. /// /// Mapping function /// Bifunctor structure /// Bifunctor trait /// Mapped bifunctor public static virtual K MapSecond(Func second, K fab) => F.BiMap(identity, second, fab); } ================================================ FILE: LanguageExt.Core/Traits/Bimonad/Bimonad.Extensions.cs ================================================ using System; using static LanguageExt.Prelude; using LanguageExt.Traits; namespace LanguageExt; public static class BimonadExtensions { public static K BindFirst(this K ma, Func> f) where M : Bimonad => M.BindFirst(ma, f); public static K BindSecond(this K ma, Func> f) where M : Bimonad => M.BindSecond(ma, f); public static K FlattenFirst(K, A> mma) where M : Bimonad => M.FlattenFirst(mma); public static K FlattenSecond(K> mma) where M : Bimonad => M.FlattenSecond(mma); } ================================================ FILE: LanguageExt.Core/Traits/Bimonad/Bimonad.Module.cs ================================================ using System; using static LanguageExt.Prelude; using LanguageExt.Traits; namespace LanguageExt; public static class Bimonad { public static K bindFirst(K ma, Func> f) where M : Bimonad => M.BindFirst(ma, f); public static K bindSecond(K ma, Func> f) where M : Bimonad => M.BindSecond(ma, f); public static K flattenFirst(K, A> mma) where M : Bimonad => M.FlattenFirst(mma); public static K flattenSecond(K> mma) where M : Bimonad => M.FlattenSecond(mma); } ================================================ FILE: LanguageExt.Core/Traits/Bimonad/Bimonad.Trait.cs ================================================ using System; using static LanguageExt.Prelude; namespace LanguageExt.Traits; public interface Bimonad : Bifunctor where M : Bimonad { //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Abstract members // public static abstract K BindFirst(K ma, Func> f); public static abstract K BindSecond(K ma, Func> f); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Default implementations // public static virtual K FlattenFirst(K, A> mma) => M.BindFirst(mma, identity); public static virtual K FlattenSecond(K> mma) => M.BindSecond(mma, identity); } ================================================ FILE: LanguageExt.Core/Traits/Bool/Bool.cs ================================================ using System.Diagnostics.Contracts; using LanguageExt.Attributes; namespace LanguageExt.Traits; /// /// Trait for things that have true and false values. /// [Trait("Bool*")] public interface Bool : Trait { /// /// Returns True /// /// True [Pure] public static abstract A True(); /// /// Returns False /// /// False [Pure] public static abstract A False(); /// /// Returns the result of the logical AND operation between `a` and `b` /// /// The result of the logical AND operation between `a` and `b` [Pure] public static abstract A And(A a, A b); /// /// Returns the result of the logical OR operation between `a` and `b` /// /// The result of the logical OR operation between `a` and `b` [Pure] public static abstract A Or(A a, A b); /// /// Returns the result of the logical NOT operation on `a` /// /// The result of the logical NOT operation on `a` [Pure] public static abstract A Not(A a); /// /// Returns the result of the logical exclusive-OR operation between `a` and `b` /// /// The result of the logical exclusive-OR operation between `a` and `b` [Pure] public static abstract A XOr(A a, A b); /// /// Logical implication /// /// If `a` is true that implies `b`, else `true` [Pure] public static abstract A Implies(A a, A b); /// /// Logical bi-conditional. Both `a` and `b` must be `true`, or both `a` and `b` must /// be false. /// /// `true` if `a == b`, `false` otherwise [Pure] public static abstract A BiCondition(A a, A b); } ================================================ FILE: LanguageExt.Core/Traits/Choice/Choice.Laws.cs ================================================ using System; using LanguageExt.Common; using static LanguageExt.Prelude; namespace LanguageExt.Traits; /// /// Functions that test that Alternative laws hold for the `F` Alternative provided. /// /// /// choose(pure(a), pure(b)) = pure(a) /// /// /// NOTE: `Equals` must be implemented for the `K〈F, *〉` derived-type, so that the laws /// can be proven to be true. If your Alternative structure doesn't have `Equals` then you /// must provide the optional `equals` parameter so that the equality of outcomes can be tested. /// /// Alternative type public static class ChoiceLaw where F : Choice, Applicative { /// /// Assert that the Alternative laws hold /// /// /// NOTE: `Equals` must be implemented for the `K〈F, *〉` derived-type, so that the laws /// can be proven to be true. If your Alternative structure doesn't have `Equals` then /// you must provide the optional `equals` parameter so that the equality of outcomes can /// be tested. /// public static Unit assert(K failure, Func, K, bool>? equals = null) => validate(failure, equals) .IfFail(errors => errors.Throw()); /// /// Validate that the Alternative laws hold /// /// /// NOTE: `Equals` must be implemented for the `K〈F, *〉` derived-type, so that the laws /// can be proven to be true. If your Alternative structure doesn't have `Equals` then /// you must provide the optional `equals` parameter so that the equality of outcomes can /// be tested. /// public static Validation validate(K failure, Func, K, bool>? equals = null) { equals ??= (fa, fb) => fa.Equals(fb); return leftZeroLaw(failure, equals) >> rightZeroLaw(failure, equals) >> leftCatchLaw(equals); } /// /// Left-zero law /// /// /// choose(empty, pure(x)) = pure(x) /// /// /// NOTE: `Equals` must be implemented for the `K〈F, *〉` derived-type, so that the laws /// can be proven to be true. If your Alternative structure doesn't have `Equals` then /// you must provide the optional `equals` parameter so that the equality of outcomes can /// be tested. /// public static Validation leftZeroLaw(K failure, Func, K, bool> equals) { var fa = failure; var fb = F.Pure(100); var fr = choose(fa, fb); return equals(fr, fb) ? unit : Error.New($"Choice left-zero law does not hold for {typeof(F).Name}"); } /// /// Right-zero law /// /// /// choose(pure(x), empty) = pure(x) /// /// /// NOTE: `Equals` must be implemented for the `K〈F, *〉` derived-type, so that the laws /// can be proven to be true. If your Alternative structure doesn't have `Equals` then /// you must provide the optional `equals` parameter so that the equality of outcomes can /// be tested. /// public static Validation rightZeroLaw(K failure, Func, K, bool> equals) { var fa = F.Pure(100); var fb = failure; var fr = choose(fa, fb); return equals(fr, fa) ? unit : Error.New($"Choice right-zero law does not hold for {typeof(F).Name}"); } /// /// Left catch law /// /// /// choose(pure(x), pure(y)) = pure(x) /// /// /// NOTE: `Equals` must be implemented for the `K〈F, *〉` derived-type, so that the laws /// can be proven to be true. If your Alternative structure doesn't have `Equals` then /// you must provide the optional `equals` parameter so that the equality of outcomes can /// be tested. /// public static Validation leftCatchLaw(Func, K, bool> equals) { var fa = F.Pure(100); var fb = F.Pure(200); var fr = choose(fa, fb); return equals(fr, fa) ? unit : Error.New($"Choice left-catch law does not hold for {typeof(F).Name}"); } } ================================================ FILE: LanguageExt.Core/Traits/Choice/Choice.Module.cs ================================================ using System; using System.Diagnostics.Contracts; using static LanguageExt.Prelude; namespace LanguageExt.Traits; public static class Choice { /// /// Where `F` defines some notion of failure or choice, this function picks the /// first argument that succeeds. So, if `fa` succeeds, then `fa` is returned; /// if it fails, then `fb` is returned. /// /// First structure to test /// Second structure to return if the first one fails /// Alternative structure type /// Bound value type /// First argument to succeed public static K choose(K fa, K fb) where F : Choice => F.Choose(fa, fb); /// /// One or more... /// /// /// Run the applicative functor repeatedly, collecting the results, until failure. /// /// Will always succeed if at least one item has been yielded. /// /// /// NOTE: It is important that the `F` applicative-type overrides `Apply` (the one with `Func` laziness) in its /// trait-implementations otherwise this will likely result in a stack-overflow. /// /// Applicative functor /// One or more values [Pure] public static K> some(K fa) where F : Choice, Applicative { return some_v(); K> many_v() => F.Choose(some_v(), F.Pure(Seq())); K> some_v() => Append.cons.Map(fa).Apply(memoK(many_v)); } /// /// Zero or more... /// /// /// Run the applicative functor repeatedly, collecting the results, until failure. /// Will always succeed. /// /// /// NOTE: It is important that the `F` applicative-type overrides `ApplyLazy` in its trait-implementations /// otherwise this will likely result in a stack-overflow. /// /// Applicative functor /// Zero or more values [Pure] public static K> many(K fa) where F : Choice, Applicative { return many_v(); K> many_v() => F.Choose(some_v(), F.Pure(Seq())); K> some_v() => Append.cons.Map(fa).Apply(memoK(many_v)); } static class Append { public static readonly Func, Seq>> cons = x => xs => x.Cons(xs); } } ================================================ FILE: LanguageExt.Core/Traits/Choice/Choice.Trait.cs ================================================ using System; namespace LanguageExt.Traits; /// /// Choose between two structures. If the first succeeds, then return it; otherwise return the second. /// /// Structure type public interface Choice where F : Choice { /// /// Where `F` defines some notion of failure or choice, this function picks the /// first argument that succeeds. So, if `fa` succeeds, then `fa` is returned; /// if it fails, then `fb` is returned. /// /// First structure to test /// Second structure to return if the first one fails /// Alternative structure type /// Bound value type /// First argument to succeed static abstract K Choose(K fa, K fb); /// /// Where `F` defines some notion of failure or choice, this function picks the /// first argument that succeeds. So, if `fa` succeeds, then `fa` is returned; /// if it fails, then `fb` is returned. /// /// First structure to test /// Second structure to return if the first one fails /// Alternative structure type /// Bound value type /// First argument to succeed static abstract K Choose(K fa, Memo fb); } ================================================ FILE: LanguageExt.Core/Traits/Choice/Extensions/Choice.Extensions.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public static partial class ChoiceExtensions { /// First structure to test /// Alternative structure type /// Bound value type extension(K fa) where F : Choice { /// /// Where `F` defines some notion of failure or choice, this function picks the /// first argument that succeeds. So, if `fa` succeeds, then `fa` is returned; /// if it fails, then `fb` is returned. /// /// Second structure to return if the first one fails /// First argument to succeed public K Choose(K fb) => F.Choose(fa, fb); /// /// Where `F` defines some notion of failure or choice, this function picks the /// first argument that succeeds. So, if `fa` succeeds, then `fa` is returned; /// if it fails, then `fb` is returned. /// /// Second structure to return if the first one fails /// First argument to succeed public K Choose(Memo fb) => F.Choose(fa, fb); } /// Applicative functor extension(K v) where F : Choice, Applicative { /// /// One or more... /// /// /// Run the applicative functor repeatedly, collecting the results, until failure. /// /// Will always succeed if at least one item has been yielded. /// /// One or more values public K> Some() => Choice.some(v); /// /// Zero or more... /// /// /// Run the applicative functor repeatedly, collecting the results, until failure. /// /// Will always succeed. /// /// Zero or more values public K> Many() => Choice.many(v); } } ================================================ FILE: LanguageExt.Core/Traits/Choice/Operators/Choice.Operators.cs ================================================ using LanguageExt.Traits; namespace LanguageExt; public static partial class ChoiceExtensions { extension(K _) where F : Choice { /// /// Choice operator. Usually means if the first argument succeeds, return it, otherwise return the second /// argument. /// /// Left hand side operand /// Right hand side operand /// public static K operator |(K lhs, K rhs) => lhs.Choose(rhs); } extension(K _) where F : Choice, Applicative { /// /// Choice operator. Usually means if the first argument succeeds, return it, otherwise return the second /// argument. /// /// Left hand side operand /// Right hand side operand /// public static K operator |(K lhs, Pure rhs) => lhs.Choose(F.Pure(rhs.Value)); } } ================================================ FILE: LanguageExt.Core/Traits/Choice/Prelude/Choice.Prelude.cs ================================================ using System; using System.Diagnostics.Contracts; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; public static partial class Prelude { /// /// Where `F` defines some notion of failure or choice, this function picks the /// first argument that succeeds. So, if `fa` succeeds, then `fa` is returned; /// if it fails, then `fb` is returned. /// /// First structure to test /// Second structure to return if the first one fails /// Alternative structure type /// Bound value type /// First argument to succeed public static K choose(K fa, K fb) where F : Choice => Choice.choose(fa, fb); /// /// One or more... /// /// /// Run the applicative functor repeatedly, collecting the results, until failure. /// /// Will always succeed if at least one item has been yielded. /// /// /// NOTE: It is important that the `F` applicative-type overrides `Apply` (the one with `Func` laziness) in its /// trait-implementations otherwise this will likely result in a stack-overflow. /// /// Applicative functor /// One or more values [Pure] public static K> some(K fa) where F : Choice, Applicative => Choice.some(fa); /// /// Zero or more... /// /// /// Run the applicative functor repeatedly, collecting the results, until failure. /// Will always succeed. /// /// /// NOTE: It is important that the `F` applicative-type overrides `ApplyLazy` in its trait-implementations /// otherwise this will likely result in a stack-overflow. /// /// Applicative functor /// Zero or more values [Pure] public static K> many(K fa) where F : Choice, Applicative => Choice.many(fa); } ================================================ FILE: LanguageExt.Core/Traits/Choice/README.md ================================================ `Choice` allows for propagation of 'failure' and 'choice' (in some appropriate sense, depending on the type). `Choice` is a `SemigroupK`, but has a `Choose` method, rather than relying on the `SemigroupK.Combine` method, (which now has a default implementation of invoking `Choose`). That creates a new semantic meaning for `Choose`, which is about choice propagation rather than the broader meaning of `Combine`. It also allows for `Choose` and `Combine` to have separate implementations depending on the type. The way to think about `Choose` and the inherited `SemigroupK.Combine` methods is: * `Choose` is the failure/choice propagation operator: `|` * `Combine` is the concatenation/combination/addition operator: `+` Any type that supports the `Choice` trait should also implement the `|` operator, to enable easy choice/failure propagation. If there is a different implementation of `Combine` (rather than accepting the default), then the type should also implement the `+` operator. `ChoiceLaw` can help you test your implementation: choose(Pure(a), Pure(b)) = Pure(a) choose(Fail, Pure(b)) = Pure(b) choose(Pure(a), Fail) = Pure(a) choose(Fail [1], Fail [2]) = Fail [2] It also tests the `Applicative` and `Functor` laws. ================================================ FILE: LanguageExt.Core/Traits/Chronicaler/Chronicaler.Extensions.cs ================================================ using System; namespace LanguageExt.Traits; public static class ChronicalerExtensions { /// /// `Memento` is an action that executes the action within this structure, returning either /// its record, if it ended with `Confess`, or its final value otherwise, with any record /// added to the current record. /// /// Similar to 'Catch' in the 'Fallible' trait, but with a notion of non-fatal errors (which /// are accumulated) vs. fatal errors (which are caught without accumulating). /// /// Action to memento public static K> Memento(this K ma) where M : Chronicaler => M.Memento(ma); /// /// `Absolve` is an action that executes this structure and discards any record it had. /// The `defaultValue` will be used if the action ended via `Confess`. /// /// /// Action to absolve public static K Absolve(this K ma, A defaultValue) where M : Chronicaler => M.Absolve(defaultValue, ma); /// /// `Condemn` is an action that executes the structure and keeps its value /// only if it had no record. Otherwise, the value (if any) will be discarded /// and only the record kept. /// /// This can be seen as converting non-fatal errors into fatal ones. /// /// Action to condemn public static K Condemn(this K ma) where M : Chronicaler => M.Condemn(ma); /// /// An action that executes the structure and applies the function `f` to its output, leaving /// the return value unchanged.- /// /// /// Equivalent to `censor` for the 'Writable` trait. /// /// Censoring function /// Action to censor public static K Censor(this K ma, Func f) where M : Chronicaler => M.Censor(f, ma); /// /// Construct a new chronicle with `these`. /// /// What to chronicle /// Chronicle structure public static K Chronicle(this These ma) where M : Chronicaler => M.Chronicle(ma); } ================================================ FILE: LanguageExt.Core/Traits/Chronicaler/Chronicaler.Module.cs ================================================ using System; namespace LanguageExt.Traits; public static class Chronicaler { /// /// `Dictate` is an action that records the output `value`. /// Equivalent to `tell` for the `Writable` traits. /// /// Value to construct with /// Chronicle structure public static K dictate(A value) where M : Chronicaler => M.Dictate(value); /// /// `Confess` is an action that ends with a final output `value`. /// Equivalent to `fail` for the 'Fallible' trait. /// /// Value to construct with /// Chronicle structure public static K confess(Ch confession) where M : Chronicaler => M.Confess(confession); /// /// `Memento` is an action that executes the action within this structure, returning either /// its record, if it ended with `Confess`, or its final value otherwise, with any record /// added to the current record. /// /// Similar to 'Catch' in the 'Fallible' trait, but with a notion of non-fatal errors (which /// are accumulated) vs. fatal errors (which are caught without accumulating). /// /// Action to memento public static K> memento(K ma) where M : Chronicaler => M.Memento(ma); /// /// `Absolve` is an action that executes this structure and discards any record it had. /// The `defaultValue` will be used if the action ended via `Confess`. /// /// /// Action to absolve public static K absolve(A defaultValue, K ma) where M : Chronicaler => M.Absolve(defaultValue, ma); /// /// `Condemn` is an action that executes the structure and keeps its value /// only if it had no record. Otherwise, the value (if any) will be discarded /// and only the record kept. /// /// This can be seen as converting non-fatal errors into fatal ones. /// /// Action to condemn public static K condemn(K ma) where M : Chronicaler => M.Condemn(ma); /// /// An action that executes the structure and applies the function `f` to its output, leaving /// the return value unchanged.- /// /// /// Equivalent to `censor` for the 'Writable` trait. /// /// Censoring function /// Action to censor public static K censor(Func f, K ma) where M : Chronicaler => M.Censor(f, ma); /// /// Construct a new chronicle with `these`. /// /// What to chronicle /// Chronicle structure public static K chronicle(These ma) where M : Chronicaler => M.Chronicle(ma); } ================================================ FILE: LanguageExt.Core/Traits/Chronicaler/Chronicaler.Trait.cs ================================================ using System; namespace LanguageExt.Traits; /// /// Hybrid error/writer monad class that allows both accumulating outputs and aborting computation with a final output. /// /// The expected use case is for computations with a notion of fatal vs. non-fatal errors. /// /// Self /// Chronicle type public interface Chronicaler where M : Chronicaler { /// /// `Dictate` is an action that records the output `value`. /// Equivalent to `tell` for the `Writable` traits. /// /// Value to construct with /// Chronicle structure public static abstract K Dictate(A value); /// /// `Confess` is an action that ends with a final output `value`. /// Equivalent to `fail` for the 'Fallible' trait. /// /// Value to construct with /// Chronicle structure public static abstract K Confess(Ch confession); /// /// `Memento` is an action that executes the action within this structure, returning either /// its record, if it ended with `Confess`, or its final value otherwise, with any record /// added to the current record. /// /// Similar to 'Catch' in the 'Fallible' trait, but with a notion of non-fatal errors (which /// are accumulated) vs. fatal errors (which are caught without accumulating). /// /// Action to memento public static abstract K> Memento(K ma); /// /// `Absolve` is an action that executes this structure and discards any record it had. /// The `defaultValue` will be used if the action ended via `Confess`. /// /// /// Action to absolve public static abstract K Absolve(A defaultValue, K ma); /// /// `Condemn` is an action that executes the structure and keeps its value /// only if it had no record. Otherwise, the value (if any) will be discarded /// and only the record kept. /// /// This can be seen as converting non-fatal errors into fatal ones. /// /// Action to condemn public static abstract K Condemn(K ma); /// /// An action that executes the structure and applies the function `f` to its output, leaving /// the return value unchanged.- /// /// /// Equivalent to `censor` for the 'Writable` trait. /// /// Censoring function /// Action to censor public static abstract K Censor(Func f, K ma); /// /// Construct a new chronicle with `these`. /// /// What to chronicle /// Chronicle structure public static abstract K Chronicle(These ma); } ================================================ FILE: LanguageExt.Core/Traits/Cofunctor/Cofunctor.Extensions.cs ================================================ using System; namespace LanguageExt.Traits; public static class CofunctorExtensions { /// /// The class of contravariant functors. /// Whereas one can think of a `Functor` as containing or producing values, a contravariant functor is a functor that /// can be thought of as consuming values. /// /// Contravariant functors are referred to colloquially as Cofunctor, even though the dual of a `Functor` is just /// a `Functor`. /// public static K Comap(this K fb, Func f) where F : Cofunctor => F.Comap(f, fb); /// /// The class of contravariant functors. /// Whereas one can think of a `Functor` as containing or producing values, a contravariant functor is a functor that /// can be thought of as consuming values. /// /// Contravariant functors are referred to colloquially as Cofunctor, even though the dual of a `Functor` is just /// a `Functor`. /// public static K Comap(this Func f, K fb) where F : Cofunctor => F.Comap(f, fb); } ================================================ FILE: LanguageExt.Core/Traits/Cofunctor/Cofunctor.Module.cs ================================================ using System; namespace LanguageExt.Traits; public static class Cofunctor { /// /// The class of contravariant functors. /// Whereas one can think of a `Functor` as containing or producing values, a contravariant functor is a functor that /// can be thought of as consuming values. /// /// Contravariant functors are referred to colloquially as Cofunctor, even though the dual of a `Functor` is just /// a `Functor`. /// public static K contraMap(Func f, K fb) where F : Cofunctor => F.Comap(f, fb); } ================================================ FILE: LanguageExt.Core/Traits/Cofunctor/Cofunctor.Prelude.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public static partial class Prelude { /// /// The class of contravariant functors. /// Whereas one can think of a `Functor` as containing or producing values, a contravariant functor is a functor that /// can be thought of as consuming values. /// /// Contravariant functors are referred to colloquially as Cofunctor, even though the dual of a `Functor` is just /// a `Functor`. /// public static K contraMap(Func f, K fb) where F : Cofunctor => F.Comap(f, fb); } ================================================ FILE: LanguageExt.Core/Traits/Cofunctor/Cofunctor.Trait.cs ================================================ using System; namespace LanguageExt.Traits; /// /// The class of contravariant functors. /// Whereas one can think of a `Functor` as containing or producing values, a contravariant functor is a functor that /// can be thought of as consuming values. /// /// Contravariant functors are referred to colloquially as Cofunctor, even though the dual of a `Functor` is just /// a `Functor`. /// /// Self-referring type public interface Cofunctor { public static abstract K Comap(Func f, K fb); } ================================================ FILE: LanguageExt.Core/Traits/Const/Const.cs ================================================ #nullable enable using LanguageExt.Attributes; namespace LanguageExt.Traits; /// /// Constant value trait /// /// [Trait("Const*")] public interface Const : Trait { public static abstract TYPE Value { get; } } ================================================ FILE: LanguageExt.Core/Traits/Coproduct/Coproduct.Extensions.cs ================================================ using System; using System.Collections.Generic; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; /// /// Co-product trait (abstract version of `Either`) /// /// Self public static class CoproductExtensions { /// /// Pattern-match either the left or right value in the coproduct /// /// Coproduct value /// Function to map the left value to a result /// Function to map the right value to a result /// Coproduct trait type /// Left value type /// Right value type /// Result type /// Result of mapping either the left or right values with the functions provided public static C Match(this K fab, Func Left, Func Right) where F : Coproduct => F.Match(Left, Right, fab); /// /// Pattern-match either the left or right value in the coproduct /// /// Coproduct value /// Function to map the left value to a result /// Function to map the right value to a result /// Coproduct trait type /// Left value type /// Right value type /// Result type /// Result of mapping either the left or right values with the functions provided public static C Match(this K fab, C Left, Func Right) where F : Coproduct => F.Match(Left, Right, fab); /// /// Pattern-match either the left or right value in the coproduct /// /// Coproduct value /// Function to map the left value to a result /// Function to map the right value to a result /// Coproduct trait type /// Left value type /// Right value type /// Result type /// Result of mapping either the left or right values with the functions provided public static C Match(this K fab, Func Left, C Right) where F : Coproduct => F.Match(Left, Right, fab); /// /// Pattern-match the left value in the coproduct /// /// Coproduct value /// Function to map the left value to a result /// Coproduct trait type /// Left value type /// Right value type /// Either the right value or the result of mapping the left with the function provided public static B IfLeft(this K fab, Func Left) where F : Coproduct => F.IfLeft(Left, fab); /// /// Pattern-match the left value in the coproduct /// /// Coproduct value /// Default value to use if the state is `Left` /// Coproduct trait type /// Left value type /// Right value type /// Either the right value or provided `Left` value public static B IfLeft(this K fab, B Left) where F : Coproduct => F.IfLeft(Left, fab); /// /// Pattern-match either the left or right value in the coproduct /// /// Coproduct value /// Function to map the right value to a result /// Coproduct trait type /// Left value type /// Right value type /// Either the left value or the result of mapping the right value with the function provided public static A IfRight(this K fab, Func Right) where F : Coproduct => F.IfRight(Right, fab); /// /// Pattern-match either the left or right value in the coproduct /// /// Coproduct value /// Default value to use if the state is `Right` /// Coproduct trait type /// Left value type /// Right value type /// Either the left value or provided `Right` value public static A IfRight(this K fab, A Right) where F : Coproduct => F.IfRight(Right, fab); /// /// Partition the foldable of coproducts into two left and right sequences. /// /// Left value type /// Right value type /// Two left and right sequences public static (Seq Lefts, Seq Rights) Partition(this K> fabs) where F : Coproduct where FF : Foldable => F.Partition(fabs); /// /// Partition the foldable of coproducts into two left and right sequences. /// /// Left value type /// Right value type /// Two left and right sequences public static (Seq Lefts, Seq Rights) PartitionSequence(this IEnumerable> fabs) where F : Coproduct => F.Partition(fabs.AsIterable()); } ================================================ FILE: LanguageExt.Core/Traits/Coproduct/Coproduct.Module.cs ================================================ using System.Collections.Generic; namespace LanguageExt.Traits; public static class Coproduct { /// /// Construct a coproduct structure in a 'Left' state /// /// Left value /// Left value type /// Right value type /// Constructed coproduct structure public static K left(A value) where F : Coproduct => F.Left(value); /// /// Construct a coproduct structure in a 'Left' state /// /// Left value /// Left value type /// Right value type /// Constructed coproduct structure public static K right(B value) where F : Coproduct => F.Right(value); /// /// Partition the foldable of coproducts into two left and right sequences. /// /// Left value type /// Right value type /// Two left and right sequences public static (Seq Lefts, Seq Rights) partition(K> fabs) where F : Coproduct where FF : Foldable => F.Partition(fabs); /// /// Partition the foldable of coproducts into two left and right sequences. /// /// Left value type /// Right value type /// Two left and right sequences public static (Seq Lefts, Seq Rights) partitionSequence(IEnumerable> fabs) where F : Coproduct => F.Partition(fabs.AsIterable()); } ================================================ FILE: LanguageExt.Core/Traits/Coproduct/Coproduct.Trait.cs ================================================ using System; using static LanguageExt.Prelude; namespace LanguageExt.Traits; /// /// Co-product trait (abstract version of `Either`) /// /// Self public interface Coproduct : CoproductCons where F : Coproduct { /// /// Pattern-match either the left or right value in the coproduct /// /// Coproduct value /// Function to map the left value to a result /// Function to map the right value to a result /// Coproduct trait type /// Left value type /// Right value type /// Result type /// Result of mapping either the left or right values with the functions provided public static abstract C Match(Func Left, Func Right, K fab); /// /// Pattern-match either the left or right value in the coproduct /// /// Coproduct value /// Function to map the left value to a result /// Function to map the right value to a result /// Coproduct trait type /// Left value type /// Right value type /// Result type /// Result of mapping either the left or right values with the functions provided public static virtual C Match(C Left, Func Right, K fab) => F.Match(_ => Left, Right, fab); /// /// Pattern-match either the left or right value in the coproduct /// /// Coproduct value /// Function to map the left value to a result /// Function to map the right value to a result /// Coproduct trait type /// Left value type /// Right value type /// Result type /// Result of mapping either the left or right values with the functions provided public static virtual C Match(Func Left, C Right, K fab) => F.Match(Left, _ => Right, fab); /// /// Pattern-match the left value in the coproduct /// /// Coproduct value /// Function to map the left value to a result /// Coproduct trait type /// Left value type /// Right value type /// Either the right value or the result of mapping the left with the function provided public static virtual B IfLeft(Func Left, K fab) => F.Match(Left, identity, fab); /// /// Pattern-match the left value in the coproduct /// /// Coproduct value /// Default value to use if the state is `Left` /// Coproduct trait type /// Left value type /// Right value type /// Either the right value or provided `Left` value public static virtual B IfLeft(B Left, K fab) => F.Match(_ => Left, identity, fab); /// /// Pattern-match either the left or right value in the coproduct /// /// Coproduct value /// Function to map the right value to a result /// Coproduct trait type /// Left value type /// Right value type /// Either the left value or the result of mapping the right value with the function provided public static virtual A IfRight(Func Right, K fab) => F.Match(identity, Right, fab); /// /// Pattern-match either the left or right value in the coproduct /// /// Coproduct value /// Default value to use if the state is `Right` /// Coproduct trait type /// Left value type /// Right value type /// Either the left value or provided `Right` value public static virtual A IfRight(A Right, K fab) => F.Match(identity, _ => Right, fab); /// /// Partition the foldable of coproducts into two left and right sequences. /// /// Left value type /// Right value type /// Two left and right sequences public static virtual (Seq Lefts, Seq Rights) Partition(K> fabs) where FF : Foldable => fabs.Fold((Lefts: Seq(), Rights: Seq()), (s, fab) => fab.Match(Left: l => s with { Lefts = s.Lefts.Add(l) }, Right: r => s with { Rights = s.Rights.Add(r) })); /// /// Partition the foldable of coproducts into two left and right sequences, then return the left sequence. /// /// Left value type /// Right value type /// Left sequence public static virtual Seq Lefts(K> fabs) where G : Foldable => fabs.Fold(Seq(), (s, fab) => fab.Match(Left: s.Add, Right: s)); /// /// Partition the foldable of coproducts into two left and right sequences, then return the right sequence. /// /// Left value type /// Right value type /// Right sequence public static virtual Seq Rights(K> fabs) where G : Foldable => fabs.Fold(Seq(), (s, fab) => fab.Match(Left: s, Right: s.Add)); } ================================================ FILE: LanguageExt.Core/Traits/CoproductCons/CoproductCons.Module.cs ================================================ namespace LanguageExt.Traits; public static class CoproductCons { /// /// Construct a coproduct structure in a 'Left' state /// /// Left value /// Left value type /// Right value type /// Constructed coproduct structure public static K left(A value) where F : CoproductCons => F.Left(value); /// /// Construct a coproduct structure in a 'Left' state /// /// Left value /// Left value type /// Right value type /// Constructed coproduct structure public static K right(B value) where F : CoproductCons => F.Right(value); } ================================================ FILE: LanguageExt.Core/Traits/CoproductCons/CoproductCons.Trait.cs ================================================ using System; using static LanguageExt.Prelude; namespace LanguageExt.Traits; /// /// Co-product trait (abstract version of `Either`) /// /// Self public interface CoproductCons where F : CoproductCons { /// /// Construct a coproduct structure in a 'Left' state /// /// Left value /// Left value type /// Right value type /// Constructed coproduct structure public static abstract K Left(A value); /// /// Construct a coproduct structure in a 'Left' state /// /// Left value /// Left value type /// Right value type /// Constructed coproduct structure public static abstract K Right(B value); } ================================================ FILE: LanguageExt.Core/Traits/CoproductK/CoproductK.Extensions.cs ================================================ using System; using System.Collections.Generic; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; /// /// Co-product trait (abstract version of `Either`) /// /// Self public static class CoproductKExtensions { /// /// Pattern-match either the left or right value in the coproduct /// /// Coproduct value /// Function to map the left value to a result /// Function to map the right value to a result /// Coproduct trait type /// Left value type /// Right value type /// Result type /// Result of mapping either the left or right values with the functions provided public static K Match(this K fab, Func Left, Func Right) where F : CoproductK => F.Match(Left, Right, fab); /// /// Partition the foldable of coproducts into two left and right sequences. /// /// Left value type /// Right value type /// Two left and right sequences public static K Left, Seq Right)> Partition(this K> fabs) where F : CoproductK, Bimonad where FF : Foldable => CoproductK.partition(fabs); /// /// Partition the foldable of coproducts into two left and right sequences. /// /// Left value type /// Right value type /// Two left and right sequences public static K Left, Seq Right)> PartitionSequence(this IEnumerable> fabs) where F : CoproductK, Bimonad => CoproductK.partitionSequence(fabs); } ================================================ FILE: LanguageExt.Core/Traits/CoproductK/CoproductK.Module.cs ================================================ using static LanguageExt.Prelude; using System.Collections.Generic; namespace LanguageExt.Traits; public static class CoproductK { /// /// Construct a coproduct structure in a 'Left' state /// /// Left value /// Left value type /// Right value type /// Constructed coproduct structure public static K left(A value) where F : CoproductK => F.Left(value); /// /// Construct a coproduct structure in a 'Left' state /// /// Left value /// Left value type /// Right value type /// Constructed coproduct structure public static K right(B value) where F : CoproductK => F.Right(value); /// /// Partition the foldable of coproducts into two left and right sequences. /// /// Left value type /// Right value type /// Two left and right sequences public static K Left, Seq Right)> partition(K> fabs) where F : CoproductK, Bimonad where FF : Foldable => fabs.Fold(F.Right Left, Seq Right)>((Left: Seq(), Right: Seq())), (fs, fab) => fs.BindSecond(s => fab.Match(l => s with { Left = s.Left.Add(l) }, r => s with { Right = s.Right.Add(r) }))); /// /// Partition the foldable of coproducts into two left and right sequences. /// /// Left value type /// Right value type /// Two left and right sequences public static K Left, Seq Right)> partitionSequence(IEnumerable> fabs) where F : CoproductK, Bimonad => partition(fabs.AsIterable()); } ================================================ FILE: LanguageExt.Core/Traits/CoproductK/CoproductK.Trait.cs ================================================ using System; using static LanguageExt.Prelude; namespace LanguageExt.Traits; /// /// Co-product trait (abstract version of `Either`) /// /// Self public interface CoproductK : CoproductCons where F : CoproductK { /// /// Pattern-match either the left or right value in the coproduct /// /// Coproduct value /// Function to map the left value to a result /// Function to map the right value to a result /// Coproduct trait type /// Left value type /// Right value type /// Result type /// Result of mapping either the left or right values with the functions provided public static abstract K Match(Func Left, Func Right, K fab); /// /// Pattern-match either the left or right value in the coproduct /// /// Coproduct value /// Function to map the left value to a result /// Function to map the right value to a result /// Coproduct trait type /// Left value type /// Right value type /// Result type /// Result of mapping either the left or right values with the functions provided public static virtual K Match(C Left, Func Right, K fab) => F.Match(_ => Left, Right, fab); /// /// Pattern-match either the left or right value in the coproduct /// /// Coproduct value /// Function to map the left value to a result /// Function to map the right value to a result /// Coproduct trait type /// Left value type /// Right value type /// Result type /// Result of mapping either the left or right values with the functions provided public static virtual K Match(Func Left, C Right, K fab) => F.Match(Left, _ => Right, fab); /// /// Pattern-match the left value in the coproduct /// /// Coproduct value /// Function to map the left value to a result /// Coproduct trait type /// Left value type /// Right value type /// Either the right value or the result of mapping the left with the function provided public static virtual K IfLeft(Func Left, K fab) => F.Match(Left, identity, fab); /// /// Pattern-match the left value in the coproduct /// /// Coproduct value /// Default value to use if the state is `Left` /// Coproduct trait type /// Left value type /// Right value type /// Either the right value or provided `Left` value public static virtual K IfLeft(B Left, K fab) => F.Match(_ => Left, identity, fab); /// /// Pattern-match either the left or right value in the coproduct /// /// Coproduct value /// Function to map the right value to a result /// Coproduct trait type /// Left value type /// Right value type /// Either the left value or the result of mapping the right value with the function provided public static virtual K IfRight(Func Right, K fab) => F.Match(identity, Right, fab); /// /// Pattern-match either the left or right value in the coproduct /// /// Coproduct value /// Default value to use if the state is `Right` /// Coproduct trait type /// Left value type /// Right value type /// Either the left value or provided `Right` value public static virtual K IfRight(A Right, K fab) => F.Match(identity, _ => Right, fab); } ================================================ FILE: LanguageExt.Core/Traits/Coreadable/Coreadable.Extensions.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public static class CoreadableExtensions { public static K Local(this K ma, Func f) where M : Coreadable => M.Local(f, ma); } ================================================ FILE: LanguageExt.Core/Traits/Coreadable/Coreadable.Module.cs ================================================ using System; namespace LanguageExt.Traits; public static class Coreadable { public static K ask() where M : Coreadable => M.Ask(); public static K asks(Func f) where M : Coreadable => M.Asks(f); public static K asksM(Func> f) where M : Coreadable, Bimonad => M.FlattenSecond(M.Asks(f)); public static K local(Func f, K ma) where M : Coreadable => M.Local(f, ma); } ================================================ FILE: LanguageExt.Core/Traits/Coreadable/Coreadable.Trait.cs ================================================ using System; namespace LanguageExt.Traits; public interface Coreadable where M : Coreadable { public static abstract K Asks(Func f); public static virtual K Ask() => M.Asks(Prelude.identity); public static abstract K Local( Func f, K ma); } ================================================ FILE: LanguageExt.Core/Traits/Decidable/Decidable.Module.cs ================================================ using System; using static LanguageExt.Prelude; namespace LanguageExt.Traits; /// /// A `Divisible` contravariant functor is the contravariant analogue of `Applicative`. /// /// Continuing the intuition that 'Contravariant' functors (`Cofunctor`) consume input, a 'Divisible' /// contravariant functor also has the ability to be composed "beside" another contravariant /// functor. /// /// Self referring type public static class Decidable { /// /// Acts as identity to 'Choose'. /// public static K lose(Func f) where F : Decidable => F.Lose(f); /// /// Acts as identity to 'Choose'. /// /// /// lost = lose(identity) /// public static K lost() where F : Decidable => lose(identity); /// /// Fan out the input /// public static K route(Func> f, K fb, K fc) where F : Decidable => F.Route(f, fb, fc); /// /// Fan out the input /// /// /// route(fb, fc) = route(id, fb, fc) /// public static K> route(K fa, K fb) where F : Decidable => route, A, B>(identity, fa, fb); } ================================================ FILE: LanguageExt.Core/Traits/Decidable/Decidable.Prelude.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; /// /// A `Divisible` contravariant functor is the contravariant analogue of `Applicative`. /// /// Continuing the intuition that 'Contravariant' functors (`Cofunctor`) consume input, a 'Divisible' /// contravariant functor also has the ability to be composed "beside" another contravariant /// functor. /// /// Self referring type public static partial class Prelude { } ================================================ FILE: LanguageExt.Core/Traits/Decidable/Decidable.Trait.cs ================================================ using System; namespace LanguageExt.Traits; /// /// A `Decidable` contravariant functor is the contravariant analogue of `Alternative`. /// /// Noting the superclass constraint that `f` must also be `Divisible`, a `Decidable` functor has the ability to /// "fan out" input, under the intuition that contravariant functors consume input. /// /// Self-referring type public interface Decidable : Divisible { /// /// Acts as identity to 'Choose'. /// public static abstract K Lose(Func f); /// /// Fan out the input /// public static abstract K Route(Func> f, K fb, K fc); } ================================================ FILE: LanguageExt.Core/Traits/Deriving/Deriving.cs ================================================ namespace LanguageExt.Traits; /// /// `Deriving` is an alias for `NaturalIso` /// /// /// It is aliased to be more declarative when used in trait-implementations. The idea is that your /// `Supertype` is a wrapper around your `Subtype`. Use of the `Deriving` trait, and the `Deriving.*` interfaces, /// makes trait-implementations much easier. /// /// Wrapper type that is a super-type wrapper of `Subtype` /// `Subtype` of `Supertype` public interface Deriving : NaturalIso; ================================================ FILE: LanguageExt.Core/Traits/Divisible/Divisible.Module.cs ================================================ using System; namespace LanguageExt.Traits; /// /// A `Divisible` contravariant functor is the contravariant analogue of `Applicative`. /// /// Continuing the intuition that 'Contravariant' functors (`Cofunctor`) consume input, a 'Divisible' /// contravariant functor also has the ability to be composed "beside" another contravariant /// functor. /// /// Self referring type public static class Divisible { /// /// If one can handle split `a` into `(b, c)`, as well as handle `b`s and `c`s, then one can handle `a`s /// public static K divide(Func f, K fb, K fc) where F : Divisible => F.Divide(f, fb, fc); /// /// Conquer acts as an identity for combining `Divisible` functors. /// public static K conquer() where F : Divisible => F.Conquer(); } ================================================ FILE: LanguageExt.Core/Traits/Divisible/Divisible.Prelude.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; /// /// A `Divisible` contravariant functor is the contravariant analogue of `Applicative`. /// /// Continuing the intuition that 'Contravariant' functors (`Cofunctor`) consume input, a 'Divisible' /// contravariant functor also has the ability to be composed "beside" another contravariant /// functor. /// /// Self referring type public static partial class Prelude { /// /// If one can handle split `a` into `(b, c)`, as well as handle `b`s and `c`s, then one can handle `a`s /// public static K divide(Func f, K fb, K fc) where F : Divisible => F.Divide(f, fb, fc); /// /// Conquer acts as an identity for combining `Divisible` functors. /// public static K conquer() where F : Divisible => F.Conquer(); } ================================================ FILE: LanguageExt.Core/Traits/Divisible/Divisible.Trait.cs ================================================ using System; namespace LanguageExt.Traits; /// /// A `Divisible` contravariant functor is the contravariant analogue of `Applicative`. /// /// Continuing the intuition that 'Contravariant' functors (`Cofunctor`) consume input, a 'Divisible' /// contravariant functor also has the ability to be composed "beside" another contravariant /// functor. /// /// Self referring type public interface Divisible : Cofunctor { /// /// If one can handle split `a` into `(b, c)`, as well as handle `b`s and `c`s, then one can handle `a`s /// public static abstract K Divide(Func f, K fb, K fc); /// /// Conquer acts as an identity for combining `Divisible` functors. /// public static abstract K Conquer(); } ================================================ FILE: LanguageExt.Core/Traits/Domain/Amount.cs ================================================ using System; using System.Numerics; namespace LanguageExt.Traits.Domain; /// /// A typical use of domain types is representing quantities, such as the amount of money in USD in a bank account, /// or the file-size in bytes. Being able to compare, add, and subtract amounts is essential. /// /// Generally, we cannot multiply or divide two compatible amounts and expect to get the amount of the same type back. /// /// Unless we’re modeling mathematical entities, such as probabilities or points on an elliptic curve.. Multiplying two /// dollars by two dollars gives four squared dollars. I don’t know about you, but I’m yet to find a practical use for /// squared dollars. /// /// Multiplying amounts by a dimensionless number, however, is meaningful. There is nothing wrong with a banking app /// increasing a dollar amount by ten percent or a disk utility dividing the total number of allocated bytes by the file /// count. /// /// The appropriate mathematical abstraction for amounts is vector spaces. Vector space is a set with additional /// operations defined on the elements of this set: addition, subtraction, and scalar multiplication, such that /// behaviors of these operations satisfy a few natural axioms. /// /// This is the same as `VectorSpace` but with ordering /// Type implementing this interface /// Scalar units public interface Amount : VectorSpace, IComparable, IComparisonOperators where SELF : Amount; ================================================ FILE: LanguageExt.Core/Traits/Domain/DomainType.cs ================================================ namespace LanguageExt.Traits.Domain; /// /// Fundamental base-trait for implementing domain-types. This is the basis for `Identifier`, `Locus`, `VectorSpace`, /// and `Quantity`. It allows the derived types to be safely instantiated from simpler values, like `int`, `float`, etc. /// And, to be converted back from the domain-type to the simpler representation. /// /// Type implementing this interface public interface DomainType where SELF : DomainType; /// /// Fundamental base-trait for implementing domain-types. This is the basis for `Identifier`, `Locus`, `VectorSpace`, /// and `Quantity`. It allows the derived types to be safely instantiated from simpler values, like `int`, `float`, etc. /// And, to be converted back from the domain-type to the simpler representation. /// /// Type implementing this interface /// Underlying representation public interface DomainType : DomainType where SELF : DomainType { /// /// Creates a domain value from its representation value /// /// /// Either an `Error` or a validly constructed `SELF`. /// public static abstract Fin From(REPR repr); /// /// Creates a domain value from its representation value /// /// /// Either throws an exception or returns a validly constructed `SELF`. /// public static virtual SELF FromUnsafe(REPR repr) => SELF.From(repr).ThrowIfFail(); /// /// Extracts the representation value from its domain value /// REPR To(); } ================================================ FILE: LanguageExt.Core/Traits/Domain/Identifier.cs ================================================ using System; using System.Numerics; namespace LanguageExt.Traits.Domain; /// /// One of the most common uses of domain types is a transparent handle for an entity or an asset in the real world, /// such as a customer identifier in an online store or an employee number in a payroll application. I call these types /// identifiers. /// /// Identifiers have no structure, i.e., we don’t care about their internal representation. The only fundamental /// requirement is the ability to compare values of those types for equality. This lack of structure suggests an /// appropriate mathematical model for such types: a set, a collection of distinct objects. /// /// Type implementing this interface public interface Identifier : DomainType, IEquatable, IEqualityOperators where SELF : Identifier; ================================================ FILE: LanguageExt.Core/Traits/Domain/Locus.cs ================================================ // TODO: Decide whether you want to develop this idea or not // https://mmapped.blog/posts/25-domain-types.html using System; using System.Numerics; namespace LanguageExt.Traits.Domain; /// /// Working with space-like structures, such as time and space, poses an interesting challenge. Spaces have two types of /// values: absolute positions and relative distances. /// /// Positions refer to points in space, such as timestamps or geographical coordinates. Distances represent a difference /// between two such points. /// /// Some natural languages acknowledge the distinction and offer different words for these concepts, such as o’clock vs. /// hours. /// /// While distances behave the same way as `Amount`, positions are trickier. We can compare, order, and subtract them to /// compute the distance between two points. For example, subtracting 5 am on Friday from 3 am on Saturday gives us /// twenty-two hours. Adding or multiplying these dates makes no sense, however. This semantic demands a new class of /// types, loci (plural of locus). /// /// We can view each position as a distance from a fixed origin point. Changing the origin or the distance type calls /// for a new locus type. /// /// Type implementing this interface /// Additive units /// Distance scalar units public interface Locus : Identifier, IComparable, IComparisonOperators, IUnaryNegationOperators, IAdditiveIdentity, IAdditionOperators, ISubtractionOperators where SELF : Locus where DISTANCE : Amount; ================================================ FILE: LanguageExt.Core/Traits/Domain/README.md ================================================ Inspired by: https://mmapped.blog/posts/25-domain-types.html ================================================ FILE: LanguageExt.Core/Traits/Domain/VectorSpace.cs ================================================ using System; using System.Numerics; namespace LanguageExt.Traits.Domain; /// /// A typical use of domain types is representing quantities, such as the amount of money in USD in a bank account, /// or the file-size in bytes. Being able to compare, add, and subtract amounts is essential. /// /// Generally, we cannot multiply or divide two compatible amounts and expect to get the amount of the same type back. /// /// Unless we’re modeling mathematical entities, such as probabilities or points on an elliptic curve.. Multiplying two /// dollars by two dollars gives four squared dollars. I don’t know about you, but I’m yet to find a practical use for /// squared dollars. /// /// Multiplying amounts by a dimensionless number, however, is meaningful. There is nothing wrong with a banking app /// increasing a dollar amount by ten percent or a disk utility dividing the total number of allocated bytes by the file /// count. /// /// The appropriate mathematical abstraction for amounts is vector spaces. Vector space is a set with additional /// operations defined on the elements of this set: addition, subtraction, and scalar multiplication, such that /// behaviors of these operations satisfy a few natural axioms. /// /// Type implementing this interface /// Scalar units public interface VectorSpace : Identifier, IUnaryNegationOperators, IAdditionOperators, ISubtractionOperators, IMultiplyOperators, IDivisionOperators where SELF : VectorSpace; ================================================ FILE: LanguageExt.Core/Traits/Eq/Eq.Extensions.cs ================================================ using System.Collections.Generic; using LanguageExt.ClassInstances; using LanguageExt.Traits; using System.Diagnostics.Contracts; namespace LanguageExt; public static partial class EqExtensions { /// /// Structural equality test /// /// The left hand side of the equality operation /// The right hand side of the equality operation /// True if x and y are equal [Pure] public static bool Equals(this A[] x, A[] y) where EQ : Eq => EqArray.Equals(x, y); /// /// Structural equality test /// /// The left hand side of the equality operation /// The right hand side of the equality operation /// True if x and y are equal [Pure] public static bool Equals(this A? mx, A? my) where EQ : Eq where A : struct => (mx, my) switch { (null, null) => true, (_, null) => false, (null, _) => false, var (x, y) => EQ.Equals(x.Value, y.Value) }; /// /// Structural equality test /// /// The left hand side of the equality operation /// The right hand side of the equality operation /// True if x and y are equal [Pure] public static bool Equals(this IEnumerable x, IEnumerable y) where EQ : Eq => EqEnumerable.Equals(x, y); } ================================================ FILE: LanguageExt.Core/Traits/Eq/Eq.Module.cs ================================================ using System.Collections.Generic; using LanguageExt.Traits; using System.Diagnostics.Contracts; namespace LanguageExt; public static partial class Eq { /// /// Structural equality test /// /// The left hand side of the equality operation /// The right hand side of the equality operation /// True if x and y are equal [Pure] public static bool equals(A x, A y) where A : Eq => A.Equals(x, y); class EqEqualityComparer : IEqualityComparer where EqA : Eq { public bool Equals(A? x, A? y) => (x, y) switch { (null, null) => true, (null, _) => false, (_, null) => false, var (nx, ny) => EqA.Equals(nx, ny) }; public int GetHashCode(A obj) => EqA.GetHashCode(obj); } public static IEqualityComparer Comparer() where EqA : Eq => Cache.Default; static class Cache where EqA : Eq { public static readonly IEqualityComparer Default = new EqEqualityComparer(); } } ================================================ FILE: LanguageExt.Core/Traits/Eq/Eq.Prelude.cs ================================================ using LanguageExt.ClassInstances; using LanguageExt.Traits; using System.Collections.Generic; using System.Diagnostics.Contracts; namespace LanguageExt; public static partial class Prelude { /// /// Structural equality test /// /// The left hand side of the equality operation /// The right hand side of the equality operation /// True if x and y are equal [Pure] public static bool equals(A x, A y) where EQ : Eq => EQ.Equals(x, y); /// /// Structural equality test /// /// The left hand side of the equality operation /// The right hand side of the equality operation /// True if x and y are equal [Pure] public static bool equals(Option x, Option y) where EQ : Eq => x.Equals(y); /// /// Structural equality test /// /// The left hand side of the equality operation /// The right hand side of the equality operation /// True if x and y are equal [Pure] public static bool equals(Either x, Either y) where EQ : Eq => x.Equals(y); /// /// Structural equality test /// /// The left hand side of the equality operation /// The right hand side of the equality operation /// True if x and y are equal [Pure] public static bool equals(Either x, Either y) where EQA : Eq where EQB : Eq => x.Equals(y); /// /// Structural equality test /// /// The left hand side of the equality operation /// The right hand side of the equality operation /// True if x and y are equal [Pure] public static bool equals(A? mx, A? my) where EQ : Eq where A : struct => (mx, my) switch { (null, null) => true, (_, null) => false, (null, _) => false, var (x, y) => EQ.Equals(x.Value, y.Value) }; /// /// Structural equality test /// /// The left hand side of the equality operation /// The right hand side of the equality operation /// True if x and y are equal [Pure] public static bool equals(Lst x, Lst y) where EQ : Eq => EqLst.Equals(x, y); /// /// Structural equality test /// /// The left hand side of the equality operation /// The right hand side of the equality operation /// True if x and y are equal [Pure] public static bool equals(HashSet x, HashSet y) where EQ : Eq => EqHashSet.Equals(x, y); /// /// Structural equality test /// /// The left hand side of the equality operation /// The right hand side of the equality operation /// True if x and y are equal [Pure] public static bool equals(Que x, Que y) where EQ : Eq => EqQue.Equals(x, y); /// /// Structural equality test /// /// The left hand side of the equality operation /// The right hand side of the equality operation /// True if x and y are equal [Pure] public static bool equals(Set x, Set y) where EQ : Eq => EqSet.Equals(x, y); /// /// Structural equality test /// /// The left hand side of the equality operation /// The right hand side of the equality operation /// True if x and y are equal [Pure] public static bool equals(Arr x, Arr y) where EQ : Eq => EqArr.Equals(x, y); /// /// Structural equality test /// /// The left hand side of the equality operation /// The right hand side of the equality operation /// True if x and y are equal [Pure] public static bool equals(A[] x, A[] y) where EQ : Eq => EqArray.Equals(x, y); /// /// Structural equality test /// /// The left hand side of the equality operation /// The right hand side of the equality operation /// True if x and y are equal [Pure] public static bool equals(IEnumerable x, IEnumerable y) where EQ : Eq => EqEnumerable.Equals(x, y); /// /// Structural equality test /// /// The left hand side of the equality operation /// The right hand side of the equality operation /// True if x and y are equal [Pure] public static bool equals(Seq x, Seq y) where EQ : Eq => EqSeq.Equals(x, y); } ================================================ FILE: LanguageExt.Core/Traits/Eq/Eq.cs ================================================ using System.Collections.Generic; using System.Diagnostics.Contracts; using LanguageExt.Attributes; namespace LanguageExt.Traits; /// /// Equality trait /// /// /// The type for which equality is defined /// [Trait("Eq*")] public interface Eq : Hashable, Trait { /// /// Equality test /// /// The left hand side of the equality operation /// The right hand side of the equality operation /// True if x and y are equal [Pure] public static abstract bool Equals(A x, A y); } ================================================ FILE: LanguageExt.Core/Traits/Fallible/Fallible.Extensions.Catch.E.cs ================================================ using System; using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt; /// /// Extensions for higher-kinded structures that have a failure state `E` /// /// Higher-kinded structure /// Failure type public static partial class FallibleExtensionsE { //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Error catching by predicate // /// /// Run the `Fallible` structure. If in a failed state, test the failure value /// against the predicate. If, it returns `true`, run the `Fail` function with /// the failure value. /// /// `Fallible` structure /// Predicate to test any failure values /// Handler when in failed state /// Bound value type /// Either `fa` or the result of `Fail` if `fa` is in a failed state and the /// predicate returns true for the failure value public static K Catch( this K fa, Func Predicate, Func> Fail) where F : Fallible => F.Catch(fa, Predicate, Fail); /// /// Run the `Fallible` structure. If in a failed state, test the failure value /// against the predicate. If, it returns `true`, run the `Fail` function with /// the failure value. /// /// `Fallible` structure /// Predicate to test any failure values /// Handler when in failed state /// Bound value type /// Either `fa` or the result of `Fail` if `fa` is in a failed state and the /// predicate returns true for the failure value public static K Catch( this K ma, Func Predicate, Func> Fail) where M : Fallible, MonadIO => M.Catch(ma, Predicate, e => M.LiftIO(Fail(e))); /// /// Run the `Fallible` structure. If in a failed state, test the failure value /// against the predicate. If, it returns `true`, run the `Fail` function with /// the failure value. /// /// `Fallible` structure /// Predicate to test any failure values /// Handler when in failed state /// Bound value type /// Either `fa` or the result of `Fail` if `fa` is in a failed state and the /// predicate returns true for the failure value public static K Catch( this K fa, Func Predicate, Func Fail) where F : Fallible => F.Catch(fa, Predicate, e => F.Fail(Fail(e))); /// /// Run the `Fallible` structure. If in a failed state, test the failure value /// against the predicate. If, it returns `true`, run the `Fail` function with /// the failure value. /// /// `Fallible` structure /// Predicate to test any failure values /// Handler when in failed state /// Bound value type /// Either `fa` or the result of `Fail` if `fa` is in a failed state and the /// predicate returns true for the failure value public static K Catch( this K fa, Func Predicate, Func Fail) where F : Fallible, Applicative => F.Catch(fa, Predicate, e => F.Pure(Fail(e))); /// /// Run the `Fallible` structure. If in a failed state, test the failure value /// against the catch structure and run its action if a match. /// /// `Fallible` structure /// Catch structure created by the `@catch` functions. Contains the /// match predicate and action /// Bound value type /// Either `fa` or the result of running the catch structure's action if `fa` is in /// a failed state and the public static K Catch( this K fa, CatchM @catch) where F : Fallible => F.Catch(fa, @catch.Match, @catch.Action); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Error catching by equality // /// /// Run the `Fallible` structure. If in a failed state, test the failure value /// against the predicate. If, it returns `true`, run the `Fail` function with /// the failure value. /// /// `Fallible` structure /// Error to match to /// Handler when in failed state /// Bound value type /// Either `fa` or the result of `Fail` if `fa` is in a failed state and the /// predicate returns true for the failure value public static K Catch( this K ma, E Match, Func> Fail) where M : Fallible, MonadIO => M.Catch(ma, e => Match?.Equals(e) ?? false, e => M.LiftIO(Fail(e))); /// /// Run the `Fallible` structure. If in a failed state, test the failure value /// against the predicate. If, it returns `true`, run the `Fail` function with /// the failure value. /// /// `Fallible` structure /// Error to match to /// Handler when in failed state /// Bound value type /// Either `fa` or the result of `Fail` if `fa` is in a failed state and the /// predicate returns true for the failure value public static K Catch( this K fa, E Match, Func Fail) where F : Fallible => F.Catch(fa, e => Match?.Equals(e) ?? false, e => F.Fail(Fail(e))); /// /// Run the `Fallible` structure. If in a failed state, test the failure value /// against the predicate. If, it returns `true`, run the `Fail` function with /// the failure value. /// /// `Fallible` structure /// Error to match to /// Handler when in failed state /// Bound value type /// Either `fa` or the result of `Fail` if `fa` is in a failed state and the /// predicate returns true for the failure value public static K Catch( this K fa, E Match, Func Fail) where F : Fallible, Applicative => F.Catch(fa, e => Match?.Equals(e) ?? false, e => F.Pure(Fail(e))); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Catch all errors // /// /// Run the `Fallible` structure. If in a failed state, test the failure value /// against the predicate. If, it returns `true`, run the `Fail` function with /// the failure value. /// /// `Fallible` structure /// Handler when in failed state /// Bound value type /// Either `fa` or the result of `Fail` if `fa` is in a failed state and the /// predicate returns true for the failure value public static K Catch( this K fa, Func> Fail) where F : Fallible => F.Catch(fa, _ => true, Fail); /// /// Run the `Fallible` structure. If in a failed state, test the failure value /// against the predicate. If, it returns `true`, run the `Fail` function with /// the failure value. /// /// `Fallible` structure /// Handler when in failed state /// Bound value type /// Either `fa` or the result of `Fail` if `fa` is in a failed state and the /// predicate returns true for the failure value public static K Catch( this K ma, Func> Fail) where M : Fallible, MonadIO => M.Catch(ma, _ => true, e => M.LiftIO(Fail(e))); /// /// Run the `Fallible` structure. If in a failed state, test the failure value /// against the predicate. If, it returns `true`, run the `Fail` function with /// the failure value. /// /// `Fallible` structure /// Handler when in failed state /// Bound value type /// Either `fa` or the result of `Fail` if `fa` is in a failed state and the /// predicate returns true for the failure value public static K Catch( this K fa, Func Fail) where F : Fallible => F.Catch(fa, _ => true, e => F.Fail(Fail(e))); /// /// Run the `Fallible` structure. If in a failed state, test the failure value /// against the predicate. If, it returns `true`, run the `Fail` function with /// the failure value. /// /// `Fallible` structure /// Handler when in failed state /// Bound value type /// Either `fa` or the result of `Fail` if `fa` is in a failed state and the /// predicate returns true for the failure value public static K Catch( this K fa, Func Fail) where F : Fallible, Applicative => F.Catch(fa, _ => true, e => F.Pure(Fail(e))); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Catch all errors and provide alternative values // /// /// Run the `Fallible` structure. If in a failed state, replace the failure value with `fail`. /// /// `Fallible` structure /// Bound value type public static K Catch(this K fa, Fail fail) where F : Fallible => F.Catch(fa, _ => true, _ => F.Fail(fail.Value)); /// /// Run the `Fallible` structure. If in a failed state, replace the failure value with `fail`. /// /// `Fallible` structure /// Bound value type public static K Catch(this K fa, E fail) where F : Fallible => F.Catch(fa, _ => true, _ => F.Fail(fail)); /// /// Run the `Fallible` structure. If in a failed state, replace the failure value with /// the success value and cancelling the failure. /// /// `Fallible` structure /// Bound value type public static K Catch(this K fa, Pure value) where F : Fallible, Applicative => F.Catch(fa, _ => true, _ => F.Pure(value.Value)); /// /// Run the `Fallible` structure. If in a failed state, replace the failure value with /// the success value and cancelling the failure. /// /// `Fallible` structure /// Bound value type public static K Catch(this K fa, A value) where F : Fallible, Applicative => F.Catch(fa, _ => true, _ => F.Pure(value)); /// /// Run the `Fallible` structure. If in a failed state, replace the failure value with /// the `alternative` computation, which may succeed or fail. /// /// `Fallible` structure /// Bound value type public static K Catch(this K fa, K alternative) where F : Fallible => F.Catch(fa, _ => true, _ => alternative); } ================================================ FILE: LanguageExt.Core/Traits/Fallible/Fallible.Extensions.Catch.cs ================================================ using System; using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt; /// /// Extensions for higher-kinded structures that have a failure state `Error` /// /// Higher-kinded structure public static partial class FallibleExtensions { //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Error catching by predicate // /// /// Run the `Fallible` structure. If in a failed state, test the failure value /// against the predicate. If, it returns `true`, run the `Fail` function with /// the failure value. /// /// `Fallible` structure /// Predicate to test any failure values /// Handler when in failed state /// Bound value type /// Either `fa` or the result of `Fail` if `fa` is in a failed state and the /// predicate returns true for the failure value public static K Catch( this K fa, Func Predicate, Func> Fail) where F : Fallible => F.Catch(fa, Predicate, Fail); /// /// Run the `Fallible` structure. If in a failed state, test the failure value /// against the predicate. If, it returns `true`, run the `Fail` function with /// the failure value. /// /// `Fallible` structure /// Predicate to test any failure values /// Handler when in failed state /// Bound value type /// Either `fa` or the result of `Fail` if `fa` is in a failed state and the /// predicate returns true for the failure value public static K CatchIO( this K ma, Func Predicate, Func> Fail) where M : Fallible, MonadIO => M.Catch(ma, Predicate, e => M.LiftIO(Fail(e))); /// /// Run the `Fallible` structure. If in a failed state, test the failure value /// against the predicate. If, it returns `true`, run the `Fail` function with /// the failure value. /// /// `Fallible` structure /// Predicate to test any failure values /// Handler when in failed state /// Bound value type /// Either `fa` or the result of `Fail` if `fa` is in a failed state and the /// predicate returns true for the failure value public static K Catch( this K fa, Func Predicate, Func Fail) where F : Fallible => F.Catch(fa, Predicate, e => F.Fail(Fail(e))); /// /// Run the `Fallible` structure. If in a failed state, test the failure value /// against the predicate. If, it returns `true`, run the `Fail` function with /// the failure value. /// /// `Fallible` structure /// Predicate to test any failure values /// Handler when in failed state /// Bound value type /// Either `fa` or the result of `Fail` if `fa` is in a failed state and the /// predicate returns true for the failure value public static K Catch( this K fa, Func Predicate, Func Fail) where F : Fallible, Applicative => F.Catch(fa, Predicate, e => F.Pure(Fail(e))); /// /// Run the `Fallible` structure. If in a failed state, test the failure value /// against the catch structure and run its action if a match. /// /// `Fallible` structure /// Catch structure created by the `@catch` functions. Contains the /// match predicate and action /// Bound value type /// Either `fa` or the result of running the catch structure's action if `fa` is in /// a failed state and the public static K Catch( this K fa, CatchM @catch) where F : Fallible => F.Catch(fa, @catch.Match, @catch.Action); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Error catching by equality // /// /// Run the `Fallible` structure. If in a failed state, test the failure value /// against the predicate. If, it returns `true`, run the `Fail` function with /// the failure value. /// /// `Fallible` structure /// Error to match to /// Handler when in failed state /// Bound value type /// Either `fa` or the result of `Fail` if `fa` is in a failed state and the /// predicate returns true for the failure value public static K Catch( this K fa, Error Match, Func> Fail) where F : Fallible => F.Catch(fa, Match.Is, Fail); /// /// Run the `Fallible` structure. If in a failed state, test the failure value /// against the predicate. If, it returns `true`, run the `Fail` function with /// the failure value. /// /// `Fallible` structure /// Error to match to /// Handler when in failed state /// Bound value type /// Either `fa` or the result of `Fail` if `fa` is in a failed state and the /// predicate returns true for the failure value public static K CatchIO( this K ma, Error Match, Func> Fail) where M : Fallible, MonadIO => M.Catch(ma, Match.Is, e => M.LiftIO(Fail(e))); /// /// Run the `Fallible` structure. If in a failed state, test the failure value /// against the predicate. If, it returns `true`, run the `Fail` function with /// the failure value. /// /// `Fallible` structure /// Error to match to /// Handler when in failed state /// Bound value type /// Either `fa` or the result of `Fail` if `fa` is in a failed state and the /// predicate returns true for the failure value public static K Catch( this K fa, Error Match, Func Fail) where F : Fallible => F.Catch(fa, Match.Is, e => F.Fail(Fail(e))); /// /// Run the `Fallible` structure. If in a failed state, test the failure value /// against the predicate. If, it returns `true`, run the `Fail` function with /// the failure value. /// /// `Fallible` structure /// Error to match to /// Handler when in failed state /// Bound value type /// Either `fa` or the result of `Fail` if `fa` is in a failed state and the /// predicate returns true for the failure value public static K Catch( this K fa, Error Match, Func Fail) where F : Fallible, Applicative => F.Catch(fa, Match.Is, e => F.Pure(Fail(e))); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Error catching by error code equality // /// /// Run the `Fallible` structure. If in a failed state, test the failure value /// against the predicate. If, it returns `true`, run the `Fail` function with /// the failure value. /// /// `Fallible` structure /// Error code to match to /// Handler when in failed state /// Bound value type /// Either `fa` or the result of `Fail` if `fa` is in a failed state and the /// predicate returns true for the failure value public static K Catch( this K fa, int Code, Func> Fail) where F : Fallible => F.Catch(fa, e => Code == e.Code || e.HasCode(Code), Fail); /// /// Run the `Fallible` structure. If in a failed state, test the failure value /// against the predicate. If, it returns `true`, run the `Fail` function with /// the failure value. /// /// `Fallible` structure /// Error code to match to /// Handler when in failed state /// Bound value type /// Either `fa` or the result of `Fail` if `fa` is in a failed state and the /// predicate returns true for the failure value public static K CatchIO( this K ma, int Code, Func> Fail) where M : Fallible, MonadIO => M.Catch(ma, e => Code == e.Code || e.HasCode(Code), e => M.LiftIO(Fail(e))); /// /// Run the `Fallible` structure. If in a failed state, test the failure value /// against the predicate. If, it returns `true`, run the `Fail` function with /// the failure value. /// /// `Fallible` structure /// Error code to match to /// Handler when in failed state /// Bound value type /// Either `fa` or the result of `Fail` if `fa` is in a failed state and the /// predicate returns true for the failure value public static K Catch( this K fa, int Code, Func Fail) where F : Fallible => F.Catch(fa, e => Code == e.Code || e.HasCode(Code), e => F.Fail(Fail(e))); /// /// Run the `Fallible` structure. If in a failed state, test the failure value /// against the predicate. If, it returns `true`, run the `Fail` function with /// the failure value. /// /// `Fallible` structure /// Error code to match to /// Handler when in failed state /// Bound value type /// Either `fa` or the result of `Fail` if `fa` is in a failed state and the /// predicate returns true for the failure value public static K Catch( this K fa, int Code, Func Fail) where F : Fallible, Applicative => F.Catch(fa, e => Code == e.Code || e.HasCode(Code), e => F.Pure(Fail(e))); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Catch all errors // /// /// Run the `Fallible` structure. If in a failed state, test the failure value /// against the predicate. If, it returns `true`, run the `Fail` function with /// the failure value. /// /// `Fallible` structure /// Handler when in failed state /// Bound value type /// Either `fa` or the result of `Fail` if `fa` is in a failed state and the /// predicate returns true for the failure value public static K Catch( this K fa, Func> Fail) where F : Fallible => F.Catch(fa, _ => true, Fail); /// /// Run the `Fallible` structure. If in a failed state, test the failure value /// against the predicate. If, it returns `true`, run the `Fail` function with /// the failure value. /// /// `Fallible` structure /// Handler when in failed state /// Bound value type /// Either `fa` or the result of `Fail` if `fa` is in a failed state and the /// predicate returns true for the failure value public static K CatchIO( this K ma, Func> Fail) where M : Fallible, MonadIO => M.Catch(ma, _ => true, e => M.LiftIO(Fail(e))); /// /// Run the `Fallible` structure. If in a failed state, test the failure value /// against the predicate. If, it returns `true`, run the `Fail` function with /// the failure value. /// /// `Fallible` structure /// Handler when in failed state /// Bound value type /// Either `fa` or the result of `Fail` if `fa` is in a failed state and the /// predicate returns true for the failure value public static K Catch( this K fa, Func Fail) where F : Fallible => F.Catch(fa, _ => true, e => F.Fail(Fail(e))); /// /// Run the `Fallible` structure. If in a failed state, test the failure value /// against the predicate. If, it returns `true`, run the `Fail` function with /// the failure value. /// /// `Fallible` structure /// Handler when in failed state /// Bound value type /// Either `fa` or the result of `Fail` if `fa` is in a failed state and the /// predicate returns true for the failure value public static K Catch( this K fa, Func Fail) where F : Fallible, Applicative => F.Catch(fa, _ => true, e => F.Pure(Fail(e))); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Catch all errors and provide alternative values // /// /// Run the `Fallible` structure. If in a failed state, replace the failure value with `fail`. /// /// `Fallible` structure /// Bound value type public static K Catch(this K fa, Fail fail) where F : Fallible => F.Catch(fa, _ => true, _ => F.Fail(fail.Value)); /// /// Run the `Fallible` structure. If in a failed state, replace the failure value with `fail`. /// /// `Fallible` structure /// Bound value type public static K Catch(this K fa, Error fail) where F : Fallible => F.Catch(fa, _ => true, _ => F.Fail(fail)); /// /// Run the `Fallible` structure. If in a failed state, replace the failure value with /// the success value and cancelling the failure. /// /// `Fallible` structure /// Bound value type public static K Catch(this K fa, Pure value) where F : Fallible, Applicative => F.Catch(fa, _ => true, _ => F.Pure(value.Value)); /// /// Run the `Fallible` structure. If in a failed state, replace the failure value with /// the success value and cancelling the failure. /// /// `Fallible` structure /// Bound value type public static K Catch(this K fa, A value) where F : Fallible, Applicative => F.Catch(fa, _ => true, _ => F.Pure(value)); /// /// Run the `Fallible` structure. If in a failed state, replace the failure value with /// the `alternative` computation, which may succeed or fail. /// /// `Fallible` structure /// Bound value type public static K Catch(this K fa, K alternative) where F : Fallible => F.Catch(fa, _ => true, _ => alternative); } ================================================ FILE: LanguageExt.Core/Traits/Fallible/Fallible.Extensions.Fails.E.cs ================================================ using System.Collections.Generic; using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt; public static partial class FallibleExtensionsE { /// /// Partitions a foldable of effects into successes and failures, /// and returns only the failures. /// /// Foldable type /// Fallible monadic type /// Bound value type /// Foldable of fallible monadic values /// A collection of `E` values public static K> Fails( this K> fma) where M : Monad, Fallible where F : Foldable => fma.Fold(M.Pure(Seq.empty()), ma => ms => ms.Bind( s => ma.Bind(_ => M.Pure(s)) .Catch((E e) => M.Pure(s.Add(e))))); /// /// Partitions a collection of effects into successes and failures, /// and returns only the failures. /// /// Fallible monadic type /// Bound value type /// Collection of fallible monadic values /// A collection of `E` values public static K> Fails( this Seq> fma) where M : Monad, Fallible => fma.Kind().Fails(); /// /// Partitions a collection of effects into successes and failures, /// and returns only the failures. /// /// Fallible monadic type /// Bound value type /// Collection of fallible monadic values /// A collection of `E` values public static K> Fails( this Iterable> fma) where M : Monad, Fallible => fma.Kind().Fails(); /// /// Partitions a collection of effects into successes and failures, /// and returns only the failures. /// /// Fallible monadic type /// Bound value type /// Collection of fallible monadic values /// A collection of `E` values public static K> Fails( this Lst> fma) where M : Monad, Fallible => fma.Kind().Fails(); /// /// Partitions a collection of effects into successes and failures, /// and returns only the failures. /// /// Fallible monadic type /// Bound value type /// Collection of fallible monadic values /// A collection of `E` values public static K> Fails( this HashSet> fma) where M : Monad, Fallible => fma.Kind().Fails(); /// /// Partitions a collection of effects into successes and failures, /// and returns only the failures. /// /// Fallible monadic type /// Bound value type /// Collection of fallible monadic values /// A collection of `E` values public static K> Fails( this Set> fma) where M : Monad, Fallible => fma.Kind().Fails(); } ================================================ FILE: LanguageExt.Core/Traits/Fallible/Fallible.Extensions.Fails.cs ================================================ using System.Collections.Generic; using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt; public static partial class FallibleExtensions { /// /// Partitions a foldable of effects into successes and failures, /// and returns only the failures. /// /// Foldable type /// Fallible monadic type /// Bound value type /// Foldable of fallible monadic values /// A collection of `Error` values public static K> Fails( this K> fma) where M : Monad, Fallible where F : Foldable => fma.Fold(M.Pure(Seq.empty()), ma => ms => ms.Bind( s => ma.Bind(_ => M.Pure(s)) .Catch(e => M.Pure(s.Add(e))))); /// /// Partitions a collection of effects into successes and failures, /// and returns only the failures. /// /// Fallible monadic type /// Bound value type /// Collection of fallible monadic values /// A collection of `Error` values public static K> Fails( this Seq> fma) where M : Monad, Fallible => fma.Kind().Fails(); /// /// Partitions a collection of effects into successes and failures, /// and returns only the failures. /// /// Fallible monadic type /// Bound value type /// Collection of fallible monadic values /// A collection of `Error` values public static K> Fails( this Iterable> fma) where M : Monad, Fallible => fma.Kind().Fails(); /// /// Partitions a collection of effects into successes and failures, /// and returns only the failures. /// /// Fallible monadic type /// Bound value type /// Collection of fallible monadic values /// A collection of `Error` values public static K> Fails( this Lst> fma) where M : Monad, Fallible => fma.Kind().Fails(); /// /// Partitions a collection of effects into successes and failures, /// and returns only the failures. /// /// Fallible monadic type /// Bound value type /// Collection of fallible monadic values /// A collection of `Error` values public static K> Fails( this HashSet> fma) where M : Monad, Fallible => fma.Kind().Fails(); /// /// Partitions a collection of effects into successes and failures, /// and returns only the failures. /// /// Fallible monadic type /// Bound value type /// Collection of fallible monadic values /// A collection of `Error` values public static K> Fails( this Set> fma) where M : Monad, Fallible => fma.Kind().Fails(); } ================================================ FILE: LanguageExt.Core/Traits/Fallible/Fallible.Extensions.Partition.E.cs ================================================ using System.Collections.Generic; using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt; public static partial class FallibleExtensionsE { /// /// Partitions a foldable of effects into two lists. /// All the `Fail` elements are extracted, in order, to the first /// component of the output. Similarly, the `Succ` elements are extracted /// to the second component of the output. /// /// Error type /// Foldable type /// Fallible monadic type /// Bound value type /// Foldable of fallible monadic values /// A tuple containing an `Error` sequence and a `Succ` sequence public static K Fails, Seq Succs)> PartitionFallible( this K> fma) where M : Monad, Fallible where F : Foldable => fma.Fold(M.Pure((Fails: Seq.empty(), Succs: Seq.empty())), ma => ms => ms.Bind( s => ma.Map(a => (s.Fails, s.Succs.Add(a))) .Catch((E e) => M.Pure((s.Fails.Add(e), s.Succs))))); /// /// Partitions a collection of effects into two lists. /// All the `Fail` elements are extracted, in order, to the first /// component of the output. Similarly, the `Succ` elements are extracted /// to the second component of the output. /// /// Error type /// Fallible monadic type /// Bound value type /// Collection of fallible monadic values /// A tuple containing an `Error` sequence and a `Succ` sequence public static K Fails, Seq Succs)> PartitionFallible( this Seq> fma) where M : Monad, Fallible => fma.Kind().PartitionFallible(); /// /// Partitions a collection of effects into two lists. /// All the `Fail` elements are extracted, in order, to the first /// component of the output. Similarly, the `Succ` elements are extracted /// to the second component of the output. /// /// Error type /// Fallible monadic type /// Bound value type /// Collection of fallible monadic values /// A tuple containing an `Error` sequence and a `Succ` sequence public static K Fails, Seq Succs)> PartitionFallible( this Iterable> fma) where M : Monad, Fallible => fma.Kind().PartitionFallible(); /// /// Partitions a collection of effects into two lists. /// All the `Fail` elements are extracted, in order, to the first /// component of the output. Similarly, the `Succ` elements are extracted /// to the second component of the output. /// /// Error type /// Fallible monadic type /// Bound value type /// Collection of fallible monadic values /// A tuple containing an `Error` sequence and a `Succ` sequence public static K Fails, Seq Succs)> PartitionFallible( this Lst> fma) where M : Monad, Fallible => fma.Kind().PartitionFallible(); /// /// Partitions a collection of effects into two lists. /// All the `Fail` elements are extracted, in order, to the first /// component of the output. Similarly, the `Succ` elements are extracted /// to the second component of the output. /// /// Error type /// Fallible monadic type /// Bound value type /// Collection of fallible monadic values /// A tuple containing an `Error` sequence and a `Succ` sequence public static K Fails, Seq Succs)> PartitionFallible( this IEnumerable> fma) where M : Monad, Fallible => Iterable.createRange(fma).PartitionFallible(); /// /// Partitions a collection of effects into two lists. /// All the `Fail` elements are extracted, in order, to the first /// component of the output. Similarly, the `Succ` elements are extracted /// to the second component of the output. /// /// Error type /// Fallible monadic type /// Bound value type /// Collection of fallible monadic values /// A tuple containing an `Error` sequence and a `Succ` sequence public static K Fails, Seq Succs)> PartitionFallible( this HashSet> fma) where M : Monad, Fallible => fma.Kind().PartitionFallible(); /// /// Partitions a collection of effects into two lists. /// All the `Fail` elements are extracted, in order, to the first /// component of the output. Similarly, the `Succ` elements are extracted /// to the second component of the output. /// /// Error type /// Fallible monadic type /// Bound value type /// Collection of fallible monadic values /// A tuple containing an `Error` sequence and a `Succ` sequence public static K Fails, Seq Succs)> PartitionFallible( this Set> fma) where M : Monad, Fallible => fma.Kind().PartitionFallible(); } ================================================ FILE: LanguageExt.Core/Traits/Fallible/Fallible.Extensions.Partition.cs ================================================ using System.Collections.Generic; using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt; public static partial class FallibleExtensions { /// /// Partitions a foldable of effects into two lists. /// All the `Fail` elements are extracted, in order, to the first /// component of the output. Similarly, the `Succ` elements are extracted /// to the second component of the output. /// /// Foldable type /// Fallible monadic type /// Bound value type /// Foldable of fallible monadic values /// A tuple containing an `Error` sequence and a `Succ` sequence public static K Fails, Seq Succs)> PartitionFallible( this K> fma) where M : Monad, Fallible where F : Foldable => fma.Fold(M.Pure((Fails: Seq.empty(), Succs: Seq.empty())), ma => ms => ms.Bind( s => ma.Bind(a => M.Pure((s.Fails, s.Succs.Add(a)))) .Catch(e => M.Pure((s.Fails.Add(e), s.Succs))))); /// /// Partitions a collection of effects into two lists. /// All the `Fail` elements are extracted, in order, to the first /// component of the output. Similarly, the `Succ` elements are extracted /// to the second component of the output. /// /// Fallible monadic type /// Bound value type /// Collection of fallible monadic values /// A tuple containing an `Error` sequence and a `Succ` sequence public static K Fails, Seq Succs)> PartitionFallible( this Seq> fma) where M : Monad, Fallible => fma.Kind().PartitionFallible(); /// /// Partitions a collection of effects into two lists. /// All the `Fail` elements are extracted, in order, to the first /// component of the output. Similarly, the `Succ` elements are extracted /// to the second component of the output. /// /// Fallible monadic type /// Bound value type /// Collection of fallible monadic values /// A tuple containing an `Error` sequence and a `Succ` sequence public static K Fails, Seq Succs)> PartitionFallible( this Iterable> fma) where M : Monad, Fallible => fma.Kind().PartitionFallible(); /// /// Partitions a collection of effects into two lists. /// All the `Fail` elements are extracted, in order, to the first /// component of the output. Similarly, the `Succ` elements are extracted /// to the second component of the output. /// /// Fallible monadic type /// Bound value type /// Collection of fallible monadic values /// A tuple containing an `Error` sequence and a `Succ` sequence public static K Fails, Seq Succs)> PartitionFallible( this Lst> fma) where M : Monad, Fallible => fma.Kind().PartitionFallible(); /// /// Partitions a collection of effects into two lists. /// All the `Fail` elements are extracted, in order, to the first /// component of the output. Similarly, the `Succ` elements are extracted /// to the second component of the output. /// /// Fallible monadic type /// Bound value type /// Collection of fallible monadic values /// A tuple containing an `Error` sequence and a `Succ` sequence public static K Fails, Seq Succs)> PartitionFallible( this IEnumerable> fma) where M : Monad, Fallible => Iterable.createRange(fma).PartitionFallible(); /// /// Partitions a collection of effects into two lists. /// All the `Fail` elements are extracted, in order, to the first /// component of the output. Similarly, the `Succ` elements are extracted /// to the second component of the output. /// /// Fallible monadic type /// Bound value type /// Collection of fallible monadic values /// A tuple containing an `Error` sequence and a `Succ` sequence public static K Fails, Seq Succs)> PartitionFallible( this HashSet> fma) where M : Monad, Fallible => fma.Kind().PartitionFallible(); /// /// Partitions a collection of effects into two lists. /// All the `Fail` elements are extracted, in order, to the first /// component of the output. Similarly, the `Succ` elements are extracted /// to the second component of the output. /// /// Fallible monadic type /// Bound value type /// Collection of fallible monadic values /// A tuple containing an `Error` sequence and a `Succ` sequence public static K Fails, Seq Succs)> PartitionFallible( this Set> fma) where M : Monad, Fallible => fma.Kind().PartitionFallible(); } ================================================ FILE: LanguageExt.Core/Traits/Fallible/Fallible.Extensions.Succs.E.cs ================================================ using System.Collections.Generic; using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt; public static partial class FallibleExtensionsE { /// /// Partitions a foldable of effects into successes and failures, /// and returns only the failures. /// /// Foldable type /// Fallible monadic type /// Bound value type /// Foldable of fallible monadic values /// A collection of `Error` values public static K> Succs( this K> fma) where M : Monad, Fallible where F : Foldable => fma.Fold(M.Pure(Seq.empty()), ma => ms => ms.Bind( s => ma.Bind(a => M.Pure(s.Add(a))) .Catch((E _) => M.Pure(s)))); /// /// Partitions a collection of effects into successes and failures, /// and returns only the failures. /// /// Fallible monadic type /// Bound value type /// Collection of fallible monadic values /// A collection of `Error` values public static K> Succs( this Seq> fma) where M : Monad, Fallible => fma.Kind().Succs(); /// /// Partitions a collection of effects into successes and failures, /// and returns only the failures. /// /// Fallible monadic type /// Bound value type /// Collection of fallible monadic values /// A collection of `Error` values public static K> Succs( this Iterable> fma) where M : Monad, Fallible => fma.Kind().Succs(); /// /// Partitions a collection of effects into successes and failures, /// and returns only the failures. /// /// Fallible monadic type /// Bound value type /// Collection of fallible monadic values /// A collection of `Error` values public static K> Succs( this Lst> fma) where M : Monad, Fallible => fma.Kind().Succs(); /// /// Partitions a collection of effects into successes and failures, /// and returns only the failures. /// /// Fallible monadic type /// Bound value type /// Collection of fallible monadic values /// A collection of `Error` values public static K> Succs( this HashSet> fma) where M : Monad, Fallible => fma.Kind().Succs(); /// /// Partitions a collection of effects into successes and failures, /// and returns only the failures. /// /// Fallible monadic type /// Bound value type /// Collection of fallible monadic values /// A collection of `Error` values public static K> Succs( this Set> fma) where M : Monad, Fallible => fma.Kind().Succs(); } ================================================ FILE: LanguageExt.Core/Traits/Fallible/Fallible.Extensions.Succs.cs ================================================ using System.Collections.Generic; using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt; public static partial class FallibleExtensions { /// /// Partitions a foldable of effects into successes and failures, /// and returns only the failures. /// /// Foldable type /// Fallible monadic type /// Bound value type /// Foldable of fallible monadic values /// A collection of `Error` values public static K> Succs( this K> fma) where M : Monad, Fallible where F : Foldable => fma.Fold(M.Pure(Seq.empty()), ma => ms => ms.Bind( s => ma.Bind(a => M.Pure(s.Add(a))) .Catch(_ => M.Pure(s)))); /// /// Partitions a collection of effects into successes and failures, /// and returns only the failures. /// /// Fallible monadic type /// Bound value type /// Collection of fallible monadic values /// A collection of `Error` values public static K> Succs( this Seq> fma) where M : Monad, Fallible => fma.Kind().Succs(); /// /// Partitions a collection of effects into successes and failures, /// and returns only the failures. /// /// Fallible monadic type /// Bound value type /// Collection of fallible monadic values /// A collection of `Error` values public static K> Succs( this Iterable> fma) where M : Monad, Fallible => fma.Kind().Succs(); /// /// Partitions a collection of effects into successes and failures, /// and returns only the failures. /// /// Fallible monadic type /// Bound value type /// Collection of fallible monadic values /// A collection of `Error` values public static K> Succs( this Lst> fma) where M : Monad, Fallible => fma.Kind().Succs(); /// /// Partitions a collection of effects into successes and failures, /// and returns only the failures. /// /// Fallible monadic type /// Bound value type /// Collection of fallible monadic values /// A collection of `Error` values public static K> Succs( this HashSet> fma) where M : Monad, Fallible => fma.Kind().Succs(); /// /// Partitions a collection of effects into successes and failures, /// and returns only the failures. /// /// Fallible monadic type /// Bound value type /// Collection of fallible monadic values /// A collection of `Error` values public static K> Succs( this Set> fma) where M : Monad, Fallible => fma.Kind().Succs(); } ================================================ FILE: LanguageExt.Core/Traits/Fallible/Fallible.Guard.cs ================================================ using System; using LanguageExt.Common; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; public static class FallibleGuardExtensions { /// /// Monadic binding support for `Fallible` /// public static K Bind( this Guard guard, Func> f) where F : Fallible => guard.Flag ? f(unit) : error(guard.OnFalse()); /// /// Monadic binding support for `Fallible` /// public static K SelectMany( this Guard guard, Func> bind, Func project) where F : Fallible, Functor => guard.Flag ? bind(default).Map(b => project(default, b)) : error(guard.OnFalse()); /// /// Monadic binding support for `Fallible` /// public static K Bind( this Guard guard, Func> f) where F : Fallible => guard.Flag ? f(unit) : fail(guard.OnFalse()); /// /// Monadic binding support for `Fallible` /// public static K SelectMany( this Guard guard, Func> bind, Func project) where F : Fallible, Functor => guard.Flag ? bind(default).Map(b => project(default, b)) : fail(guard.OnFalse()); } ================================================ FILE: LanguageExt.Core/Traits/Fallible/Fallible.Interface.cs ================================================ /* using System.Diagnostics.Contracts; namespace LanguageExt.Traits; /// /// Derive your concrete types from this interface to acquire to common set of /// operators that will make working with the generic `Fallible` trait more elegant. /// /// It isn't a requirement to implement this interface for `Fallible` to work, but /// it guides the implementor. It is optional! /// /// /// Primarily makes `@catch` work nicely, but is generally beneficial. /// /// 'Self' type, for example `Either〈L, R〉` /// Trait implementation, for example `Either〈L〉` /// Failure type, for example `L` /// Bound value type, for example `R` public interface Fallible : K where FA : Fallible where F : Fallible, Applicative { [Pure] public static abstract implicit operator FA(Fail fail); [Pure] public static abstract implicit operator FA(Pure fail); [Pure] public static abstract FA operator |(FA lhs, FA rhs); [Pure] public static abstract FA operator |(K lhs, FA rhs); [Pure] public static abstract FA operator |(FA lhs, K rhs); [Pure] public static abstract FA operator |(FA lhs, Pure rhs); [Pure] public static abstract FA operator |(FA lhs, Fail rhs); [Pure] public static abstract FA operator |(FA lhs, CatchM rhs); } */ ================================================ FILE: LanguageExt.Core/Traits/Fallible/Fallible.Module.cs ================================================ using LanguageExt.Common; namespace LanguageExt.Traits; /// /// Module for higher-kinded structures that have a failure state `E` /// /// Higher-kinded structure /// Failure type public static class Fallible { /// /// Raise a failure state in the `Fallible` structure `F` /// /// Error to raise /// Fallible trait /// Error type /// Bound value type /// public static K fail(E error) where F : Fallible => F.Fail(error); /// /// Raise a failure state in the `Fallible` structure `F` /// /// Error to raise /// Fallible trait /// Bound value type /// public static K error(Error error) where F : Fallible => F.Fail(error); /// /// Raise a failure state in the `Fallible` structure `F` /// /// Error to raise /// Fallible trait /// Error type /// Bound value type /// public static K fail(E error) where F : Fallible => F.Fail(error); /// /// Raise a failure state in the `Fallible` structure `F` /// /// Error to raise /// Fallible trait /// Error type /// Bound value type /// public static K error(Error error) where F : Fallible => F.Fail(error); } ================================================ FILE: LanguageExt.Core/Traits/Fallible/Fallible.Operators.cs ================================================ using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt; public static partial class FallibleExtensions { extension(K _) where F : Fallible { /// /// Catch operator. Catch an error if the predicate in the structure matches. /// /// Left-hand side operand /// Right-hand side operand public static K operator |(K lhs, CatchM rhs) => lhs.Catch(rhs); /// /// Catch operator. Catch an error if the predicate in the structure matches. /// /// Left-hand side operand /// Right-hand side operand public static K operator |(K lhs, Fail rhs) => lhs.Catch(rhs); } extension(K _) where F : Fallible { /// /// Catch operator. Catch an error if the predicate in the structure matches. /// /// Left-hand side operand /// Right-hand side operand public static K operator |(K lhs, Error rhs) => lhs.Catch(rhs); } } ================================================ FILE: LanguageExt.Core/Traits/Fallible/Fallible.Prelude.Catch.E.cs ================================================ using System; using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt; public static partial class Prelude { /// /// Catch an error if the predicate matches /// internal static CatchM matchError(Func predicate, Func> Fail) where M : Fallible => new (predicate, Fail); /// /// Catch an error if the predicate matches /// internal static CatchM matchError(Func predicate, Func> Fail) where M : Fallible, Monad => new (predicate, e => MonadIO.liftIO(Fail(e))); /// /// Catch an error if the error matches the argument provided /// public static CatchM @catch(E error, Func> Fail) where M : Fallible => matchError(e => error?.Equals(e) ?? false, Fail); /// /// Catch an error if the error matches the argument provided /// public static CatchM @catch(E error, K Fail) where M : Fallible => matchError(e => error?.Equals(e) ?? false, (E _) => Fail); /// /// Catch an error if the error matches the argument provided /// public static CatchM @catch(Func predicate, Func> Fail) where M : Fallible => matchError(predicate, Fail); /// /// Catch an error if the error matches the argument provided /// public static CatchM @catch(Func predicate, K Fail) where M : Fallible => matchError(predicate, _ => Fail); /// /// Catch all errors and return Fail /// public static CatchM @catch(Func> Fail) where M : Fallible => matchError(static _ => true, Fail); } ================================================ FILE: LanguageExt.Core/Traits/Fallible/Fallible.Prelude.Catch.cs ================================================ using System; using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt; public static partial class Prelude { //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Error specific // /// /// Catch all `Error` errors of subtype `E` and return Fail /// public static CatchM catchOf(Func> Fail) where E : Error where M : Fallible, Monad => matchError(e => e is E || e.IsType(), es => es is E e ? Fail(e) : es.Filter().ForAllM(e => Fail((E)e))); /// /// Catch all `Error` errors of subtype `E` and return Fail /// public static CatchM catchOfFold(Func> Fail) where E : Error where M : Fallible, MonoidK => matchError(e => e is E || e.IsType(), es => es is E e ? Fail(e) : es.Filter().FoldM(e => Fail((E)e))); /// /// Catch an error if the error matches the argument provided /// public static CatchM @catch(Error error, Func> Fail) where M : Fallible => matchError(error.Is, Fail); /// /// Catch an error if the error matches the argument provided /// public static CatchM @catch(Error error, K Fail) where M : Fallible => matchError(error.Is, (Error _) => Fail); /// /// Catch an error if the error matches the argument provided /// public static CatchM @catch(int errorCode, Func> Fail) where M : Fallible => matchError(e => e.Code == errorCode || e.HasCode(errorCode), Fail); /// /// Catch an error if the error matches the argument provided /// public static CatchM @catch(int errorCode, K Fail) where M : Fallible => matchError(e => e.Code == errorCode || e.HasCode(errorCode), (Error _) => Fail); /// /// Catch an error if the error matches the argument provided /// public static CatchM @catch(Func predicate, Func> Fail) where M : Fallible => matchError(predicate, Fail); /// /// Catch an error if the error matches the argument provided /// public static CatchM @catch(Func predicate, K Fail) where M : Fallible => matchError(predicate, _ => Fail); /// /// Catch all errors and return Fail /// public static CatchM @catch(Func> Fail) where M : Fallible => matchError(static _ => true, Fail); /// /// Catch all errors and return Fail /// public static CatchM @catch(K Fail) where M : Fallible => matchError(static _ => true, (Error _) => Fail); /// /// Catch all `Expected` errors and return Fail /// public static CatchM expected(Func> Fail) where M : Fallible, Monad => catchOf(Fail); /// /// Catch all `Expected` errors of subtype `E` and return Fail /// public static CatchM expectedOf(Func> Fail) where E : Expected where M : Fallible, Monad => catchOf(Fail); /// /// Catch all `Exceptional` errors and return Fail /// public static CatchM exceptional(Func> Fail) where M : Fallible, Monad => catchOf(Fail); /// /// Catch all errors and return Fail /// public static CatchM exceptionalOf(Func> Fail) where E : Exceptional where M : Fallible, Monad => catchOf(Fail); /// /// Catch all `Expected` errors and return Fail /// public static CatchM expectedFold(Func> Fail) where M : Fallible, MonoidK => catchOfFold(Fail); /// /// Catch all `Expected` errors of subtype `E` and return Fail /// public static CatchM expectedOfFold(Func> Fail) where E : Expected where M : Fallible, MonoidK => catchOfFold(Fail); /// /// Catch all `Exceptional` errors and return Fail /// public static CatchM exceptionalFold(Func> Fail) where M : Fallible, MonoidK => catchOfFold(Fail); /// /// Catch all errors and return Fail /// public static CatchM exceptionalOfFold(Func> Fail) where E : Exceptional where M : Fallible, MonoidK => catchOfFold(Fail); } ================================================ FILE: LanguageExt.Core/Traits/Fallible/Fallible.Prelude.Fails.E.cs ================================================ using System.Collections.Generic; using LanguageExt.Common; namespace LanguageExt.Traits; public static partial class FallibleExtensions { /// /// Partitions a foldable of effects into successes and failures, /// and returns only the failures. /// /// Foldable type /// Fallible monadic type /// Bound value type /// Foldable of fallible monadic values /// A collection of `E` values public static K> fails( K> fma) where M : Monad, Fallible where F : Foldable => fma.Fold(M.Pure(Seq.empty()), ma => ms => ms.Bind( s => ma.Bind(_ => M.Pure(s)) .Catch((E e) => M.Pure(s.Add(e))))); /// /// Partitions a collection of effects into successes and failures, /// and returns only the failures. /// /// Fallible monadic type /// Bound value type /// Collection of fallible monadic values /// A collection of `E` values public static K> fails( Seq> fma) where M : Monad, Fallible => fma.Kind().Fails(); /// /// Partitions a collection of effects into successes and failures, /// and returns only the failures. /// /// Fallible monadic type /// Bound value type /// Collection of fallible monadic values /// A collection of `E` values public static K> fails( Iterable> fma) where M : Monad, Fallible => fma.Kind().Fails(); /// /// Partitions a collection of effects into successes and failures, /// and returns only the failures. /// /// Fallible monadic type /// Bound value type /// Collection of fallible monadic values /// A collection of `E` values public static K> fails( Lst> fma) where M : Monad, Fallible => fma.Kind().Fails(); /// /// Partitions a collection of effects into successes and failures, /// and returns only the failures. /// /// Fallible monadic type /// Bound value type /// Collection of fallible monadic values /// A collection of `E` values public static K> fails( IEnumerable> fma) where M : Monad, Fallible => Iterable.createRange(fma).Fails(); /// /// Partitions a collection of effects into successes and failures, /// and returns only the failures. /// /// Fallible monadic type /// Bound value type /// Collection of fallible monadic values /// A collection of `E` values public static K> fails( HashSet> fma) where M : Monad, Fallible => fma.Kind().Fails(); /// /// Partitions a collection of effects into successes and failures, /// and returns only the failures. /// /// Fallible monadic type /// Bound value type /// Collection of fallible monadic values /// A collection of `E` values public static K> fails( Set> fma) where M : Monad, Fallible => fma.Kind().Fails(); } ================================================ FILE: LanguageExt.Core/Traits/Fallible/Fallible.Prelude.Fails.cs ================================================ using System.Collections.Generic; using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt; public static partial class Prelude { /// /// Partitions a foldable of effects into successes and failures, /// and returns only the failures. /// /// Foldable type /// Fallible monadic type /// Bound value type /// Foldable of fallible monadic values /// A collection of `Error` values public static K> fails( K> fma) where M : Monad, Fallible where F : Foldable => fma.Fold(M.Pure(LanguageExt.Seq.empty()), ma => ms => ms.Bind( s => ma.Bind(_ => M.Pure(s)) .Catch(e => M.Pure(s.Add(e))))); /// /// Partitions a collection of effects into successes and failures, /// and returns only the failures. /// /// Fallible monadic type /// Bound value type /// Collection of fallible monadic values /// A collection of `Error` values public static K> fails( Seq> fma) where M : Monad, Fallible => fma.Kind().Fails(); /// /// Partitions a collection of effects into successes and failures, /// and returns only the failures. /// /// Fallible monadic type /// Bound value type /// Collection of fallible monadic values /// A collection of `Error` values public static K> fails( Iterable> fma) where M : Monad, Fallible => fma.Kind().Fails(); /// /// Partitions a collection of effects into successes and failures, /// and returns only the failures. /// /// Fallible monadic type /// Bound value type /// Collection of fallible monadic values /// A collection of `Error` values public static K> fails( Lst> fma) where M : Monad, Fallible => fma.Kind().Fails(); /// /// Partitions a collection of effects into successes and failures, /// and returns only the failures. /// /// Fallible monadic type /// Bound value type /// Collection of fallible monadic values /// A collection of `Error` values public static K> fails( IEnumerable> fma) where M : Monad, Fallible => LanguageExt.Iterable.createRange(fma).Fails(); /// /// Partitions a collection of effects into successes and failures, /// and returns only the failures. /// /// Fallible monadic type /// Bound value type /// Collection of fallible monadic values /// A collection of `Error` values public static K> fails( HashSet> fma) where M : Monad, Fallible => fma.Kind().Fails(); /// /// Partitions a collection of effects into successes and failures, /// and returns only the failures. /// /// Fallible monadic type /// Bound value type /// Collection of fallible monadic values /// A collection of `Error` values public static K> fails( Set> fma) where M : Monad, Fallible => fma.Kind().Fails(); } ================================================ FILE: LanguageExt.Core/Traits/Fallible/Fallible.Prelude.Partition.E.cs ================================================ using System.Collections.Generic; using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt; public static partial class Prelude { /// /// Partitions a foldable of effects into two lists. /// All the `Fail` elements are extracted, in order, to the first /// component of the output. Similarly, the `Succ` elements are extracted /// to the second component of the output. /// /// Error type /// Foldable type /// Fallible monadic type /// Bound value type /// Foldable of fallible monadic values /// A tuple containing an `Error` sequence and a `Succ` sequence public static K Fails, Seq Succs)> partitionFallible( K> fma) where M : Monad, Fallible where F : Foldable => fma.Fold(M.Pure((Fails: LanguageExt.Seq.empty(), Succs: LanguageExt.Seq.empty())), ma => ms => from s in ms from r in ma.Map(a => (s.Fails, s.Succs.Add(a))) .Catch((E e) => M.Pure((s.Fails.Add(e), s.Succs))) select r); /// /// Partitions a collection of effects into two lists. /// All the `Fail` elements are extracted, in order, to the first /// component of the output. Similarly, the `Succ` elements are extracted /// to the second component of the output. /// /// Error type /// Fallible monadic type /// Bound value type /// Collection of fallible monadic values /// A tuple containing an `Error` sequence and a `Succ` sequence public static K Fails, Seq Succs)> partitionFallible( Seq> fma) where M : Monad, Fallible => fma.Kind().PartitionFallible(); /// /// Partitions a collection of effects into two lists. /// All the `Fail` elements are extracted, in order, to the first /// component of the output. Similarly, the `Succ` elements are extracted /// to the second component of the output. /// /// Error type /// Fallible monadic type /// Bound value type /// Collection of fallible monadic values /// A tuple containing an `Error` sequence and a `Succ` sequence public static K Fails, Seq Succs)> partitionFallible( Iterable> fma) where M : Monad, Fallible => fma.Kind().PartitionFallible(); /// /// Partitions a collection of effects into two lists. /// All the `Fail` elements are extracted, in order, to the first /// component of the output. Similarly, the `Succ` elements are extracted /// to the second component of the output. /// /// Error type /// Fallible monadic type /// Bound value type /// Collection of fallible monadic values /// A tuple containing an `Error` sequence and a `Succ` sequence public static K Fails, Seq Succs)> partitionFallible( Lst> fma) where M : Monad, Fallible => fma.Kind().PartitionFallible(); /// /// Partitions a collection of effects into two lists. /// All the `Fail` elements are extracted, in order, to the first /// component of the output. Similarly, the `Succ` elements are extracted /// to the second component of the output. /// /// Error type /// Fallible monadic type /// Bound value type /// Collection of fallible monadic values /// A tuple containing an `Error` sequence and a `Succ` sequence public static K Fails, Seq Succs)> partitionFallible( IEnumerable> fma) where M : Monad, Fallible => LanguageExt.Iterable.createRange(fma).PartitionFallible(); /// /// Partitions a collection of effects into two lists. /// All the `Fail` elements are extracted, in order, to the first /// component of the output. Similarly, the `Succ` elements are extracted /// to the second component of the output. /// /// Error type /// Fallible monadic type /// Bound value type /// Collection of fallible monadic values /// A tuple containing an `Error` sequence and a `Succ` sequence public static K Fails, Seq Succs)> partitionFallible( HashSet> fma) where M : Monad, Fallible => fma.Kind().PartitionFallible(); /// /// Partitions a collection of effects into two lists. /// All the `Fail` elements are extracted, in order, to the first /// component of the output. Similarly, the `Succ` elements are extracted /// to the second component of the output. /// /// Error type /// Fallible monadic type /// Bound value type /// Collection of fallible monadic values /// A tuple containing an `Error` sequence and a `Succ` sequence public static K Fails, Seq Succs)> partitionFallible( Set> fma) where M : Monad, Fallible => fma.Kind().PartitionFallible(); } ================================================ FILE: LanguageExt.Core/Traits/Fallible/Fallible.Prelude.Partition.cs ================================================ using System.Collections.Generic; using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt; public static partial class Prelude { /// /// Partitions a foldable of effects into two lists. /// All the `Fail` elements are extracted, in order, to the first /// component of the output. Similarly, the `Succ` elements are extracted /// to the second component of the output. /// /// Foldable type /// Fallible monadic type /// Bound value type /// Foldable of fallible monadic values /// A tuple containing an `Error` sequence and a `Succ` sequence public static K Fails, Seq Succs)> partitionFallible( K> fma) where M : Monad, Fallible where F : Foldable => fma.Fold(M.Pure((Fails: LanguageExt.Seq.empty(), Succs: LanguageExt.Seq.empty())), ma => ms => from s in ms from r in ma.Map(a => (s.Fails, s.Succs.Add(a))) .Catch(e => M.Pure((s.Fails.Add(e), s.Succs))) select r); /// /// Partitions a collection of effects into two lists. /// All the `Fail` elements are extracted, in order, to the first /// component of the output. Similarly, the `Succ` elements are extracted /// to the second component of the output. /// /// Fallible monadic type /// Bound value type /// Collection of fallible monadic values /// A tuple containing an `Error` sequence and a `Succ` sequence public static K Fails, Seq Succs)> partitionFallible( Seq> fma) where M : Monad, Fallible => fma.Kind().PartitionFallible(); /// /// Partitions a collection of effects into two lists. /// All the `Fail` elements are extracted, in order, to the first /// component of the output. Similarly, the `Succ` elements are extracted /// to the second component of the output. /// /// Fallible monadic type /// Bound value type /// Collection of fallible monadic values /// A tuple containing an `Error` sequence and a `Succ` sequence public static K Fails, Seq Succs)> partitionFallible( Iterable> fma) where M : Monad, Fallible => fma.Kind().PartitionFallible(); /// /// Partitions a collection of effects into two lists. /// All the `Fail` elements are extracted, in order, to the first /// component of the output. Similarly, the `Succ` elements are extracted /// to the second component of the output. /// /// Fallible monadic type /// Bound value type /// Collection of fallible monadic values /// A tuple containing an `Error` sequence and a `Succ` sequence public static K Fails, Seq Succs)> partitionFallible( Lst> fma) where M : Monad, Fallible => fma.Kind().PartitionFallible(); /// /// Partitions a collection of effects into two lists. /// All the `Fail` elements are extracted, in order, to the first /// component of the output. Similarly, the `Succ` elements are extracted /// to the second component of the output. /// /// Fallible monadic type /// Bound value type /// Collection of fallible monadic values /// A tuple containing an `Error` sequence and a `Succ` sequence public static K Fails, Seq Succs)> partitionFallible( IEnumerable> fma) where M : Monad, Fallible => LanguageExt.Iterable.createRange(fma).PartitionFallible(); /// /// Partitions a collection of effects into two lists. /// All the `Fail` elements are extracted, in order, to the first /// component of the output. Similarly, the `Succ` elements are extracted /// to the second component of the output. /// /// Fallible monadic type /// Bound value type /// Collection of fallible monadic values /// A tuple containing an `Error` sequence and a `Succ` sequence public static K Fails, Seq Succs)> partitionFallible( HashSet> fma) where M : Monad, Fallible => fma.Kind().PartitionFallible(); /// /// Partitions a collection of effects into two lists. /// All the `Fail` elements are extracted, in order, to the first /// component of the output. Similarly, the `Succ` elements are extracted /// to the second component of the output. /// /// Fallible monadic type /// Bound value type /// Collection of fallible monadic values /// A tuple containing an `Error` sequence and a `Succ` sequence public static K Fails, Seq Succs)> partitionFallible( Set> fma) where M : Monad, Fallible => fma.Kind().PartitionFallible(); } ================================================ FILE: LanguageExt.Core/Traits/Fallible/Fallible.Prelude.Succs.E.cs ================================================ using System.Collections.Generic; using LanguageExt.Common; namespace LanguageExt.Traits; public static partial class FallibleExtensionsE { /// /// Partitions a foldable of effects into successes and failures, /// and returns only the failures. /// /// Foldable type /// Fallible monadic type /// Bound value type /// Foldable of fallible monadic values /// A collection of `Error` values public static K> succs( K> fma) where M : Monad, Fallible where F : Foldable => fma.Fold(M.Pure(Seq.empty()), ma => ms => ms.Bind( s => ma.Bind(a => M.Pure(s.Add(a))) .Catch((E _) => M.Pure(s)))); /// /// Partitions a collection of effects into successes and failures, /// and returns only the failures. /// /// Fallible monadic type /// Bound value type /// Collection of fallible monadic values /// A collection of `Error` values public static K> succs( Seq> fma) where M : Monad, Fallible => fma.Kind().Succs(); /// /// Partitions a collection of effects into successes and failures, /// and returns only the failures. /// /// Fallible monadic type /// Bound value type /// Collection of fallible monadic values /// A collection of `Error` values public static K> succs( Iterable> fma) where M : Monad, Fallible => fma.Kind().Succs(); /// /// Partitions a collection of effects into successes and failures, /// and returns only the failures. /// /// Fallible monadic type /// Bound value type /// Collection of fallible monadic values /// A collection of `Error` values public static K> succs( Lst> fma) where M : Monad, Fallible => fma.Kind().Succs(); /// /// Partitions a collection of effects into successes and failures, /// and returns only the failures. /// /// Fallible monadic type /// Bound value type /// Collection of fallible monadic values /// A collection of `Error` values public static K> succs( IEnumerable> fma) where M : Monad, Fallible => Iterable.createRange(fma).Succs(); /// /// Partitions a collection of effects into successes and failures, /// and returns only the failures. /// /// Fallible monadic type /// Bound value type /// Collection of fallible monadic values /// A collection of `Error` values public static K> succs( HashSet> fma) where M : Monad, Fallible => fma.Kind().Succs(); /// /// Partitions a collection of effects into successes and failures, /// and returns only the failures. /// /// Fallible monadic type /// Bound value type /// Collection of fallible monadic values /// A collection of `Error` values public static K> succs( Set> fma) where M : Monad, Fallible => fma.Kind().Succs(); } ================================================ FILE: LanguageExt.Core/Traits/Fallible/Fallible.Prelude.Succs.cs ================================================ using System.Collections.Generic; using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt; public static partial class Prelude { /// /// Partitions a foldable of effects into successes and failures, /// and returns only the failures. /// /// Foldable type /// Fallible monadic type /// Bound value type /// Foldable of fallible monadic values /// A collection of `Error` values public static K> succs( K> fma) where M : Monad, Fallible where F : Foldable => fma.Fold(M.Pure(LanguageExt.Seq.empty()), ma => ms => ms.Bind( s => ma.Bind(a => M.Pure(s.Add(a))) .Catch(_ => M.Pure(s)))); /// /// Partitions a collection of effects into successes and failures, /// and returns only the failures. /// /// Fallible monadic type /// Bound value type /// Collection of fallible monadic values /// A collection of `Error` values public static K> succs( Seq> fma) where M : Monad, Fallible => fma.Kind().Succs(); /// /// Partitions a collection of effects into successes and failures, /// and returns only the failures. /// /// Fallible monadic type /// Bound value type /// Collection of fallible monadic values /// A collection of `Error` values public static K> succs( Iterable> fma) where M : Monad, Fallible => fma.Kind().Succs(); /// /// Partitions a collection of effects into successes and failures, /// and returns only the failures. /// /// Fallible monadic type /// Bound value type /// Collection of fallible monadic values /// A collection of `Error` values public static K> succs( Lst> fma) where M : Monad, Fallible => fma.Kind().Succs(); /// /// Partitions a collection of effects into successes and failures, /// and returns only the failures. /// /// Fallible monadic type /// Bound value type /// Collection of fallible monadic values /// A collection of `Error` values public static K> succs( IEnumerable> fma) where M : Monad, Fallible => LanguageExt.Iterable.createRange(fma).Succs(); /// /// Partitions a collection of effects into successes and failures, /// and returns only the failures. /// /// Fallible monadic type /// Bound value type /// Collection of fallible monadic values /// A collection of `Error` values public static K> succs( HashSet> fma) where M : Monad, Fallible => fma.Kind().Succs(); /// /// Partitions a collection of effects into successes and failures, /// and returns only the failures. /// /// Fallible monadic type /// Bound value type /// Collection of fallible monadic values /// A collection of `Error` values public static K> succs( Set> fma) where M : Monad, Fallible => fma.Kind().Succs(); } ================================================ FILE: LanguageExt.Core/Traits/Fallible/Fallible.Prelude.cs ================================================ using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt; /// /// Module for higher-kinded structures that have a failure state `E` /// /// Higher-kinded structure /// Failure type public static partial class Prelude { /// /// Raise a failure state in the `Fallible` structure `F` /// /// Error to raise /// Fallible trait /// Error type /// Bound value type /// public static K fail(E error) where F : Fallible => F.Fail(error); /// /// Raise a failure state in the `Fallible` structure `F` /// /// Error to raise /// Fallible trait /// Bound value type /// public static K error(Error error) where F : Fallible => F.Fail(error); /// /// Raise a failure state in the `Fallible` structure `F` /// /// Error to raise /// Fallible trait /// Error type /// Bound value type /// public static K fail(E error) where F : Fallible => F.Fail(error); /// /// Raise a failure state in the `Fallible` structure `F` /// /// Error to raise /// Fallible trait /// Error type /// Bound value type /// public static K error(Error error) where F : Fallible => F.Fail(error); } ================================================ FILE: LanguageExt.Core/Traits/Fallible/Fallible.Trait.cs ================================================ using System; using LanguageExt.Common; namespace LanguageExt.Traits; /// /// Trait for higher-kinded structures that have a failure state `E` /// /// Failure type /// Higher-kinded structure public interface Fallible { //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Abstract members // /// /// Raise a failure state in the `Fallible` structure `F` /// /// Error value /// Bound value type /// public static abstract K Fail(E error); /// /// Run the `Fallible` structure. If in a failed state, test the failure value /// against the predicate. If it returns `true`, run the `Fail` function with /// the failure value. /// /// `Fallible` structure /// Predicate to test any failure values /// Handler when in failed state /// Bound value type /// Either `fa` or the result of `Fail` if `fa` is in a failed state and the /// predicate returns true for the failure value public static abstract K Catch( K fa, Func Predicate, Func> Fail); } /// /// Trait for higher-kinded structures that have a failure state `Error` /// /// Higher-kinded structure public interface Fallible : Fallible; ================================================ FILE: LanguageExt.Core/Traits/Final/Final.Extensions.cs ================================================ namespace LanguageExt.Traits; public static class FinalExtensions { /// /// Run a `finally` operation after the `ma` operation regardless of whether `ma` succeeds or not. /// /// Primary operation /// Finally operation /// Result of primary operation public static K Finally(this K ma, K @finally) where F : Final => F.Finally(ma, @finally); } ================================================ FILE: LanguageExt.Core/Traits/Final/Final.Module.cs ================================================ namespace LanguageExt.Traits; public static class Final { /// /// Create a `finally` operation that can be used as the right-hand side of a `|` operator to /// cause a final operation to be run regardless of whether the primary operation succeeds or not. /// /// Finally operation /// Result of primary operation public static Finally final(K @finally) where F : Final => new(@finally); } ================================================ FILE: LanguageExt.Core/Traits/Final/Final.Operators.cs ================================================ using LanguageExt.Traits; namespace LanguageExt; public static partial class FinalExtensions { extension(K _) where F : Final { /// /// Run a `finally` operation after the main operation regardless of whether it succeeds or not. /// /// Primary operation /// Finally operation /// Result of primary operation public static K operator |(K lhs, Finally rhs) => lhs.Finally(rhs.Operation); } } ================================================ FILE: LanguageExt.Core/Traits/Final/Final.Prelude.cs ================================================ using LanguageExt.Traits; namespace LanguageExt; public static partial class Prelude { /// /// Create a `finally` operation that can be used as the right-hand side of a `|` operator to /// cause a final operation to be run regardless of whether the primary operation succeeds or not. /// /// Finally operation /// Result of primary operation public static Finally final(K @finally) where F : Final => new (@finally); } ================================================ FILE: LanguageExt.Core/Traits/Final/Final.Trait.cs ================================================ namespace LanguageExt.Traits; /// /// Mimics `finally` in a `try/finally` operation /// public interface Final where F : Final { /// /// Run a `finally` operation after the `fa` operation regardless of whether `fa` succeeds or not. /// /// Primary operation /// Finally operation /// Result of primary operation public static abstract K Finally(K fa, K @finally); } ================================================ FILE: LanguageExt.Core/Traits/Final/Finally.cs ================================================ using LanguageExt.Traits; namespace LanguageExt; /// /// Create a `finally` operation that can be used as the right-hand side of a `|` operator to /// cause a final operation to be run regardless of whether the primary operation succeeds or not. /// /// /// public readonly record struct Finally(K Operation) where F : Final; ================================================ FILE: LanguageExt.Core/Traits/Floating/Floating.Prelude.cs ================================================ #nullable enable using LanguageExt.Traits; using System.Diagnostics.Contracts; namespace LanguageExt; public static partial class Trait { /// /// Ratio constructor /// /// Value type /// Numerator /// Denominator /// Ratio struct [Pure] public static Ratio Ratio(A num, A den) => new (num, den); /// /// Returns an approximation of pi. /// /// A reasonable approximation of pi in this type [Pure] public static A pi() where FLOAT : Floating => FLOAT.Pi(); /// /// The exponential function. /// /// The value for which we are calculating the exponential /// The value of e^x [Pure] public static A exp(A x) where FLOAT : Floating => FLOAT.Exp(x); /// /// Calculates the square root of a value. /// /// The value for which we are calculating the square root. /// The value of sqrt(x). [Pure] public static A sqrt(A x) where FLOAT : Floating => FLOAT.Sqrt(x); /// /// Calculates the natural logarithm of a value. /// /// /// The value for which we are calculating the natural logarithm. /// /// The value of ln(x). [Pure] public static A log(A x) where FLOAT : Floating => FLOAT.Log(x); /// Raises x to the power y /// /// The base to be raised to y /// The exponent to which we are raising x /// The value of x^y. [Pure] public static A pow(A x, A y) where FLOAT : Floating => FLOAT.Pow(x, y); /// /// Calculates the logarithm of a value with respect to an arbitrary base. /// /// The base to use for the logarithm of x /// The value for which we are calculating the logarithm. /// The value of log b (x). [Pure] public static A logBase(A x, A y) where FLOAT : Floating => FLOAT.LogBase(x, y); /// /// Calculates the sine of an angle. /// /// An angle, in radians /// The value of sin(x) [Pure] public static A sin(A x) where FLOAT : Floating => FLOAT.Sin(x); /// /// Calculates the cosine of an angle. /// /// An angle, in radians /// The value of cos(x) [Pure] public static A cos(A x) where FLOAT : Floating => FLOAT.Cos(x); /// /// Calculates the tangent of an angle. /// /// An angle, in radians /// The value of tan(x) [Pure] public static A tan(A x) where FLOAT : Floating => FLOAT.Tan(x); /// /// Calculates an arcsine. /// /// The value for which an arcsine is to be calculated. /// The value of asin(x), in radians. [Pure] public static A asin(A x) where FLOAT : Floating => FLOAT.Asin(x); /// /// Calculates an arc-cosine. /// /// The value for which an arc-cosine is to be calculated /// The value of acos(x), in radians [Pure] public static A acos(A x) where FLOAT : Floating => FLOAT.Acos(x); /// /// Calculates an arc-tangent. /// /// The value for which an arc-tangent is to be calculated /// The value of atan(x), in radians [Pure] public static A atan(A x) where FLOAT : Floating => FLOAT.Atan(x); /// /// Calculates a hyperbolic sine. /// /// The value for which a hyperbolic sine is to be calculated /// The value of sinh(x) [Pure] public static A sinh(A x) where FLOAT : Floating => FLOAT.Sinh(x); /// /// Calculates a hyperbolic cosine. /// /// The value for which a hyperbolic cosine is to be calculated /// The value of cosh(x) [Pure] public static A cosh(A x) where FLOAT : Floating => FLOAT.Cosh(x); /// /// Calculates a hyperbolic tangent. /// /// /// The value for which a hyperbolic tangent is to be calculated. /// /// The value of tanh(x) [Pure] public static A tanh(A x) where FLOAT : Floating => FLOAT.Tanh(x); /// Calculates an area hyperbolic sine /// The value for which an area hyperbolic sine is to be calculated. /// /// The value of asinh(x). [Pure] public static A asinh(A x) where FLOAT : Floating => FLOAT.Asinh(x); /// /// Calculates an area hyperbolic cosine. /// /// The value for which an area hyperbolic cosine is to be calculated. /// /// The value of acosh(x). [Pure] public static A acosh(A x) where FLOAT : Floating => FLOAT.Acosh(x); /// /// Calculates an area hyperbolic tangent. /// /// The value for which an area hyperbolic tangent is to be calculated. /// /// The value of atanh(x) [Pure] public static A atanh(A x) where FLOAT : Floating => FLOAT.Atanh(x); } ================================================ FILE: LanguageExt.Core/Traits/Floating/Floating.cs ================================================ using System.Diagnostics.Contracts; using LanguageExt.Attributes; namespace LanguageExt.Traits; /// /// Floating point number trait /// /// The floating point value type [Trait("Float*")] public interface Floating : Fraction { /// /// Returns an approximation of pi. /// /// A reasonable approximation of pi in this type [Pure] public static abstract A Pi(); /// /// The exponential function. /// /// The value for which we are calculating the exponential /// The value of e^x [Pure] public static abstract A Exp(A x); /// /// Calculates the square root of a value. /// /// The value for which we are calculating the square root. /// The value of sqrt(x). [Pure] public static abstract A Sqrt(A x); /// /// Calculates the natural logarithm of a value. /// /// /// The value for which we are calculating the natural logarithm. /// /// The value of ln(x). [Pure] public static abstract A Log(A x); /// Raises x to the power y /// /// The base to be raised to y /// The exponent to which we are raising x /// The value of x^y. [Pure] public static abstract A Pow(A x, A y); /// /// Calculates the logarithm of a value with respect to an arbitrary base. /// /// The base to use for the logarithm of t /// The value for which we are calculating the logarithm. /// The value of log x (y). [Pure] public static abstract A LogBase(A x, A y); /// /// Calculates the sine of an angle. /// /// An angle, in radians /// The value of sin(x) [Pure] public static abstract A Sin(A x); /// /// Calculates the cosine of an angle. /// /// An angle, in radians /// The value of cos(x) [Pure] public static abstract A Cos(A x); /// /// Calculates the tangent of an angle. /// /// An angle, in radians /// The value of tan(x) [Pure] public static abstract A Tan(A x); /// /// Calculates an arcsine. /// /// The value for which an arcsine is to be calculated. /// The value of asin(x), in radians. [Pure] public static abstract A Asin(A x); /// /// Calculates an arc-cosine. /// /// The value for which an arc-cosine is to be calculated /// The value of acos(x), in radians [Pure] public static abstract A Acos(A x); /// /// Calculates an arc-tangent. /// /// The value for which an arc-tangent is to be calculated /// The value of atan(x), in radians [Pure] public static abstract A Atan(A x); /// /// Calculates a hyperbolic sine. /// /// The value for which a hyperbolic sine is to be calculated /// The value of sinh(x) [Pure] public static abstract A Sinh(A x); /// /// Calculates a hyperbolic cosine. /// /// The value for which a hyperbolic cosine is to be calculated /// The value of cosh(x) [Pure] public static abstract A Cosh(A x); /// /// Calculates a hyperbolic tangent. /// /// /// The value for which a hyperbolic tangent is to be calculated. /// /// The value of tanh(x) [Pure] public static abstract A Tanh(A x); /// Calculates an area hyperbolic sine /// The value for which an area hyperbolic sine is to be calculated. /// /// The value of asinh(x). [Pure] public static abstract A Asinh(A x); /// /// Calculates an area hyperbolic cosine. /// /// The value for which an area hyperbolic cosine is to be calculated. /// /// The value of acosh(x). [Pure] public static abstract A Acosh(A x); /// /// Calculates an area hyperbolic tangent. /// /// The value for which an area hyperbolic tangent is to be calculated. /// /// The value of atanh(x) [Pure] public static abstract A Atanh(A x); } ================================================ FILE: LanguageExt.Core/Traits/Foldable/Fold.Module.cs ================================================ using System; namespace LanguageExt; /// /// Fold continuation module /// public record Fold { /// /// Indicates the fold operation has completed /// /// State public static Fold Done(S state) => new Fold.Done(state); /// /// Indicates the fold operation should continue /// /// Current state /// Current value /// Continuation function, pass your updated state to this public static Fold Loop(S state, A value, Func> next) => new Fold.Loop(state, value, next); } ================================================ FILE: LanguageExt.Core/Traits/Foldable/Fold.cs ================================================ using System; namespace LanguageExt; /// /// A continuation of a fold operation /// /// /// Returned from `Foldable.FoldStep` and `Foldable.FoldStepBack`, it enables consumption of a foldable structure /// one element at a time. Which in turn means we can avoid recursion and stack overflows. /// /// Current state /// Value type /// State type public abstract record Fold(S State) { /// /// Indicates the fold operation has completed /// /// State public sealed record Done(S State) : Fold(State); /// /// Indicates the fold operation should continue /// /// Current state /// Current value /// Continuation function, pass your updated state to this public sealed record Loop(S State, A Value, Func> Next) : Fold(State); } ================================================ FILE: LanguageExt.Core/Traits/Foldable/Foldable.Extensions.cs ================================================ using System; using System.Numerics; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; public static partial class FoldableExtensions { /// Mapping operation /// Foldable /// Applicative /// Input bound value /// Mapping bound value extension(Func> f) where T : Foldable where F : Applicative { /// /// Fold the structure: `ta` and pass each element that it yields to `f`, resulting in an `F` applicative-value. /// The fold operator is applicative `Action`, which causes each applicative-value to be sequenced. /// /// Foldable structure /// public K ForM(K ta) => ta.Fold(pure(unit), x => f(x).Action); } /// Foldable structure /// Foldable /// Applicative /// Input bound value /// Mapping bound value extension(K ta) where T : Foldable where F : Applicative { /// /// Fold the structure: `ta` and pass each element that it yields to `f`, resulting in an `F` applicative-value. /// The fold operator is applicative `Action`, which causes each applicative-value to be sequenced. /// /// Mapping operation /// public K ForM(Func> f) => ta.Fold(pure(unit), x => f(x).Action); } /// Foldable structure /// Value type extension(K ta) where T : Foldable { /// /// Runs a single step of the folding operation. The return value indicates whether the folding /// operation should continue, and if so, what the next step should be. /// /// /// It is up to the consumer of this method to implement the actual state-aggregation (the folding) /// before passing it to the continuation function. /// /// Foldable structure /// Initial state value /// Value type /// State type /// A discriminated union that can be either `Done` or `Loop`. public Fold FoldStep(S initialState) => T.FoldStep(ta, initialState); /// /// Runs a single step of the folding operation. The return value indicates whether the folding /// operation should continue, and if so, what the next step should be. /// /// /// It is up to the consumer of this method to implement the actual state-aggregation (the folding) /// before passing it to the continuation function. /// /// Foldable structure /// Initial state value /// Value type /// State type /// A discriminated union that can be either `Done` or `Loop`. public Fold FoldStepBack(S initialState) => T.FoldStep(ta, initialState); /// /// Fold until the `Option` returns `None` /// /// Fold function /// Initial state for the fold /// State type /// Aggregated value public S FoldMaybe(S initialState, Func>> f) => T.FoldMaybe(f, initialState, ta); /// /// Fold until the `Option` returns `None` /// /// Fold function /// Initial state for the fold /// State type /// Aggregated value public S FoldMaybe( S initialState, Func> f) => T.FoldMaybe(s => a => f(s, a), initialState, ta); /// /// Fold until the `Option` returns `None` /// /// Fold function /// Initial state for the fold /// State type /// Aggregated value public S FoldBackMaybe( S initialState, Func>> f) => T.FoldBackMaybe(f, initialState, ta); /// /// Fold until the `Option` returns `None` /// /// Fold function /// Initial state for the fold /// State type /// Aggregated value public S FoldBackMaybe( S initialState, Func> f) => T.FoldBackMaybe(a => s => f(s, a), initialState, ta); /// /// Same behaviour as `Fold` but allows early exit of the operation once /// the predicate function becomes `false` for the state/value pair /// public S FoldWhile( S initialState, Func> f, Func<(S State, A Value), bool> predicate) => T.FoldWhile(f, predicate, initialState, ta); /// /// Same behaviour as `Fold` but allows early exit of the operation once /// the predicate function becomes `false` for the state/value pair /// public S FoldWhile( S initialState, Func f, Func<(S State, A Value), bool> predicate) => T.FoldWhile(a => s => f(s, a), predicate, initialState, ta); /// /// Same behaviour as `FoldBack` but allows early exit of the operation once /// the predicate function becomes `false` for the state/value pair /// public S FoldBackWhile( S initialState, Func> f, Func<(S State, A Value), bool> predicate) => T.FoldBackWhile(f, predicate, initialState, ta); /// /// Same behaviour as `FoldBack` but allows early exit of the operation once /// the predicate function becomes `false` for the state/value pair /// public S FoldBackWhile( S initialState, Func f, Func<(S State, A Value), bool> predicate) => T.FoldBackWhile(curry(f), predicate, initialState, ta); /// /// Same behaviour as `Fold` but the fold operation returns a monadic type and allows /// early exit of the operation once the predicate function becomes `false` for the /// state/value pair /// public K FoldWhileM( S initialState, Func>> f, Func<(S State, A Value), bool> predicate) where M : Monad => T.FoldWhileM(f, predicate, initialState, ta); /// /// Same behaviour as `Fold` but the fold operation returns a monadic type and allows /// early exit of the operation once the predicate function becomes `false` for the /// state/value pair /// public K FoldWhileM( S initialState, Func> f, Func<(S State, A Value), bool> predicate) where M : Monad => T.FoldWhileM(a => s => f(s, a), predicate, initialState, ta); /// /// Same behaviour as `FoldBack` but the fold operation returns a monadic type and allows /// early exit of the operation once the predicate function becomes `false` for the /// state/value pair /// public K FoldBackWhileM( S initialState, Func>> f, Func<(S State, A Value), bool> predicate) where M : Monad => T.FoldBackWhileM(f, predicate, initialState, ta); /// /// Same behaviour as `FoldBack` but the fold operation returns a monadic type and allows /// early exit of the operation once the predicate function becomes `false` for the /// state/value pair /// public K FoldBackWhileM( S initialState, Func> f, Func<(S State, A Value), bool> predicate) where M : Monad => T.FoldBackWhileM(curry(f), predicate, initialState, ta); /// /// Same behaviour as `Fold` but allows early exit of the operation once /// the predicate function becomes `false` for the state/value pair /// public S FoldUntil( S initialState, Func> f, Func<(S State, A Value), bool> predicate) => T.FoldUntil(f, predicate, initialState, ta); /// /// Same behaviour as `Fold` but allows early exit of the operation once /// the predicate function becomes `false` for the state/value pair /// public S FoldUntil( S initialState, Func f, Func<(S State, A Value), bool> predicate) => T.FoldUntil(a => s => f(s, a), predicate, initialState, ta); /// /// Same behaviour as `Fold` but the fold operation returns a monadic type and allows /// early exit of the operation once the predicate function becomes `false` for the /// state/value pair /// public K FoldUntilM( S initialState, Func>> f, Func<(S State, A Value), bool> predicate) where M : Monad => T.FoldUntilM(f, predicate, initialState, ta); /// /// Same behaviour as `Fold` but the fold operation returns a monadic type and allows /// early exit of the operation once the predicate function becomes `false` for the /// state/value pair /// public K FoldUntilM( S initialState, Func> f, Func<(S State, A Value), bool> predicate) where M : Monad => T.FoldUntilM(a => s => f(s, a), predicate, initialState, ta); /// /// Same behaviour as `FoldBack` but allows early exit of the operation once /// the predicate function becomes `false` for the state/value pair /// public S FoldBackUntil( S initialState, Func> f, Func<(S State, A Value), bool> predicate) => T.FoldBackUntil(f, predicate, initialState, ta); /// /// Same behaviour as `FoldBack` but allows early exit of the operation once /// the predicate function becomes `false` for the state/value pair /// public S FoldBackUntil( S initialState, Func f, Func<(S State, A Value), bool> predicate) => T.FoldBackUntil(curry(f), predicate, initialState, ta); /// /// Same behaviour as `FoldBack` but the fold operation returns a monadic type and allows /// early exit of the operation once the predicate function becomes `false` for the /// state/value pair /// public K FoldBackUntilM( S initialState, Func>> f, Func<(S State, A Value), bool> predicate) where M : Monad => T.FoldBackUntilM(f, predicate, initialState, ta); /// /// Same behaviour as `FoldBack` but the fold operation returns a monadic type and allows /// early exit of the operation once the predicate function becomes `false` for the /// state/value pair /// public K FoldBackUntilM( S initialState, Func> f, Func<(S State, A Value), bool> predicate) where M : Monad => T.FoldBackUntilM(curry(f), predicate, initialState, ta); /// /// Right-associative fold of a structure, lazy in the accumulator. /// /// In the case of lists, 'Fold', when applied to a binary operator, a /// starting value (typically the right-identity of the operator), and a /// list, reduces the list using the binary operator, from right to left. /// public S Fold(S initialState, Func> f) => T.Fold(f, initialState, ta); /// /// Right-associative fold of a structure, lazy in the accumulator. /// /// In the case of lists, 'Fold', when applied to a binary operator, a /// starting value (typically the right-identity of the operator), and a /// list, reduces the list using the binary operator, from right to left. /// public S Fold(S initialState, Func f) => T.Fold(a => s => f(s, a), initialState, ta); /// /// Right-associative fold of a structure, lazy in the accumulator. /// /// In the case of lists, 'Fold', when applied to a binary operator, a /// starting value (typically the right-identity of the operator), and a /// list, reduces the list using the binary operator, from right to left. /// public K FoldM(S initialState, Func>> f) where M : Monad => T.FoldM(f, initialState, ta); /// /// Right-associative fold of a structure, lazy in the accumulator. /// /// In the case of lists, 'Fold', when applied to a binary operator, a /// starting value (typically the right-identity of the operator), and a /// list, reduces the list using the binary operator, from right to left. /// public K FoldM(S initialState, Func> f) where M : Monad => T.FoldM(a => s => f(s, a), initialState, ta); /// /// Left-associative fold of a structure, lazy in the accumulator. This /// is rarely what you want, but can work well for structures with efficient /// right-to-left sequencing and an operator that is lazy in its left /// argument. /// /// In the case of lists, 'FoldLeft', when applied to a binary operator, a /// starting value (typically the left-identity of the operator), and a /// list, reduces the list using the binary operator, from left to right /// /// /// Note that to produce the outermost application of the operator the /// entire input list must be traversed. Like all left-associative folds, /// `FoldBack` will diverge if given an infinite list. /// public S FoldBack(S initialState, Func> f) => T.FoldBack(f, initialState, ta); /// /// Left-associative fold of a structure, lazy in the accumulator. This /// is rarely what you want, but can work well for structures with efficient /// right-to-left sequencing and an operator that is lazy in its left /// argument. /// /// In the case of lists, 'FoldLeft', when applied to a binary operator, a /// starting value (typically the left-identity of the operator), and a /// list, reduces the list using the binary operator, from left to right /// /// /// Note that to produce the outermost application of the operator the /// entire input list must be traversed. Like all left-associative folds, /// `FoldBack` will diverge if given an infinite list. /// public S FoldBack(S initialState, Func f) => T.FoldBack(curry(f), initialState, ta); /// /// Left-associative fold of a structure, lazy in the accumulator. This /// is rarely what you want, but can work well for structures with efficient /// right-to-left sequencing and an operator that is lazy in its left /// argument. /// /// In the case of lists, 'FoldLeft', when applied to a binary operator, a /// starting value (typically the left-identity of the operator), and a /// list, reduces the list using the binary operator, from left to right /// /// /// Note that to produce the outermost application of the operator the /// entire input list must be traversed. Like all left-associative folds, /// `FoldBack` will diverge if given an infinite list. /// public K FoldBackM( S initialState, Func>> f) where M : Monad => T.FoldBackM(f, initialState, ta); /// /// Left-associative fold of a structure, lazy in the accumulator. This /// is rarely what you want, but can work well for structures with efficient /// right-to-left sequencing and an operator that is lazy in its left /// argument. /// /// In the case of lists, 'FoldLeft', when applied to a binary operator, a /// starting value (typically the left-identity of the operator), and a /// list, reduces the list using the binary operator, from left to right /// /// /// Note that to produce the outermost application of the operator the /// entire input list must be traversed. Like all left-associative folds, /// `FoldBack` will diverge if given an infinite list. /// public K FoldBackM( S initialState, Func> f) where M : Monad => T.FoldBackM(curry(f), initialState, ta); } extension(K tm) where T : Foldable where A : Monoid { /// /// Given a structure with elements whose type is a `Monoid`, combine them /// via the monoid's `Append` operator. This fold is right-associative and /// lazy in the accumulator. When you need a strict left-associative fold, /// use `FoldMap` instead, with `identity` as the map. /// public A Fold() => T.Fold(tm); /// /// Given a structure with elements whose type is a `Monoid`, combine them /// via the monoid's `Append` operator. This fold is right-associative and /// lazy in the accumulator. When you need a strict left-associative fold, /// use `FoldMap` instead, with `identity` as the map. /// public A FoldWhile(Func<(A State, A Value), bool> predicate) => T.FoldWhile(predicate, tm); /// /// Given a structure with elements whose type is a `Monoid`, combine them /// via the monoid's `Append` operator. This fold is right-associative and /// lazy in the accumulator. When you need a strict left-associative fold, /// use `FoldMap` instead, with `identity` as the map. /// public A FoldUntil(Func<(A State, A Value), bool> predicate) => T.FoldUntil(predicate, tm); } extension(K ta) where T : Foldable { /// /// Map each element of the structure into a monoid, and combine the /// results with `Append`. This fold is right-associative and lazy in the /// accumulator. For strict left-associative folds consider `FoldMapBack` /// instead. /// public B FoldMap(Func f) where B : Monoid => T.FoldMap(f, ta); /// /// Map each element of the structure into a monoid, and combine the /// results with `Append`. This fold is right-associative and lazy in the /// accumulator. For strict left-associative folds consider `FoldMapBack` /// instead. /// public B FoldMapWhile(Func f, Func<(B State, A Value), bool> predicate) where B : Monoid => T.FoldMapWhile(f, predicate, ta); /// /// Map each element of the structure into a monoid, and combine the /// results with `Append`. This fold is right-associative and lazy in the /// accumulator. For strict left-associative folds consider `FoldMapBack` /// instead. /// public B FoldMapUntil(Func f, Func<(B State, A Value), bool> predicate) where B : Monoid => T.FoldMapUntil(f, predicate, ta); /// /// A left-associative variant of 'FoldMap' that is strict in the /// accumulator. Use this method for strict reduction when partial /// results are merged via `Append`. /// public B FoldMapBack(Func f) where B : Monoid => T.FoldMapBack(f, ta); /// /// A left-associative variant of 'FoldMap' that is strict in the /// accumulator. Use this method for strict reduction when partial /// results are merged via `Append`. /// public B FoldMapBackWhile(Func f, Func<(B State, A Value), bool> predicate) where B : Monoid => T.FoldMapWhileBack(f, predicate, ta); /// /// A left-associative variant of 'FoldMap' that is strict in the /// accumulator. Use this method for strict reduction when partial /// results are merged via `Append`. /// public B FoldMapBackUntil(Func f, Func<(B State, A Value), bool> predicate) where B : Monoid => T.FoldMapUntilBack(f, predicate, ta); /// /// List of elements of a structure, from left to right /// public Seq ToSeq() => T.ToSeq(ta); /// /// List of elements of a structure, from left to right /// public Lst ToLst() => T.ToLst(ta); /// /// List of elements of a structure, from left to right /// public Arr ToArr() => T.ToArr(ta); /// /// List of elements of a structure, from left to right /// public Iterable ToIterable() => T.ToIterable(ta); /// /// List of elements of a structure, from left to right /// public bool IsEmpty => T.IsEmpty(ta); /// /// Returns the size/length of a finite structure as an `int`. The /// default implementation just counts elements starting with the leftmost. /// /// Instances for structures that can compute the element count faster /// than via element-by-element counting, should provide a specialised /// implementation. /// public int Count => T.Count(ta); /// /// Does an element that fits the predicate occur in the structure? /// public bool Exists(Func predicate) => T.Exists(predicate, ta); /// /// Does the predicate hold for all elements in the structure? /// public bool ForAll(Func predicate) => T.ForAll(predicate, ta); } extension(K ta) where EqA : Eq where T : Foldable { /// /// Does the element exist in the structure? /// public bool Contains(A value) => T.Contains(value, ta); } extension(K ta) where T : Foldable { /// /// Does the element exist in the structure? /// public bool Contains(A value) => T.Contains(value, ta); /// /// Find the first element that match the predicate /// public Option Find(Func predicate) => T.Find(predicate, ta); /// /// Find the last element that match the predicate /// public Option FindBack(Func predicate) => T.FindBack(predicate, ta); /// /// Find the elements that match the predicate /// public Iterable FindAll(Func predicate) => T.FindAll(predicate, ta); /// /// Find the elements that match the predicate /// public Iterable FindAllBack(Func predicate) => T.FindAllBack(predicate, ta); } extension(K ta) where T : Foldable where A : IAdditionOperators, IAdditiveIdentity { /// /// Computes the sum of the numbers of a structure. /// public A Sum() => T.Sum(ta); } extension(K ta) where T : Foldable where A : IMultiplyOperators, IMultiplicativeIdentity { /// /// Computes the product of the numbers of a structure. /// public A Product() => T.Product(ta); } extension(K ta) where T : Foldable { /// /// Get the head item in the foldable or `None` /// public Option Head => T.Head(ta); /// /// Get the head item in the foldable or `None` /// public Option Last => T.Last(ta); /// /// Map each element of a structure to an 'Applicative' action, evaluate these /// actions from left to right, and ignore the results. For a version that /// doesn't ignore the results see `Traversable.traverse`. /// public K Iter(Func> f) where F : Monad => T.Iter(f, ta); /// /// Map each element of a structure to an action, evaluate these /// actions from left to right, and ignore the results. For a version that /// doesn't ignore the results see `Traversable.traverse`. /// public Unit Iter(Action f) => T.Iter(f, ta); /// /// Map each element of a structure to an action, evaluate these /// actions from left to right, and ignore the results. For a version that /// doesn't ignore the results see `Traversable.traverse`. /// public Unit Iter(Action f) => T.Iter(f, ta); } /// /// Find the minimum value in the structure /// public static Option Min(this K ta) where T : Foldable where OrdA : Ord => T.Min(ta); /// /// Find the minimum value in the structure /// public static Option Min(this K ta) where T : Foldable where A : IComparable => T.Min(ta); /// /// Find the maximum value in the structure /// public static Option Max(this K ta) where T : Foldable where OrdA : Ord => T.Max(ta); /// /// Find the maximum value in the structure /// public static Option Max(this K ta) where T : Foldable where A : IComparable => T.Max(ta); /// /// Find the minimum value in the structure /// public static A Min(this K ta, A initialMin) where T : Foldable where OrdA : Ord => T.Min(ta, initialMin); /// /// Find the minimum value in the structure /// public static A Min(this K ta, A initialMin) where T : Foldable where A : IComparable => T.Min(ta, initialMin); /// /// Find the maximum value in the structure /// public static A Max(this K ta, A initialMax) where T : Foldable where OrdA : Ord => T.Max(ta, initialMax); /// /// Find the maximum value in the structure /// public static A Max(this K ta, A initialMax) where T : Foldable where A : IComparable => T.Max(ta, initialMax); /// /// Find the average of all the values in the structure /// public static A Average(this K ta) where T : Foldable where A : INumber => T.Average(ta); /// /// Find the average of all the values in the structure /// public static B Average(this K ta, Func f) where T : Foldable where B : INumber => T.Average(f, ta); /// /// Find the element at the specified index or `None` if out of range /// public static Option At(this K ta, Index index) where T : Foldable => T.At(ta, index); /// /// Partition a foldable into two sequences based on a predicate /// /// Predicate function /// Foldable structure /// Bound value type /// Partitioned structure public static (Seq True, Seq False) Partition(this K ta, Func f) where T : Foldable => T.Partition(f, ta); } ================================================ FILE: LanguageExt.Core/Traits/Foldable/Foldable.ExtensionsT.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Numerics; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; public static partial class FoldableExtensions { /// /// Same behaviour as `Fold` but allows early exit of the operation once /// the predicate function becomes `false` for the state/value pair /// public static S FoldWhileT( this K> tua, S initialState, Func> f, Func<(S State, A Value), bool> predicate) where T : Foldable where U : Foldable => Foldable.fold(ua => s1 => Foldable.foldWhile(f, predicate, s1, ua), initialState, tua); /// /// Same behaviour as `Fold` but allows early exit of the operation once /// the predicate function becomes `false` for the state/value pair /// public static S FoldWhileT( this K> tua, S initialState, Func f, Func<(S State, A Value), bool> predicate) where T : Foldable where U : Foldable => Foldable.fold(ua => s1 => Foldable.foldWhile(f, predicate, s1, ua), initialState, tua); /// /// Same behaviour as `FoldBack` but allows early exit of the operation once /// the predicate function becomes `false` for the state/value pair /// public static S FoldBackWhileT( this K> tua, S initialState, Func> f, Func<(S State, A Value), bool> predicate) where T : Foldable where U : Foldable => Foldable.foldBack(s1 => ua => Foldable.foldBackWhile(f, predicate, s1, ua), initialState, tua); /// /// Same behaviour as `FoldBack` but allows early exit of the operation once /// the predicate function becomes `false` for the state/value pair /// public static S FoldBackWhileT( this K> tua, S initialState, Func f, Func<(S State, A Value), bool> predicate) where T : Foldable where U : Foldable => Foldable.foldBack(s1 => ua => Foldable.foldBackWhile(f, predicate, s1, ua), initialState, tua); /// /// Same behaviour as `Fold` but allows early exit of the operation once /// the predicate function becomes `false` for the state/value pair /// public static S FoldUntilT( this K> tua, S initialState, Func> f, Func<(S State, A Value), bool> predicate) where T : Foldable where U : Foldable => Foldable.fold(ua => s1 => Foldable.foldUntil(f, predicate, s1, ua), initialState, tua); /// /// Same behaviour as `Fold` but allows early exit of the operation once /// the predicate function becomes `false` for the state/value pair /// public static S FoldUntilT( this K> tua, S initialState, Func f, Func<(S State, A Value), bool> predicate) where T : Foldable where U : Foldable => Foldable.fold(ua => s1 => Foldable.foldUntil(f, predicate, s1, ua), initialState, tua); /// /// Same behaviour as `FoldBack` but allows early exit of the operation once /// the predicate function becomes `false` for the state/value pair /// public static S FoldBackUntilT( this K> tua, S initialState, Func> f, Func<(S State, A Value), bool> predicate) where T : Foldable where U : Foldable => Foldable.foldBack(s1 => ua => Foldable.foldBackUntil(f, predicate, s1, ua), initialState, tua); /// /// Same behaviour as `FoldBack` but allows early exit of the operation once /// the predicate function becomes `false` for the state/value pair /// public static S FoldBackUntilT( this K> tua, S initialState, Func f, Func<(S State, A Value), bool> predicate) where T : Foldable where U : Foldable => Foldable.foldBack(s1 => ua => Foldable.foldBackUntil(f, predicate, s1, ua), initialState, tua); /// /// Right-associative fold of a structure, lazy in the accumulator. /// /// In the case of lists, 'Fold', when applied to a binary operator, a /// starting value (typically the right-identity of the operator), and a /// list, reduces the list using the binary operator, from right to left. /// public static S FoldT( this K> tua, S initialState, Func> f) where T : Foldable where U : Foldable => Foldable.fold(ua => s1 => Foldable.fold(f, s1, ua), initialState, tua); /// /// Right-associative fold of a structure, lazy in the accumulator. /// /// In the case of lists, 'Fold', when applied to a binary operator, a /// starting value (typically the right-identity of the operator), and a /// list, reduces the list using the binary operator, from right to left. /// public static S FoldT( this K> tua, S initialState, Func f) where T : Foldable where U : Foldable => Foldable.fold(ua => s1 => Foldable.fold(f, s1, ua), initialState, tua); /// /// Left-associative fold of a structure, lazy in the accumulator. This /// is rarely what you want, but can work well for structures with efficient /// right-to-left sequencing and an operator that is lazy in its left /// argument. /// /// In the case of lists, 'FoldLeft', when applied to a binary operator, a /// starting value (typically the left-identity of the operator), and a /// list, reduces the list using the binary operator, from left to right /// /// /// Note that to produce the outermost application of the operator the /// entire input list must be traversed. Like all left-associative folds, /// `FoldBack' will diverge if given an infinite list. /// public static S FoldBackT( this K> tua, S initialState, Func> f) where T : Foldable where U : Foldable => Foldable.foldBack(s1 => ua => Foldable.foldBack(f, s1, ua), initialState, tua); /// /// Left-associative fold of a structure, lazy in the accumulator. This /// is rarely what you want, but can work well for structures with efficient /// right-to-left sequencing and an operator that is lazy in its left /// argument. /// /// In the case of lists, 'FoldLeft', when applied to a binary operator, a /// starting value (typically the left-identity of the operator), and a /// list, reduces the list using the binary operator, from left to right /// /// /// Note that to produce the outermost application of the operator the /// entire input list must be traversed. Like all left-associative folds, /// `FoldBack' will diverge if given an infinite list. /// public static S FoldBackT( this K> tua, S initialState, Func f) where T : Foldable where U : Foldable => Foldable.foldBack(s1 => ua => Foldable.foldBack(f, s1, ua), initialState, tua); /// /// Given a structure with elements whose type is a `Monoid`, combine them /// via the monoid's `Append` operator. This fold is right-associative and /// lazy in the accumulator. When you need a strict left-associative fold, /// use 'foldMap'' instead, with 'id' as the map. /// public static A FoldT(this K> tua) where T : Foldable where U : Foldable where A : Monoid => Foldable.fold(ua => s1 => Foldable.fold(a => s => s + a, s1, ua), A.Empty, tua); /// /// Given a structure with elements whose type is a `Monoid`, combine them /// via the monoid's `Append` operator. This fold is right-associative and /// lazy in the accumulator. When you need a strict left-associative fold, /// use 'foldMap'' instead, with 'id' as the map. /// public static A FoldWhileT(this K> tua, Func<(A State, A Value), bool> predicate) where T : Foldable where U : Foldable where A : Monoid => Foldable.fold(ua => s1 => Foldable.foldWhile(a => s => s + a, predicate, s1, ua), A.Empty, tua); /// /// Given a structure with elements whose type is a `Monoid`, combine them /// via the monoid's `Append` operator. This fold is right-associative and /// lazy in the accumulator. When you need a strict left-associative fold, /// use 'foldMap'' instead, with 'id' as the map. /// public static A FoldUntilT(this K> tua, Func<(A State, A Value), bool> predicate) where T : Foldable where U : Foldable where A : Monoid => Foldable.fold(ua => s1 => Foldable.foldUntil(a => s => s + a, predicate, s1, ua), A.Empty, tua); /// /// Map each element of the structure into a monoid, and combine the /// results with `Append`. This fold is right-associative and lazy in the /// accumulator. For strict left-associative folds consider `FoldMapBack` /// instead. /// public static B FoldMapT(this K> tua, Func f) where T : Foldable where U : Foldable where B : Monoid => Foldable.fold(ua => s1 => Foldable.fold(a => s => s + f(a), s1, ua), B.Empty, tua); /// /// Map each element of the structure into a monoid, and combine the /// results with `Append`. This fold is right-associative and lazy in the /// accumulator. For strict left-associative folds consider `FoldMapBack` /// instead. /// public static B FoldMapWhileT(this K> tua, Func f, Func<(B State, A Value), bool> predicate) where T : Foldable where U : Foldable where B : Monoid => Foldable.fold(ua => s1 => Foldable.foldWhile(a => s => s + f(a), predicate, s1, ua), B.Empty, tua); /// /// Map each element of the structure into a monoid, and combine the /// results with `Append`. This fold is right-associative and lazy in the /// accumulator. For strict left-associative folds consider `FoldMapBack` /// instead. /// public static B FoldMapUntilT(this K> tua, Func f, Func<(B State, A Value), bool> predicate) where T : Foldable where U : Foldable where B : Monoid => Foldable.fold(ua => s1 => Foldable.foldUntil(a => s => s + f(a), predicate, s1, ua), B.Empty, tua); /// /// A left-associative variant of 'FoldMap' that is strict in the /// accumulator. Use this method for strict reduction when partial /// results are merged via `Append`. /// public static B FoldMapBackT(this K> tua, Func f) where T : Foldable where U : Foldable where B : Monoid => Foldable.foldBack(s1 => ua => Foldable.foldBack(s => a => s + f(a), s1, ua), B.Empty, tua); /// /// A left-associative variant of 'FoldMap' that is strict in the /// accumulator. Use this method for strict reduction when partial /// results are merged via `Append`. /// public static B FoldMapBackWhileT(this K> tua, Func f, Func<(B State, A Value), bool> predicate) where T : Foldable where U : Foldable where B : Monoid => Foldable.foldBack(s1 => ua => Foldable.foldBackWhile(s => a => s + f(a), predicate, s1, ua), B.Empty, tua); /// /// A left-associative variant of 'FoldMap' that is strict in the /// accumulator. Use this method for strict reduction when partial /// results are merged via `Append`. /// public static B FoldMapBackUntilT(this K> tua, Func f, Func<(B State, A Value), bool> predicate) where T : Foldable where U : Foldable where B : Monoid => Foldable.foldBack(s1 => ua => Foldable.foldBackUntil(s => a => s + f(a), predicate, s1, ua), B.Empty, tua); /// /// List of elements of a structure, from left to right /// public static Seq ToSeqT(this K> tua) where T : Foldable where U : Foldable => Foldable.fold(ua => s1 => Foldable.fold(a => s => s.Add(a), s1, ua), Seq.Empty, tua); /// /// List of elements of a structure, from left to right /// public static Lst ToLstT(this K> tua) where T : Foldable where U : Foldable => Foldable.fold(ua => s1 => Foldable.fold(a => s => s.Add(a), s1, ua), Lst.Empty, tua); /// /// List of elements of a structure, from left to right /// public static Arr ToArrT(this K> tua) where T : Foldable where U : Foldable { return new(Go().ToArray()); IEnumerable Go() { foreach(var ua in tua.ToIterable()) { foreach (var a in ua.ToIterable()) { yield return a; } } } } /// /// List of elements of a structure, from left to right /// public static Iterable ToEnumerableT(this K> tua) where T : Foldable where U : Foldable => Foldable.fold( ua => s1 => Foldable.fold( a => s => { s.Add(a); return s; }, s1, ua), new List(), tua).AsIterable(); /// /// List of elements of a structure, from left to right /// public static bool IsEmptyT(this K> tua) where T : Foldable where U : Foldable => Foldable.fold(ua => s => s && ua.IsEmpty, true, tua); /// /// Returns the size/length of a finite structure as an `int`. The /// default implementation just counts elements starting with the leftmost. /// /// Instances for structures that can compute the element count faster /// than via element-by-element counting, should provide a specialised /// implementation. /// public static int CountT(this K> tua) where T : Foldable where U : Foldable => Foldable.fold(ua => s => s + ua.Count, 0, tua); /// /// Does an element that fits the predicate occur in the structure? /// public static bool ExistsT(this K> tua, Func predicate) where T : Foldable where U : Foldable => Foldable.fold(ua => s => s || ua.Exists(predicate), false, tua); /// /// Does the predicate hold for all elements in the structure? /// public static bool ForAllT(this K> tua, Func predicate) where T : Foldable where U : Foldable => Foldable.fold(ua => s => s && ua.ForAll(predicate), true, tua); /// /// Does the element exist in the structure? /// public static bool ContainsT(this K> tua, A value) where EqA : Eq where T : Foldable where U : Foldable => Foldable.exists(ua => Foldable.contains(value, ua), tua); /// /// Does the element exist in the structure? /// public static bool ContainsT(this K> tua, A value) where T : Foldable where U : Foldable => Foldable.exists(ua => Foldable.contains(value, ua), tua); /// /// Find the first element that match the predicate /// public static Option FindT(this K> tua, Func predicate) where T : Foldable where U : Foldable => Foldable.foldWhile(ua => s1 => s1 || Foldable.find(predicate, ua), s => s.State.IsNone, Option.None, tua); /// /// Find the last element that match the predicate /// public static Option FindBackT(this K> tua, Func predicate) where T : Foldable where U : Foldable => Foldable.foldBackWhile(s1 => ua => s1 || Foldable.find(predicate, ua), s => s.State.IsNone, Option.None, tua); /// /// Find the the elements that match the predicate /// public static Iterable FindAllT(this K> tua, Func predicate) where T : Foldable where U : Foldable => Foldable.fold(ua => s1 => s1 + Foldable.findAll(predicate, ua), Iterable.Empty, tua); /// /// Find the the elements that match the predicate /// public static Iterable FindAllBackT(this K> tua, Func predicate) where T : Foldable where U : Foldable => Foldable.foldBack(s1 => ua => s1 + Foldable.findAllBack(predicate, ua), Iterable.Empty, tua); /// /// Computes the sum of the numbers of a structure. /// public static A SumT(this K> tua) where T : Foldable where U : Foldable where A : IAdditionOperators, IAdditiveIdentity => Foldable.fold(ua => s => s + Foldable.sum(ua), A.AdditiveIdentity, tua); /// /// Computes the product of the numbers of a structure. /// public static A ProductT(this K> tua) where T : Foldable where U : Foldable where A : IMultiplyOperators, IMultiplicativeIdentity => Foldable.fold(ua => s => s * Foldable.product(ua), A.MultiplicativeIdentity, tua); /// /// Get the head item in the foldable or `None` /// public static Option HeadT(this K> tua) where T : Foldable where U : Foldable => Foldable.foldWhile( ua => s => s || Foldable.head(ua), s => s.State.IsNone, Option.None, tua); /// /// Get the head item in the foldable or `None` /// public static Option LastT(this K> tua) where T : Foldable where U : Foldable => Foldable.foldBackWhile( s => ua => s || Foldable.last(ua), s => s.State.IsNone, Option.None, tua); /// /// Map each element of a structure to an 'Applicative' action, evaluate these /// actions from left to right, and ignore the results. For a version that /// doesn't ignore the results see `Traversable.traverse`. /// public static K IterT(this K> tua, Func> f) where T : Foldable where U : Foldable where F : Monad => Foldable.iter(ua => Foldable.iter(f, ua), tua); /// /// Map each element of a structure to an action, evaluate these /// actions from left to right, and ignore the results. For a version that /// doesn't ignore the results see `Traversable.traverse`. /// public static Unit IterT(this K> tua, Action f) where T : Foldable where U : Foldable => ignore(Foldable.fold(ua => ix1 => Foldable.fold(a => ix2 => { f(ix2, a); return ix2 + 1; }, ix1, ua), 0, tua)); /// /// Map each element of a structure to an action, evaluate these /// actions from left to right, and ignore the results. For a version that /// doesn't ignore the results see `Traversable.traverse`. /// public static Unit IterT(this K> tua, Action f) where T : Foldable where U : Foldable => Foldable.fold(ua => _ => Foldable.iter(f, ua), unit, tua); /// /// Find the minimum value in the structure /// public static Option MinT(this K> tua) where T : Foldable where U : Foldable where OrdA : Ord => Foldable.fold( ua => s => s switch { { IsSome: true, Value: A value } => Foldable.min(ua, value), _ => Foldable.min(ua) }, Option.None, tua); /// /// Find the minimum value in the structure /// public static Option MinT(this K> tua) where T : Foldable where U : Foldable where A : IComparable => Foldable.fold( ua => s => s switch { { IsSome: true, Value: A value } => Foldable.min(ua, value), _ => Foldable.min(ua) }, Option.None, tua); /// /// Find the maximum value in the structure /// public static Option MaxT(this K> tua) where T : Foldable where U : Foldable where OrdA : Ord => Foldable.fold( ua => s => s switch { { IsSome: true, Value: A value } => Foldable.max(ua, value), _ => Foldable.max(ua) }, Option.None, tua); /// /// Find the maximum value in the structure /// public static Option MaxT(this K> tua) where T : Foldable where U : Foldable where A : IComparable => Foldable.fold( ua => s => s switch { { IsSome: true, Value: A value } => Foldable.max(ua, value), _ => Foldable.max(ua) }, Option.None, tua); /// /// Find the minimum value in the structure /// public static A MinT(this K> tua, A initialMin) where T : Foldable where U : Foldable where OrdA : Ord => Foldable.fold(ua => s => Foldable.min(ua, s), initialMin, tua); /// /// Find the minimum value in the structure /// public static A MinT(this K> tua, A initialMin) where T : Foldable where U : Foldable where A : IComparable => Foldable.fold(ua => s => Foldable.min(ua, s), initialMin, tua); /// /// Find the maximum value in the structure /// public static A MaxT(this K> tua, A initialMax) where T : Foldable where U : Foldable where OrdA : Ord => Foldable.fold(ua => s => Foldable.max(ua, s), initialMax, tua); /// /// Find the maximum value in the structure /// public static A MaxT(this K> tua, A initialMax) where T : Foldable where U : Foldable where A : IComparable => Foldable.fold(ua => s => Foldable.max(ua, s), initialMax, tua); /// /// Find the average of all the values in the structure /// public static A AverageT(this K ta) where T : Foldable where A : INumber => T.Average(ta); /// /// Find the average of all the values in the structure /// public static B AverageT(this K ta, Func f) where T : Foldable where B : INumber => T.Average(f, ta); /// /// Find the element at the specified index or `None` if out of range /// public static Option AtT(this K ta, Index index) where T : Foldable => T.At(ta, index); } ================================================ FILE: LanguageExt.Core/Traits/Foldable/Foldable.Module.cs ================================================ using System; using System.Collections.Generic; using System.Numerics; using static LanguageExt.Prelude; namespace LanguageExt.Traits; public static class Foldable { /// /// Fold the structure: `ta` and pass each element that it yields to `f`, resulting in an `F` applicative-value. /// The fold operator is applicative `Action`, which causes each applicative-value to be sequenced. /// /// Foldable structure /// Mapping operation /// Foldable /// Applicative /// Input bound value /// Mapping bound value /// public static K forM(K ta, Func> f) where F : Applicative where T : Foldable => ta.Fold(pure(unit), x => f(x).Action); /// /// Same behaviour as `Fold` but allows early exit of the operation once /// the predicate function becomes `false` for the state/value pair /// public static S foldWhile( Func> f, Func<(S State, A Value), bool> predicate, S initialState, K ta) where T : Foldable => T.FoldWhile(f, predicate, initialState, ta); /// /// Same behaviour as `Fold` but allows early exit of the operation once /// the predicate function becomes `false` for the state/value pair /// public static S foldWhile( Func f, Func<(S State, A Value), bool> predicate, S initialState, K ta) where T : Foldable => T.FoldWhile(a => s => f(s, a), predicate, initialState, ta); /// /// Same behaviour as `FoldBack` but allows early exit of the operation once /// the predicate function becomes `false` for the state/value pair /// public static S foldBackWhile( Func> f, Func<(S State, A Value), bool> predicate, S initialState, K ta) where T : Foldable => T.FoldBackWhile(f, predicate, initialState, ta); /// /// Same behaviour as `FoldBack` but allows early exit of the operation once /// the predicate function becomes `false` for the state/value pair /// public static S foldBackWhile( Func f, Func<(S State, A Value), bool> predicate, S initialState, K ta) where T : Foldable => T.FoldBackWhile(curry(f), predicate, initialState, ta); /// /// Same behaviour as `Fold` but the fold operation returns a monadic type and allows /// early exit of the operation once the predicate function becomes `false` for the /// state/value pair /// public static K foldWhileM( Func>> f, Func<(S State, A Value), bool> predicate, S initialState, K ta) where T : Foldable where M : Monad => T.FoldWhileM(f, predicate, initialState, ta); /// /// Same behaviour as `Fold` but the fold operation returns a monadic type and allows /// early exit of the operation once the predicate function becomes `false` for the /// state/value pair /// public static K foldWhileM( Func> f, Func<(S State, A Value), bool> predicate, S initialState, K ta) where T : Foldable where M : Monad => T.FoldWhileM(a => s => f(s, a), predicate, initialState, ta); /// /// Same behaviour as `FoldBack` but the fold operation returns a monadic type and allows /// early exit of the operation once the predicate function becomes `false` for the /// state/value pair /// public static K foldBackWhileM( Func>> f, Func<(S State, A Value), bool> predicate, S initialState, K ta) where T : Foldable where M : Monad => T.FoldBackWhileM(f, predicate, initialState, ta); /// /// Same behaviour as `FoldBack` but the fold operation returns a monadic type and allows /// early exit of the operation once the predicate function becomes `false` for the /// state/value pair /// public static K foldBackWhileM( Func> f, Func<(S State, A Value), bool> predicate, S initialState, K ta) where T : Foldable where M : Monad => T.FoldBackWhileM(curry(f), predicate, initialState, ta); /// /// Same behaviour as `Fold` but allows early exit of the operation once /// the predicate function becomes `false` for the state/value pair /// public static S foldUntil( Func> f, Func<(S State, A Value), bool> predicate, S initialState, K ta) where T : Foldable => T.FoldUntil(f, predicate, initialState, ta); /// /// Same behaviour as `Fold` but allows early exit of the operation once /// the predicate function becomes `false` for the state/value pair /// public static S foldUntil( Func f, Func<(S State, A Value), bool> predicate, S initialState, K ta) where T : Foldable => T.FoldUntil(a => s => f(s, a), predicate, initialState, ta); /// /// Same behaviour as `Fold` but the fold operation returns a monadic type and allows /// early exit of the operation once the predicate function becomes `false` for the /// state/value pair /// public static K foldUntilM( Func>> f, Func<(S State, A Value), bool> predicate, S initialState, K ta) where M : Monad where T : Foldable => T.FoldUntilM(f, predicate, initialState, ta); /// /// Same behaviour as `Fold` but the fold operation returns a monadic type and allows /// early exit of the operation once the predicate function becomes `false` for the /// state/value pair /// public static K foldUntilM( Func> f, Func<(S State, A Value), bool> predicate, S initialState, K ta) where M : Monad where T : Foldable => T.FoldUntilM(a => s => f(s, a), predicate, initialState, ta); /// /// Same behaviour as `FoldBack` but allows early exit of the operation once /// the predicate function becomes `false` for the state/value pair /// public static S foldBackUntil( Func> f, Func<(S State, A Value), bool> predicate, S initialState, K ta) where T : Foldable => T.FoldBackUntil(f, predicate, initialState, ta); /// /// Same behaviour as `FoldBack` but allows early exit of the operation once /// the predicate function becomes `false` for the state/value pair /// public static S foldBackUntil( Func f, Func<(S State, A Value), bool> predicate, S initialState, K ta) where T : Foldable => T.FoldBackUntil(curry(f), predicate, initialState, ta); /// /// Same behaviour as `FoldBack` but the fold operation returns a monadic type and allows /// early exit of the operation once the predicate function becomes `false` for the /// state/value pair /// public static K foldBackUntilM( Func>> f, Func<(S State, A Value), bool> predicate, S initialState, K ta) where T : Foldable where M : Monad => T.FoldBackUntilM(f, predicate, initialState, ta); /// /// Same behaviour as `FoldBack` but the fold operation returns a monadic type and allows /// early exit of the operation once the predicate function becomes `false` for the /// state/value pair /// public static K foldBackUntilM( Func> f, Func<(S State, A Value), bool> predicate, S initialState, K ta) where T : Foldable where M : Monad => T.FoldBackUntilM(curry(f), predicate, initialState, ta); /// /// Right-associative fold of a structure, lazy in the accumulator. /// /// In the case of lists, 'Fold', when applied to a binary operator, a /// starting value (typically the right-identity of the operator), and a /// list, reduces the list using the binary operator, from right to left. /// public static S fold(Func> f, S initialState, K ta) where T : Foldable => T.Fold(f, initialState, ta); /// /// Right-associative fold of a structure, lazy in the accumulator. /// /// In the case of lists, 'Fold', when applied to a binary operator, a /// starting value (typically the right-identity of the operator), and a /// list, reduces the list using the binary operator, from right to left. /// public static S fold(Func f, S initialState, K ta) where T : Foldable => T.Fold(a => s => f(s, a), initialState, ta); /// /// Right-associative fold of a structure, lazy in the accumulator. /// /// In the case of lists, 'Fold', when applied to a binary operator, a /// starting value (typically the right-identity of the operator), and a /// list, reduces the list using the binary operator, from right to left. /// public static K foldM( Func>> f, S initialState, K ta) where T : Foldable where M : Monad => T.FoldM(f, initialState, ta); /// /// Right-associative fold of a structure, lazy in the accumulator. /// /// In the case of lists, 'Fold', when applied to a binary operator, a /// starting value (typically the right-identity of the operator), and a /// list, reduces the list using the binary operator, from right to left. /// public static K foldM( Func> f, S initialState, K ta) where T : Foldable where M : Monad => T.FoldM(a => s => f(s, a), initialState, ta); /// /// Left-associative fold of a structure, lazy in the accumulator. This /// is rarely what you want, but can work well for structures with efficient /// right-to-left sequencing and an operator that is lazy in its left /// argument. /// /// In the case of lists, 'FoldLeft', when applied to a binary operator, a /// starting value (typically the left-identity of the operator), and a /// list, reduces the list using the binary operator, from left to right /// /// /// Note that to produce the outermost application of the operator the /// entire input list must be traversed. Like all left-associative folds, /// `FoldBack' will diverge if given an infinite list. /// public static S foldBack(Func> f, S initialState, K ta) where T : Foldable => T.FoldBack(f, initialState, ta); /// /// Left-associative fold of a structure, lazy in the accumulator. This /// is rarely what you want, but can work well for structures with efficient /// right-to-left sequencing and an operator that is lazy in its left /// argument. /// /// In the case of lists, 'FoldLeft', when applied to a binary operator, a /// starting value (typically the left-identity of the operator), and a /// list, reduces the list using the binary operator, from left to right /// /// /// Note that to produce the outermost application of the operator the /// entire input list must be traversed. Like all left-associative folds, /// `FoldBack' will diverge if given an infinite list. /// public static S foldBack(Func f, S initialState, K ta) where T : Foldable => T.FoldBack(curry(f), initialState, ta); /// /// Left-associative fold of a structure, lazy in the accumulator. This /// is rarely what you want, but can work well for structures with efficient /// right-to-left sequencing and an operator that is lazy in its left /// argument. /// /// In the case of lists, 'FoldLeft', when applied to a binary operator, a /// starting value (typically the left-identity of the operator), and a /// list, reduces the list using the binary operator, from left to right /// /// /// Note that to produce the outermost application of the operator the /// entire input list must be traversed. Like all left-associative folds, /// `FoldBack' will diverge if given an infinite list. /// public static K foldBackM( Func>> f, S initialState, K ta) where T : Foldable where M : Monad => T.FoldBackM(f, initialState, ta); /// /// Left-associative fold of a structure, lazy in the accumulator. This /// is rarely what you want, but can work well for structures with efficient /// right-to-left sequencing and an operator that is lazy in its left /// argument. /// /// In the case of lists, 'FoldLeft', when applied to a binary operator, a /// starting value (typically the left-identity of the operator), and a /// list, reduces the list using the binary operator, from left to right /// /// /// Note that to produce the outermost application of the operator the /// entire input list must be traversed. Like all left-associative folds, /// `FoldBack' will diverge if given an infinite list. /// public static K foldBackM( Func> f, S initialState, K ta) where T : Foldable where M : Monad => T.FoldBackM(curry(f), initialState, ta); /// /// Given a structure with elements whose type is a `Monoid`, combine them /// via the monoid's `Append` operator. This fold is right-associative and /// lazy in the accumulator. When you need a strict left-associative fold, /// use 'foldMap'' instead, with 'id' as the map. /// public static A fold(K tm) where T : Foldable where A : Monoid => T.FoldMap(identity, tm) ; /// /// Given a structure with elements whose type is a `Monoid`, combine them /// via the monoid's `Append` operator. This fold is right-associative and /// lazy in the accumulator. When you need a strict left-associative fold, /// use 'foldMap'' instead, with 'id' as the map. /// public static A foldWhile(Func<(A State, A Value), bool> predicate, K tm) where T : Foldable where A : Monoid => T.FoldMapWhile(identity, predicate, tm) ; /// /// Given a structure with elements whose type is a `Monoid`, combine them /// via the monoid's `Append` operator. This fold is right-associative and /// lazy in the accumulator. When you need a strict left-associative fold, /// use 'foldMap'' instead, with 'id' as the map. /// public static A foldUntil(Func<(A State, A Value), bool> predicate, K tm) where T : Foldable where A : Monoid => T.FoldMapUntil(identity, predicate, tm) ; /// /// Map each element of the structure into a monoid, and combine the /// results with `Append`. This fold is right-associative and lazy in the /// accumulator. For strict left-associative folds consider `FoldMapBack` /// instead. /// public static B foldMap(Func f, K ta) where T : Foldable where B : Monoid => T.FoldMap(f, ta); /// /// Map each element of the structure into a monoid, and combine the /// results with `Append`. This fold is right-associative and lazy in the /// accumulator. For strict left-associative folds consider `FoldMapBack` /// instead. /// public static B foldMapWhile(Func f, Func<(B State, A Value), bool> predicate, K ta) where T : Foldable where B : Monoid => T.FoldMapWhile(f, predicate, ta); /// /// Map each element of the structure into a monoid, and combine the /// results with `Append`. This fold is right-associative and lazy in the /// accumulator. For strict left-associative folds consider `FoldMapBack` /// instead. /// public static B foldMapUntil(Func f, Func<(B State, A Value), bool> predicate, K ta) where T : Foldable where B : Monoid => T.FoldMapUntil(f, predicate, ta); /// /// A left-associative variant of 'FoldMap' that is strict in the /// accumulator. Use this method for strict reduction when partial /// results are merged via `Append`. /// public static B foldMapBack(Func f, K ta) where T : Foldable where B : Monoid => T.FoldMapBack(f, ta); /// /// A left-associative variant of 'FoldMap' that is strict in the /// accumulator. Use this method for strict reduction when partial /// results are merged via `Append`. /// public static B foldMapBackWhile(Func f, Func<(B State, A Value), bool> predicate, K ta) where T : Foldable where B : Monoid => T.FoldMapWhileBack(f, predicate, ta); /// /// A left-associative variant of 'FoldMap' that is strict in the /// accumulator. Use this method for strict reduction when partial /// results are merged via `Append`. /// public static B foldMapBackUntil(Func f, Func<(B State, A Value), bool> predicate, K ta) where T : Foldable where B : Monoid => T.FoldMapUntilBack(f, predicate, ta); /// /// List of elements of a structure, from left to right /// public static Seq toSeq(K ta) where T : Foldable => T.ToSeq(ta); /// /// List of elements of a structure, from left to right /// public static Lst toLst(K ta) where T : Foldable => T.ToLst(ta); /// /// List of elements of a structure, from left to right /// public static Arr toArr(K ta) where T : Foldable => T.ToArr(ta); /// /// List of elements of a structure, from left to right /// public static Iterable toIterable(K ta) where T : Foldable => T.ToIterable(ta); /// /// List of elements of a structure, from left to right /// public static bool isEmpty(K ta) where T : Foldable => T.IsEmpty(ta); /// /// Returns the size/length of a finite structure as an `int`. The /// default implementation just counts elements starting with the leftmost. /// /// Instances for structures that can compute the element count faster /// than via element-by-element counting, should provide a specialised /// implementation. /// public static int count(K ta) where T : Foldable => T.Count(ta); /// /// Does an element that fits the predicate occur in the structure? /// public static bool exists(Func predicate, K ta) where T : Foldable => T.Exists(predicate, ta); /// /// Does the predicate hold for all elements in the structure? /// public static bool forAll(Func predicate, K ta) where T : Foldable => T.ForAll(predicate, ta); /// /// Does the element exist in the structure? /// public static bool contains(A value, K ta) where EqA : Eq where T : Foldable => T.Contains(value, ta); /// /// Does the element exist in the structure? /// public static bool contains(A value, K ta) where T : Foldable => T.Contains(value, ta); /// /// Find the first element that match the predicate /// public static Option find(Func predicate, K ta) where T : Foldable => T.Find(predicate, ta); /// /// Find the last element that match the predicate /// public static Option findBack(Func predicate, K ta) where T : Foldable => T.FindBack(predicate, ta); /// /// Find the elements that match the predicate /// public static Iterable findAll(Func predicate, K ta) where T : Foldable => T.FindAll(predicate, ta); /// /// Find the elements that match the predicate /// public static Iterable findAllBack(Func predicate, K ta) where T : Foldable => T.FindAllBack(predicate, ta); /// /// Computes the sum of the numbers of a structure. /// public static A sum(K ta) where T : Foldable where A : IAdditionOperators, IAdditiveIdentity => T.Sum(ta); /// /// Computes the product of the numbers of a structure. /// public static A product(K ta) where T : Foldable where A : IMultiplyOperators, IMultiplicativeIdentity => T.Product(ta); /// /// Get the head item in the foldable or `None` /// public static Option head(K ta) where T : Foldable => T.Head(ta); /// /// Get the head item in the foldable or `None` /// public static Option last(K ta) where T : Foldable => T.Last(ta); /// /// Map each element of a structure to an 'Applicative' action, evaluate these /// actions from left to right, and ignore the results. For a version that /// doesn't ignore the results see `Traversable.traverse`. /// public static K iter(Func> f, K ta) where T : Foldable where F : Monad => T.Iter(f, ta); /// /// Map each element of a structure to an action, evaluate these /// actions from left to right, and ignore the results. For a version that /// doesn't ignore the results see `Traversable.traverse`. /// public static Unit iter(Action f, K ta) where T : Foldable => T.Iter(f, ta); /// /// Map each element of a structure to an action, evaluate these /// actions from left to right, and ignore the results. For a version that /// doesn't ignore the results see `Traversable.traverse`. /// public static Unit iter(Action f, K ta) where T : Foldable => T.Iter(f, ta); /// /// Find the minimum value in the structure /// public static Option min(K ta) where T : Foldable where OrdA : Ord => T.Min(ta); /// /// Find the minimum value in the structure /// public static Option min(K ta) where T : Foldable where A : IComparable => T.Min(ta); /// /// Find the maximum value in the structure /// public static Option max(K ta) where T : Foldable where OrdA : Ord => T.Max(ta); /// /// Find the maximum value in the structure /// public static Option max(K ta) where T : Foldable where A : IComparable => T.Max(ta); /// /// Find the minimum value in the structure /// public static A min(K ta, A initialMin) where T : Foldable where OrdA : Ord => T.Min(ta, initialMin); /// /// Find the minimum value in the structure /// public static A min(K ta, A initialMin) where T : Foldable where A : IComparable => T.Min(ta, initialMin); /// /// Find the maximum value in the structure /// public static A max(K ta, A initialMax) where T : Foldable where OrdA : Ord => T.Max(ta, initialMax); /// /// Find the maximum value in the structure /// public static A max(K ta, A initialMax) where T : Foldable where A : IComparable => T.Max(ta, initialMax); /// /// Find the average of all the values in the structure /// public static A average(K ta) where T : Foldable where A : INumber => T.Average(ta); /// /// Find the average of all the values in the structure /// public static B average(Func f, K ta) where T : Foldable where B : INumber => T.Average(f, ta); /// /// Find the element at the specified index or `None` if out of range /// public static Option at(K ta, Index index) where T : Foldable => T.At(ta, index); /// /// Partition a foldable into two sequences based on a predicate /// /// Predicate function /// Foldable structure /// Bound value type /// Partitioned structure public static (Seq True, Seq False) partition(Func f, K ta) where T : Foldable => T.Partition(f, ta); } ================================================ FILE: LanguageExt.Core/Traits/Foldable/Foldable.Prelude.cs ================================================ using System; using System.Numerics; using System.Threading.Tasks; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; public static partial class Prelude { /// /// Same behaviour as `Fold` but allows early exit of the operation once /// the predicate function becomes `false` for the state/value pair /// public static S foldWhile( Func> f, Func<(S State, A Value), bool> predicate, S initialState, K ta) where T : Foldable => T.FoldWhile(f, predicate, initialState, ta); /// /// Same behaviour as `Fold` but allows early exit of the operation once /// the predicate function becomes `false` for the state/value pair /// public static S foldWhile( Func f, Func<(S State, A Value), bool> predicate, S initialState, K ta) where T : Foldable => T.FoldWhile(a => s => f(s, a), predicate, initialState, ta); /// /// Same behaviour as `FoldBack` but allows early exit of the operation once /// the predicate function becomes `false` for the state/value pair /// public static S foldBackWhile( Func> f, Func<(S State, A Value), bool> predicate, S initialState, K ta) where T : Foldable => T.FoldBackWhile(f, predicate, initialState, ta); /// /// Same behaviour as `FoldBack` but allows early exit of the operation once /// the predicate function becomes `false` for the state/value pair /// public static S foldBackWhile( Func f, Func<(S State, A Value), bool> predicate, S initialState, K ta) where T : Foldable => T.FoldBackWhile(curry(f), predicate, initialState, ta); /// /// Same behaviour as `Fold` but the fold operation returns a monadic type and allows /// early exit of the operation once the predicate function becomes `false` for the /// state/value pair /// public static K foldWhileM( Func>> f, Func<(S State, A Value), bool> predicate, S initialState, K ta) where T : Foldable where M : Monad => T.FoldWhileM(f, predicate, initialState, ta); /// /// Same behaviour as `Fold` but the fold operation returns a monadic type and allows /// early exit of the operation once the predicate function becomes `false` for the /// state/value pair /// public static K foldWhileM( Func> f, Func<(S State, A Value), bool> predicate, S initialState, K ta) where T : Foldable where M : Monad => T.FoldWhileM(a => s => f(s, a), predicate, initialState, ta); /// /// Same behaviour as `FoldBack` but the fold operation returns a monadic type and allows /// early exit of the operation once the predicate function becomes `false` for the /// state/value pair /// public static K foldBackWhileM( Func>> f, Func<(S State, A Value), bool> predicate, S initialState, K ta) where T : Foldable where M : Monad => T.FoldBackWhileM(f, predicate, initialState, ta); /// /// Same behaviour as `FoldBack` but the fold operation returns a monadic type and allows /// early exit of the operation once the predicate function becomes `false` for the /// state/value pair /// public static K foldBackWhileM( Func> f, Func<(S State, A Value), bool> predicate, S initialState, K ta) where T : Foldable where M : Monad => T.FoldBackWhileM(curry(f), predicate, initialState, ta); /// /// Same behaviour as `Fold` but allows early exit of the operation once /// the predicate function becomes `false` for the state/value pair /// public static S foldUntil( Func> f, Func<(S State, A Value), bool> predicate, S initialState, K ta) where T : Foldable => T.FoldUntil(f, predicate, initialState, ta); /// /// Same behaviour as `Fold` but allows early exit of the operation once /// the predicate function becomes `false` for the state/value pair /// public static S foldUntil( Func f, Func<(S State, A Value), bool> predicate, S initialState, K ta) where T : Foldable => T.FoldUntil(a => s => f(s, a), predicate, initialState, ta); /// /// Same behaviour as `Fold` but the fold operation returns a monadic type and allows /// early exit of the operation once the predicate function becomes `false` for the /// state/value pair /// public static K foldUntilM( Func>> f, Func<(S State, A Value), bool> predicate, S initialState, K ta) where M : Monad where T : Foldable => T.FoldUntilM(f, predicate, initialState, ta); /// /// Same behaviour as `Fold` but the fold operation returns a monadic type and allows /// early exit of the operation once the predicate function becomes `false` for the /// state/value pair /// public static K foldUntilM( Func> f, Func<(S State, A Value), bool> predicate, S initialState, K ta) where M : Monad where T : Foldable => T.FoldUntilM(a => s => f(s, a), predicate, initialState, ta); /// /// Same behaviour as `FoldBack` but allows early exit of the operation once /// the predicate function becomes `false` for the state/value pair /// public static S foldBackUntil( Func> f, Func<(S State, A Value), bool> predicate, S initialState, K ta) where T : Foldable => T.FoldBackUntil(f, predicate, initialState, ta); /// /// Same behaviour as `FoldBack` but allows early exit of the operation once /// the predicate function becomes `false` for the state/value pair /// public static S foldBackUntil( Func f, Func<(S State, A Value), bool> predicate, S initialState, K ta) where T : Foldable => T.FoldBackUntil(curry(f), predicate, initialState, ta); /// /// Same behaviour as `FoldBack` but the fold operation returns a monadic type and allows /// early exit of the operation once the predicate function becomes `false` for the /// state/value pair /// public static K foldBackUntilM( Func>> f, Func<(S State, A Value), bool> predicate, S initialState, K ta) where T : Foldable where M : Monad => T.FoldBackUntilM(f, predicate, initialState, ta); /// /// Same behaviour as `FoldBack` but the fold operation returns a monadic type and allows /// early exit of the operation once the predicate function becomes `false` for the /// state/value pair /// public static K foldBackUntilM( Func> f, Func<(S State, A Value), bool> predicate, S initialState, K ta) where T : Foldable where M : Monad => T.FoldBackUntilM(curry(f), predicate, initialState, ta); /// /// Right-associative fold of a structure, lazy in the accumulator. /// /// In the case of lists, 'Fold', when applied to a binary operator, a /// starting value (typically the right-identity of the operator), and a /// list, reduces the list using the binary operator, from right to left. /// public static S fold(Func> f, S initialState, K ta) where T : Foldable => T.Fold(f, initialState, ta); /// /// Right-associative fold of a structure, lazy in the accumulator. /// /// In the case of lists, 'Fold', when applied to a binary operator, a /// starting value (typically the right-identity of the operator), and a /// list, reduces the list using the binary operator, from right to left. /// public static S fold(Func f, S initialState, K ta) where T : Foldable => T.Fold(a => s => f(s, a), initialState, ta); /// /// Right-associative fold of a structure, lazy in the accumulator. /// /// In the case of lists, 'Fold', when applied to a binary operator, a /// starting value (typically the right-identity of the operator), and a /// list, reduces the list using the binary operator, from right to left. /// public static K foldM( Func>> f, S initialState, K ta) where T : Foldable where M : Monad => T.FoldM(f, initialState, ta); /// /// Right-associative fold of a structure, lazy in the accumulator. /// /// In the case of lists, 'Fold', when applied to a binary operator, a /// starting value (typically the right-identity of the operator), and a /// list, reduces the list using the binary operator, from right to left. /// public static K foldM( Func> f, S initialState, K ta) where T : Foldable where M : Monad => T.FoldM(a => s => f(s, a), initialState, ta); /// /// Left-associative fold of a structure, lazy in the accumulator. This /// is rarely what you want, but can work well for structures with efficient /// right-to-left sequencing and an operator that is lazy in its left /// argument. /// /// In the case of lists, 'FoldLeft', when applied to a binary operator, a /// starting value (typically the left-identity of the operator), and a /// list, reduces the list using the binary operator, from left to right /// /// /// Note that to produce the outermost application of the operator the /// entire input list must be traversed. Like all left-associative folds, /// `FoldBack' will diverge if given an infinite list. /// public static S foldBack(Func> f, S initialState, K ta) where T : Foldable => T.FoldBack(f, initialState, ta); /// /// Left-associative fold of a structure, lazy in the accumulator. This /// is rarely what you want, but can work well for structures with efficient /// right-to-left sequencing and an operator that is lazy in its left /// argument. /// /// In the case of lists, 'FoldLeft', when applied to a binary operator, a /// starting value (typically the left-identity of the operator), and a /// list, reduces the list using the binary operator, from left to right /// /// /// Note that to produce the outermost application of the operator the /// entire input list must be traversed. Like all left-associative folds, /// `FoldBack' will diverge if given an infinite list. /// public static S foldBack(Func f, S initialState, K ta) where T : Foldable => T.FoldBack(curry(f), initialState, ta); /// /// Left-associative fold of a structure, lazy in the accumulator. This /// is rarely what you want, but can work well for structures with efficient /// right-to-left sequencing and an operator that is lazy in its left /// argument. /// /// In the case of lists, 'FoldLeft', when applied to a binary operator, a /// starting value (typically the left-identity of the operator), and a /// list, reduces the list using the binary operator, from left to right /// /// /// Note that to produce the outermost application of the operator the /// entire input list must be traversed. Like all left-associative folds, /// `FoldBack' will diverge if given an infinite list. /// public static K foldBackM( Func>> f, S initialState, K ta) where T : Foldable where M : Monad => T.FoldBackM(f, initialState, ta); /// /// Left-associative fold of a structure, lazy in the accumulator. This /// is rarely what you want, but can work well for structures with efficient /// right-to-left sequencing and an operator that is lazy in its left /// argument. /// /// In the case of lists, 'FoldLeft', when applied to a binary operator, a /// starting value (typically the left-identity of the operator), and a /// list, reduces the list using the binary operator, from left to right /// /// /// Note that to produce the outermost application of the operator the /// entire input list must be traversed. Like all left-associative folds, /// `FoldBack' will diverge if given an infinite list. /// public static K foldBackM( Func> f, S initialState, K ta) where T : Foldable where M : Monad => T.FoldBackM(curry(f), initialState, ta); /// /// Given a structure with elements whose type is a `Monoid`, combine them /// via the monoid's `Append` operator. This fold is right-associative and /// lazy in the accumulator. When you need a strict left-associative fold, /// use 'foldMap'' instead, with 'id' as the map. /// public static A fold(K tm) where T : Foldable where A : Monoid => T.FoldMap(identity, tm) ; /// /// Given a structure with elements whose type is a `Monoid`, combine them /// via the monoid's `Append` operator. This fold is right-associative and /// lazy in the accumulator. When you need a strict left-associative fold, /// use 'foldMap'' instead, with 'id' as the map. /// public static A foldWhile(Func<(A State, A Value), bool> predicate, K tm) where T : Foldable where A : Monoid => T.FoldMapWhile(identity, predicate, tm) ; /// /// Given a structure with elements whose type is a `Monoid`, combine them /// via the monoid's `Append` operator. This fold is right-associative and /// lazy in the accumulator. When you need a strict left-associative fold, /// use 'foldMap'' instead, with 'id' as the map. /// public static A foldUntil(Func<(A State, A Value), bool> predicate, K tm) where T : Foldable where A : Monoid => T.FoldMapUntil(identity, predicate, tm) ; /// /// Map each element of the structure into a monoid, and combine the /// results with `Append`. This fold is right-associative and lazy in the /// accumulator. For strict left-associative folds consider `FoldMapBack` /// instead. /// public static B foldMap(Func f, K ta) where T : Foldable where B : Monoid => T.FoldMap(f, ta); /// /// Map each element of the structure into a monoid, and combine the /// results with `Append`. This fold is right-associative and lazy in the /// accumulator. For strict left-associative folds consider `FoldMapBack` /// instead. /// public static B foldMapWhile(Func f, Func<(B State, A Value), bool> predicate, K ta) where T : Foldable where B : Monoid => T.FoldMapWhile(f, predicate, ta); /// /// Map each element of the structure into a monoid, and combine the /// results with `Append`. This fold is right-associative and lazy in the /// accumulator. For strict left-associative folds consider `FoldMapBack` /// instead. /// public static B foldMapUntil(Func f, Func<(B State, A Value), bool> predicate, K ta) where T : Foldable where B : Monoid => T.FoldMapUntil(f, predicate, ta); /// /// A left-associative variant of 'FoldMap' that is strict in the /// accumulator. Use this method for strict reduction when partial /// results are merged via `Append`. /// public static B foldMapBack(Func f, K ta) where T : Foldable where B : Monoid => T.FoldMapBack(f, ta); /// /// A left-associative variant of 'FoldMap' that is strict in the /// accumulator. Use this method for strict reduction when partial /// results are merged via `Append`. /// public static B foldMapWhileBack(Func f, Func<(B State, A Value), bool> predicate, K ta) where T : Foldable where B : Monoid => T.FoldMapWhileBack(f, predicate, ta); /// /// A left-associative variant of 'FoldMap' that is strict in the /// accumulator. Use this method for strict reduction when partial /// results are merged via `Append`. /// public static B foldMapUntilBack(Func f, Func<(B State, A Value), bool> predicate, K ta) where T : Foldable where B : Monoid => T.FoldMapUntilBack(f, predicate, ta); /// /// Map each element of a structure to an 'Applicative' action, evaluate these /// actions from left to right, and ignore the results. For a version that /// doesn't ignore the results see `Traversable.traverse`. /// public static K iter(Func> f, K ta) where T : Foldable where F : Monad => T.Iter(f, ta); /// /// Map each element of a structure to an action, evaluate these /// actions from left to right, and ignore the results. For a version that /// doesn't ignore the results see `Traversable.traverse`. /// public static Unit iter(Action f, K ta) where T : Foldable => T.Iter(f, ta); /// /// Map each element of a structure to an action, evaluate these /// actions from left to right, and ignore the results. For a version that /// doesn't ignore the results see `Traversable.traverse`. /// public static Unit iter(Action f, K ta) where T : Foldable => T.Iter(f, ta); /// /// Partition a foldable into two sequences based on a predicate /// /// Predicate function /// Foldable structure /// Bound value type /// Partitioned structure public static (Seq True, Seq False) partition(Func f, K ta) where T : Foldable => T.Partition(f, ta); } ================================================ FILE: LanguageExt.Core/Traits/Foldable/Foldable.Trait.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Numerics; using LanguageExt.ClassInstances; using static LanguageExt.Prelude; namespace LanguageExt.Traits; public interface Foldable where T : Foldable { //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Abstract members // /// /// Runs a single step of the folding operation. The return value indicates whether the folding /// operation should continue, and if so, what the next step should be. /// /// /// It is up to the consumer of /// this method to implement the actual state-aggregation (the folding) before passing it to the /// continuation function. /// /// /// This differs from `FoldStepBack` in that it is a right-associative fold, whereas `FoldStepBack` is a /// left-associative fold. If that's confusing, think that this works on a list in reverse. /// /// Foldable structure /// Initial state value /// Value type /// State type /// A discriminated union that can be either `Done` or `Loop`. public static abstract Fold FoldStep(K ta, S initialState); /// /// Runs a single step of the folding operation. The return value indicates whether the folding /// operation should continue, and if so, what the next step should be. /// /// /// It is up to the consumer of /// this method to implement the actual state-aggregation (the folding) before passing it to the /// continuation function. /// /// /// This differs from `FoldStep` in that it is a left-associative fold, whereas `FoldStepBack` is a /// right-associative fold. If that's confusing, think that this works on a list in reverse. /// /// Foldable structure /// Initial state value /// Value type /// State type /// A discriminated union that can be either `Done` or `Loop`. public static abstract Fold FoldStepBack(K ta, S initialState); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Default implementations // /// /// Same behaviour as `Fold` but allows early exit of the operation once /// the predicate function becomes `false` for the state/value pair /// public static virtual S FoldWhile( Func> f, Func<(S State, A Value), bool> predicate, S initialState, K ta) { var step = T.FoldStep(ta, initialState); while(true) { switch (step) { case Fold.Done(var state): return state; case Fold.Loop(var state, var value, var next): if (predicate((state, value))) { step = next(f(value)(state)); } else { return state; } break; default: throw new NotSupportedException(); } } } /// /// Same behaviour as `FoldBack` but allows early exit of the operation once /// the predicate function becomes `false` for the state/value pair /// public static virtual S FoldBackWhile( Func> f, Func<(S State, A Value), bool> predicate, S initialState, K ta) { var step = T.FoldStepBack(ta, initialState); while(true) { switch (step) { case Fold.Done(var state): return state; case Fold.Loop(var state, var value, var next): if (predicate((state, value))) { step = next(f(state)(value)); } else { return state; } break; default: throw new NotSupportedException(); } } } /// /// Fold until the `Option` returns `None` /// /// Fold function /// Initial state for the fold /// Foldable structure /// Value type /// State type /// Aggregated value public static virtual S FoldMaybe( Func>> f, S initialState, K ta) { var step = T.FoldStep(ta, initialState); while(true) { switch (step) { case Fold.Done(var state): return state; case Fold.Loop(var state, var value, var next): switch(f(state)(value)) { case { IsSome: true, Case: S state1}: step = next(state1); break; default: return state; } break; default: throw new NotSupportedException(); } } } /// /// Fold until the `Option` returns `None` /// /// Fold function /// Initial state for the fold /// Foldable structure /// Value type /// State type /// Aggregated value public static virtual S FoldBackMaybe( Func>> f, S initialState, K ta) { var step = T.FoldStepBack(ta, initialState); while(true) { switch (step) { case Fold.Done(var state): return state; case Fold.Loop(var state, var value, var next): switch(f(value)(state)) { case { IsSome: true, Case: S state1}: step = next(state1); break; default: return state; } break; default: throw new NotSupportedException(); } } } /// /// Same behaviour as `Fold` but the fold operation returns a monadic type and allows /// early exit of the operation once the predicate function becomes `false` for the /// state/value pair /// public static virtual K FoldWhileM( Func>> f, Func<(S State, A Value), bool> predicate, S initialState, K ta) where M : Monad { var step = T.FoldStep(ta, initialState); return Monad.recur(step, go); K, S>> go(Fold step) { switch (step) { case Fold.Done(var state): return M.Pure(Next.Done, S>(state)); case Fold.Loop(var state, var value, var next): if (predicate((state, value))) { return f(value)(state).Map(s => Next.Loop, S>(next(s))); } else { return M.Pure(Next.Done, S>(state)); } default: throw new NotSupportedException(); } } } /// /// Same behaviour as `FoldBack` but the fold operation returns a monadic type and allows /// early exit of the operation once the predicate function becomes `false` for the /// state/value pair /// public static virtual K FoldBackWhileM( Func>> f, Func<(S State, A Value), bool> predicate, S initialState, K ta) where M : Monad { var step = T.FoldStepBack(ta, initialState); return Monad.recur(step, go); K, S>> go(Fold step) { switch (step) { case Fold.Done(var state): return M.Pure(Next.Done, S>(state)); case Fold.Loop(var state, var value, var next): if (predicate((state, value))) { return f(state)(value).Map(s => Next.Loop, S>(next(s))); } else { return M.Pure(Next.Done, S>(state)); } default: throw new NotSupportedException(); } } } /// /// Same behaviour as `Fold` but allows early exit of the operation once /// the predicate function becomes `false` for the state/value pair /// public static virtual S FoldUntil( Func> f, Func<(S State, A Value), bool> predicate, S initialState, K ta) { var step = T.FoldStep(ta, initialState); while(true) { switch (step) { case Fold.Done(var state): return state; case Fold.Loop(var state, var value, var next): if (predicate((state, value))) { return state; } else { step = next(f(value)(state)); } break; default: throw new NotSupportedException(); } } } /// /// Same behaviour as `Fold` but the fold operation returns a monadic type and allows /// early exit of the operation once the predicate function becomes `false` for the /// state/value pair /// public static virtual K FoldUntilM( Func>> f, Func<(S State, A Value), bool> predicate, S initialState, K ta) where M : Monad { var step = T.FoldStep(ta, initialState); return Monad.recur(step, go); K, S>> go(Fold step) { switch (step) { case Fold.Done(var state): return M.Pure(Next.Done, S>(state)); case Fold.Loop(var state, var value, var next): if (predicate((state, value))) { return M.Pure(Next.Done, S>(state)); } else { return f(value)(state).Map(s => Next.Loop, S>(next(s))); } default: throw new NotSupportedException(); } } } /// /// Same behaviour as `FoldBack` but allows early exit of the operation once /// the predicate function becomes `false` for the state/value pair /// public static virtual S FoldBackUntil( Func> f, Func<(S State, A Value), bool> predicate, S initialState, K ta) { var step = T.FoldStepBack(ta, initialState); while(true) { switch (step) { case Fold.Done(var state): return state; case Fold.Loop(var state, var value, var next): if (predicate((state, value))) { return state; } else { step = next(f(state)(value)); } break; default: throw new NotSupportedException(); } } } /// /// Same behaviour as `FoldBack` but the fold operation returns a monadic type and allows /// early exit of the operation once the predicate function becomes `false` for the /// state/value pair /// public static virtual K FoldBackUntilM( Func>> f, Func<(S State, A Value), bool> predicate, S initialState, K ta) where M : Monad { var step = T.FoldStepBack(ta, initialState); return Monad.recur(step, go); K, S>> go(Fold step) { switch (step) { case Fold.Done(var state): return M.Pure(Next.Done, S>(state)); case Fold.Loop(var state, var value, var next): if (predicate((state, value))) { return M.Pure(Next.Done, S>(state)); } else { return f(state)(value).Map(s => Next.Loop, S>(next(s))); } default: throw new NotSupportedException(); } } } /// /// Right-associative fold of a structure, lazy in the accumulator. /// /// In the case of lists, 'Fold', when applied to a binary operator, a /// starting value (typically the right-identity of the operator), and a /// list, reduces the list using the binary operator, from right to left. /// public static virtual S Fold(Func> f, S initialState, K ta) { var step = T.FoldStep(ta, initialState); while(true) { switch (step) { case Fold.Done(var state): return state; case Fold.Loop(var state, var value, var next): step = next(f(value)(state)); break; default: throw new NotSupportedException(); } } } /// /// Right-associative fold of a structure, lazy in the accumulator. /// /// In the case of lists, 'Fold', when applied to a binary operator, a /// starting value (typically the right-identity of the operator), and a /// list, reduces the list using the binary operator, from right to left. /// public static virtual K FoldM( Func>> f, S initialState, K ta) where M : Monad { var step = T.FoldStep(ta, initialState); return Monad.recur(step, go); K, S>> go(Fold step) { switch (step) { case Fold.Done(var state): return M.Pure(Next.Done, S>(state)); case Fold.Loop(var state, var value, var next): return f(value)(state).Map(s => Next.Loop, S>(next(s))); default: throw new NotSupportedException(); } } } /// /// Left-associative fold of a structure, lazy in the accumulator. This /// is rarely what you want, but can work well for structures with efficient /// right-to-left sequencing and an operator that is lazy in its left /// argument. /// /// In the case of lists, 'FoldLeft', when applied to a binary operator, a /// starting value (typically the left-identity of the operator), and a /// list, reduces the list using the binary operator, from left to right /// /// /// Note that to produce the outermost application of the operator the /// entire input list must be traversed. Like all left-associative folds, /// `FoldBack` will diverge if given an infinite list. /// public static virtual S FoldBack(Func> f, S initialState, K ta) { var step = T.FoldStepBack(ta, initialState); while(true) { switch (step) { case Fold.Done(var state): return state; case Fold.Loop(var state, var value, var next): step = next(f(state)(value)); break; default: throw new NotSupportedException(); } } } /// /// Left-associative fold of a structure, lazy in the accumulator. This /// is rarely what you want, but can work well for structures with efficient /// right-to-left sequencing and an operator that is lazy in its left /// argument. /// /// In the case of lists, 'FoldLeft', when applied to a binary operator, a /// starting value (typically the left-identity of the operator), and a /// list, reduces the list using the binary operator, from left to right /// /// /// Note that to produce the outermost application of the operator the /// entire input list must be traversed. Like all left-associative folds, /// `FoldBack` will diverge if given an infinite list. /// public static virtual K FoldBackM( Func>> f, S initialState, K ta) where M : Monad { var step = T.FoldStepBack(ta, initialState); return Monad.recur(step, go); K, S>> go(Fold step) { switch (step) { case Fold.Done(var state): return M.Pure(Next.Done, S>(state)); case Fold.Loop(var state, var value, var next): return f(state)(value).Map(s => Next.Loop, S>(next(s))); default: throw new NotSupportedException(); } } } /// /// Given a structure with elements whose type is a `Monoid`, combine them /// via the monoid's `Combine` operator /// public static virtual A Fold(K ta) where A : Monoid { var step = T.FoldStep(ta, A.Empty); while(true) { switch (step) { case Fold.Done(var state): return state; case Fold.Loop(var state, var value, var next): step = next(state + value); break; default: throw new NotSupportedException(); } } } /// /// Given a structure with elements whose type is a `Monoid`, combine them /// via the monoid's `Combine` operator. /// public static virtual A FoldWhile(Func<(A State, A Value), bool> predicate, K ta) where A : Monoid { var step = T.FoldStep(ta, A.Empty); while(true) { switch (step) { case Fold.Done(var state): return state; case Fold.Loop(var state, var value, var next): if (predicate((state, value))) { step = next(state + value); } else { return state; } break; default: throw new NotSupportedException(); } } } /// /// Given a structure with elements whose type is a `Monoid`, combine them /// via the monoid's `Combine` operator. /// public static virtual A FoldUntil(Func<(A State, A Value), bool> predicate, K ta) where A : Monoid { var step = T.FoldStep(ta, A.Empty); while(true) { switch (step) { case Fold.Done(var state): return state; case Fold.Loop(var state, var value, var next): if (predicate((state, value))) { return state; } else { step = next(state + value); } break; default: throw new NotSupportedException(); } } } /// /// Map each element of the structure into a monoid, and combine the /// results with `Monoid.Combine`. /// public static virtual B FoldMap(Func f, K ta) where B : Monoid { var step = T.FoldStep(ta, B.Empty); while(true) { switch (step) { case Fold.Done(var state): return state; case Fold.Loop(var state, var value, var next): step = next(state + f(value)); break; default: throw new NotSupportedException(); } } } /// /// Map each element of the structure into a monoid, and combine the /// results with `Combine`. /// public static virtual B FoldMapWhile(Func f, Func<(B State, A Value), bool> predicate, K ta) where B : Monoid { var step = T.FoldStep(ta, B.Empty); while(true) { switch (step) { case Fold.Done(var state): return state; case Fold.Loop(var state, var value, var next): if (predicate((state, value))) { step = next(state + f(value)); } else { return state; } break; default: throw new NotSupportedException(); } } } /// /// Map each element of the structure into a monoid, and combine the /// results with `Append`. This fold is right-associative and lazy in the /// accumulator. For strict left-associative folds consider `FoldMapBack` /// instead. /// public static virtual B FoldMapUntil(Func f, Func<(B State, A Value), bool> predicate, K ta) where B : Monoid { var step = T.FoldStep(ta, B.Empty); while(true) { switch (step) { case Fold.Done(var state): return state; case Fold.Loop(var state, var value, var next): if (predicate((state, value))) { return state; } else { step = next(state + f(value)); } break; default: throw new NotSupportedException(); } } } /// /// A left-associative variant of 'FoldMap' that is strict in the /// accumulator. Use this method for strict reduction when partial /// results are merged via `Append`. /// public static virtual B FoldMapBack(Func f, K ta) where B : Monoid { var step = T.FoldStepBack(ta, B.Empty); while(true) { switch (step) { case Fold.Done(var state): return state; case Fold.Loop(var state, var value, var next): step = next(state + f(value)); break; default: throw new NotSupportedException(); } } } /// /// A left-associative variant of 'FoldMap' that is strict in the /// accumulator. Use this method for strict reduction when partial /// results are merged via `Append`. /// public static virtual B FoldMapWhileBack(Func f, Func<(B State, A Value), bool> predicate, K ta) where B : Monoid { var step = T.FoldStepBack(ta, B.Empty); while(true) { switch (step) { case Fold.Done(var state): return state; case Fold.Loop(var state, var value, var next): if (predicate((state, value))) { step = next(state + f(value)); } else { return state; } break; default: throw new NotSupportedException(); } } } /// /// A left-associative variant of 'FoldMap' that is strict in the /// accumulator. Use this method for strict reduction when partial /// results are merged via `Append`. /// public static virtual B FoldMapUntilBack(Func f, Func<(B State, A Value), bool> predicate, K ta) where B : Monoid { var step = T.FoldStepBack(ta, B.Empty); while(true) { switch (step) { case Fold.Done(var state): return state; case Fold.Loop(var state, var value, var next): if (predicate((state, value))) { return state; } else { step = next(state + f(value)); } break; default: throw new NotSupportedException(); } } } /// /// List of elements of a structure, from left to right /// public static virtual Seq ToSeq(K ta) { return new Seq(go()); IEnumerable go() { var step = T.FoldStep(ta, unit); while (true) { switch (step) { case Fold.Done(_): yield break; case Fold.Loop(_, var value, var next): yield return value; step = next(default); break; default: throw new NotSupportedException(); } } } } /// /// List of elements of a structure, from left to right /// public static virtual Lst ToLst(K ta) { return new Lst(go()); IEnumerable go() { var step = T.FoldStep(ta, unit); while (true) { switch (step) { case Fold.Done(_): yield break; case Fold.Loop(_, var value, var next): yield return value; step = next(default); break; default: throw new NotSupportedException(); } } } } /// /// List of elements of a structure, from left to right /// public static virtual Arr ToArr(K ta) { return new Arr(go()); IEnumerable go() { var step = T.FoldStep(ta, unit); while (true) { switch (step) { case Fold.Done(_): yield break; case Fold.Loop(_, var value, var next): yield return value; step = next(default); break; default: throw new NotSupportedException(); } } } } /// /// List of elements of a structure, from left to right /// public static virtual Iterable ToIterable(K ta) { return go().AsIterable(); IEnumerable go() { var step = T.FoldStep(ta, unit); while (true) { switch (step) { case Fold.Done(_): yield break; case Fold.Loop(_, var value, var next): yield return value; step = next(default); break; default: throw new NotSupportedException(); } } } } /// /// List of elements of a structure, from left to right /// public static virtual bool IsEmpty(K ta) { var step = T.FoldStep(ta, unit); switch (step) { case Fold.Loop(_, _, _): return false; default: return true; } } /// /// Returns the size/length of a finite structure as an `int`. The /// default implementation just counts elements starting with the leftmost. /// /// Instances for structures that can compute the element count faster /// than via element-by-element counting, should provide a specialised /// implementation. /// public static virtual int Count(K ta) { var step = T.FoldStep(ta, unit); var count = 0; while (true) { switch (step) { case Fold.Done(_): return count; case Fold.Loop(_, _, var next): count++; step = next(default); break; default: throw new NotSupportedException(); } } } /// /// Does an element that fits the predicate occur in the structure? /// public static virtual bool Exists(Func predicate, K ta) { var step = T.FoldStep(ta, unit); while (true) { switch (step) { case Fold.Done(_): return false; case Fold.Loop(_, var value, var next): if(predicate(value)) return true; step = next(default); break; default: throw new NotSupportedException(); } } } /// /// Does the predicate hold for all elements in the structure? /// public static virtual bool ForAll(Func predicate, K ta) { var step = T.FoldStep(ta, unit); while (true) { switch (step) { case Fold.Done(_): return true; case Fold.Loop(_, var value, var next): if(!predicate(value)) return false; step = next(default); break; default: throw new NotSupportedException(); } } } /// /// Does the element exist in the structure? /// public static virtual bool Contains(A value, K ta) where EqA : Eq { var step = T.FoldStep(ta, unit); while (true) { switch (step) { case Fold.Done(_): return false; case Fold.Loop(_, var x, var next): if(EqA.Equals(value, x)) return true; step = next(default); break; default: throw new NotSupportedException(); } } } /// /// Does the element exist in the structure? /// public static virtual bool Contains(A value, K ta) { var step = T.FoldStep(ta, unit); while (true) { switch (step) { case Fold.Done(_): return false; case Fold.Loop(_, var x, var next): if(EqDefault.Equals(value, x)) return true; step = next(default); break; default: throw new NotSupportedException(); } } } /// /// Find the first element that match the predicate /// public static virtual Option Find(Func predicate, K ta) { var step = T.FoldStep(ta, unit); while (true) { switch (step) { case Fold.Done(_): return default; case Fold.Loop(_, var value, var next): if(predicate(value)) return Some(value); step = next(default); break; default: throw new NotSupportedException(); } } } /// /// Find the last element that match the predicate /// public static virtual Option FindBack(Func predicate, K ta) { var step = T.FoldStepBack(ta, unit); while (true) { switch (step) { case Fold.Done(_): return default; case Fold.Loop(_, var value, var next): if(predicate(value)) return Some(value); step = next(default); break; default: throw new NotSupportedException(); } } } /// /// Find the elements that match the predicate /// public static virtual Iterable FindAll(Func predicate, K ta) { return go().AsIterable(); IEnumerable go() { var step = T.FoldStep(ta, unit); while (true) { switch (step) { case Fold.Done(_): yield break; case Fold.Loop(_, var value, var next): if (predicate(value)) { yield return value; } step = next(default); break; default: throw new NotSupportedException(); } } } } /// /// Find the elements that match the predicate /// public static virtual Iterable FindAllBack(Func predicate, K ta) { return go().AsIterable(); IEnumerable go() { var step = T.FoldStepBack(ta, unit); while (true) { switch (step) { case Fold.Done(_): yield break; case Fold.Loop(_, var value, var next): if (predicate(value)) { yield return value; } step = next(default); break; default: throw new NotSupportedException(); } } } } /// /// Computes the sum of the numbers of a structure. /// public static virtual A Sum(K ta) where A : IAdditionOperators, IAdditiveIdentity { var step = T.FoldStep(ta, A.AdditiveIdentity); while (true) { switch (step) { case Fold.Done(var state): return state; case Fold.Loop(var state, var value, var next): step = next(state + value); break; default: throw new NotSupportedException(); } } } /// /// Computes the product of the numbers of a structure. /// public static virtual A Product(K ta) where A : IMultiplyOperators, IMultiplicativeIdentity { var step = T.FoldStep(ta, A.MultiplicativeIdentity); while (true) { switch (step) { case Fold.Done(var state): return state; case Fold.Loop(var state, var value, var next): step = next(state * value); break; default: throw new NotSupportedException(); } } } /// /// Get the head item in the foldable or `None` /// public static virtual Option Head(K ta) { var step = T.FoldStep(ta, unit); switch (step) { case Fold.Done(_): return default; case Fold.Loop(_, var value, _): return value; default: throw new NotSupportedException(); } } /// /// Get the last item in the foldable or `None` /// public static virtual Option Last(K ta) { var step = T.FoldStepBack(ta, unit); switch (step) { case Fold.Done(_): return default; case Fold.Loop(_, var value, _): return value; default: throw new NotSupportedException(); } } /// /// Map each element of a structure to a monadic action, evaluate these /// actions from left to right, and ignore the results. /// public static virtual K Iter(Func> f, K ta) where M : Monad { var step = T.FoldStep(ta, unit); return Monad.recur(step, go); K, Unit>> go(Fold step) { switch (step) { case Fold.Done(_): return M.Pure(Next.Done, Unit>(default)); case Fold.Loop(_, var value, var next): return f(value).Map(_ => Next.Loop, Unit>(next(default))); default: throw new NotSupportedException(); } } } /// /// Map each element of a structure to an action, evaluate these /// actions from left to right, and ignore the results. For a version that /// doesn't ignore the results see `Traversable.traverse`. /// public static virtual Unit Iter(Action f, K ta) { var step = T.FoldStep(ta, unit); while (true) { switch (step) { case Fold.Done(_): return default; case Fold.Loop(_, var value, var next): f(value); step = next(default); break; default: throw new NotSupportedException(); } } } /// /// Map each element of a structure to an action, evaluate these /// actions from left to right, and ignore the results. For a version that /// doesn't ignore the results see `Traversable.traverse`. /// public static virtual Unit Iter(Action f, K ta) { var step = T.FoldStep(ta, unit); var ix = 0; while (true) { switch (step) { case Fold.Done(_): return default; case Fold.Loop(_, var value, var next): f(ix, value); step = next(default); break; default: throw new NotSupportedException(); } } } /// /// Find the minimum value in the structure /// public static virtual Option Min(K ta) where OrdA : Ord { var step = T.FoldStep(ta, unit); A current; switch (step) { case Fold.Done(_): return default; case Fold.Loop(_, var value, var next): current = value; step = next(default); break; default: throw new NotSupportedException(); } while (true) { switch (step) { case Fold.Done(_): return current; case Fold.Loop(_, var value, var next): if (OrdA.Compare(value, current) < 0) { current = value; } step = next(default); break; default: throw new NotSupportedException(); } } } /// /// Find the minimum value in the structure /// public static virtual Option Min(K ta) where A : IComparable { var step = T.FoldStep(ta, unit); A current; switch (step) { case Fold.Done(_): return default; case Fold.Loop(_, var value, var next): current = value; step = next(default); break; default: throw new NotSupportedException(); } while (true) { switch (step) { case Fold.Done(_): return current; case Fold.Loop(_, var value, var next): if (value.CompareTo(current) < 0) { current = value; } step = next(default); break; default: throw new NotSupportedException(); } } } /// /// Find the maximum value in the structure /// public static virtual Option Max(K ta) where OrdA : Ord { var step = T.FoldStep(ta, unit); A current; switch (step) { case Fold.Done(_): return default; case Fold.Loop(_, var value, var next): current = value; step = next(default); break; default: throw new NotSupportedException(); } while (true) { switch (step) { case Fold.Done(_): return current; case Fold.Loop(_, var value, var next): if (OrdA.Compare(value, current) > 0) { current = value; } step = next(default); break; default: throw new NotSupportedException(); } } } /// /// Find the maximum value in the structure /// public static virtual Option Max(K ta) where A : IComparable { var step = T.FoldStep(ta, unit); A current; switch (step) { case Fold.Done(_): return default; case Fold.Loop(_, var value, var next): current = value; step = next(default); break; default: throw new NotSupportedException(); } while (true) { switch (step) { case Fold.Done(_): return current; case Fold.Loop(_, var value, var next): if (value.CompareTo(current) > 0) { current = value; } step = next(default); break; default: throw new NotSupportedException(); } } } /// /// Find the minimum value in the structure /// public static virtual A Min(K ta, A initialMin) where OrdA : Ord { var step = T.FoldStep(ta, unit); var current = initialMin; while (true) { switch (step) { case Fold.Done(_): return current; case Fold.Loop(_, var value, var next): if (OrdA.Compare(value, current) < 0) { current = value; } step = next(default); break; default: throw new NotSupportedException(); } } } /// /// Find the minimum value in the structure /// public static virtual A Min(K ta, A initialMin) where A : IComparable { var step = T.FoldStep(ta, unit); var current = initialMin; while (true) { switch (step) { case Fold.Done(_): return current; case Fold.Loop(_, var value, var next): if (value.CompareTo(current) < 0) { current = value; } step = next(default); break; default: throw new NotSupportedException(); } } } /// /// Find the maximum value in the structure /// public static virtual A Max(K ta, A initialMax) where OrdA : Ord { var step = T.FoldStep(ta, unit); var current = initialMax; while (true) { switch (step) { case Fold.Done(_): return current; case Fold.Loop(_, var value, var next): if (OrdA.Compare(value, current) > 0) { current = value; } step = next(default); break; default: throw new NotSupportedException(); } } } /// /// Find the maximum value in the structure /// public static virtual A Max(K ta, A initialMax) where A : IComparable { var step = T.FoldStep(ta, unit); var current = initialMax; while (true) { switch (step) { case Fold.Done(_): return current; case Fold.Loop(_, var value, var next): if (value.CompareTo(current) > 0) { current = value; } step = next(default); break; default: throw new NotSupportedException(); } } } /// /// Find the average of all the values in the structure /// public static virtual A Average(K ta) where A : INumber { var step = T.FoldStep(ta, unit); var taken = A.Zero; var total = A.Zero; while (true) { switch (step) { case Fold.Done(_): return taken == A.Zero ? A.Zero : total / taken; case Fold.Loop(_, var value, var next): taken += A.One; total += value; step = next(default); break; default: throw new NotSupportedException(); } } } /// /// Find the average of all the values in the structure /// public static virtual B Average(Func f, K ta) where B : INumber { var step = T.FoldStep(ta, unit); var taken = B.Zero; var total = B.Zero; while (true) { switch (step) { case Fold.Done(_): return taken == B.Zero ? B.Zero : total / taken; case Fold.Loop(_, var value, var next): taken += B.One; total += f(value); step = next(default); break; default: throw new NotSupportedException(); } } } /// /// Find the element at the specified index or `None` if out of range /// public static virtual Option At(K ta, Index index) { var step = index.IsFromEnd ? T.FoldStepBack(ta, unit) : T.FoldStep(ta, unit); var ix = 0; while (true) { switch (step) { case Fold.Done(_): return default; case Fold.Loop(_, var value, _) when ix == index.Value: return value; case Fold.Loop(_, _, var next): ix++; step = next(default); break; default: throw new NotSupportedException(); } } } /// /// Partition a foldable into two sequences based on a predicate /// /// Predicate function /// Foldable structure /// Bound value type /// Partitioned structure public static virtual (Seq True, Seq False) Partition(Func f, K ta) { var step = T.FoldStep(ta, unit); var @true = Seq(); var @false = Seq(); while (true) { switch (step) { case Fold.Done(_): return (@true, @false); case Fold.Loop(_, var value, var next): if (f(value)) { @true = @true.Add(value); } else { @false = @false.Add(value); } step = next(default); break; default: throw new NotSupportedException(); } } } } ================================================ FILE: LanguageExt.Core/Traits/Fraction/Fraction.Prelude.cs ================================================ #nullable enable using LanguageExt.Traits; using System.Diagnostics.Contracts; namespace LanguageExt; public static partial class Trait { /// /// Generates a fractional value from an integer ratio. /// /// The ratio to convert /// The equivalent of x in the implementing type. [Pure] public static A fromRational(Ratio x) where FRACTION : Fraction => FRACTION.FromRational(x); } ================================================ FILE: LanguageExt.Core/Traits/Fraction/Fraction.cs ================================================ using System.Diagnostics.Contracts; using LanguageExt.Attributes; namespace LanguageExt.Traits; /// /// Fractional number trait /// /// The type for which fractional /// operations are being defined. [Trait("Fraction*")] public interface Fraction : Num { /// /// Generates a fractional value from an integer ratio. /// /// The ratio to convert /// The equivalent of x in the implementing type. [Pure] public static abstract A FromRational(Ratio x); } ================================================ FILE: LanguageExt.Core/Traits/Functor/Extensions/Functor.Extensions.cs ================================================ using System; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; public static partial class FunctorExtensions { extension(Guard ma) { /// /// Monad bind operation /// /// Monadic bind function /// Projection function /// Functor trait /// Intermediate bound value type /// Target bound value type /// M〈C〉 public K SelectMany(Func> bind, Func project) where F : Functor, Fallible => ma switch { { Flag: true } => F.Map(b => project(default, b), bind(default)), var guard => F.Fail(guard.OnFalse()) }; } extension(Guard, Unit> ma) { /// /// Monad bind operation /// /// Monadic bind function /// Projection function /// Functor trait /// Intermediate bound value type /// Target bound value type /// M〈C〉 public K SelectMany(Func> bind, Func project) where F : Functor, Fallible => ma switch { { Flag: true } => F.Map(b => project(default, b), bind(default)), var guard => F.Fail(guard.OnFalse().Value) }; } extension(K ma) where Fnctr : Functor { /// /// Ignores the bound value result and instead maps it to `Unit` /// /// Functor with unit bound value public K IgnoreF() => ma.Map(_ => default(Unit)); /// /// Functor map operation /// /// /// Unwraps the value within the functor, passes it to the map function `f` provided, and /// then takes the mapped value and wraps it back up into a new functor. /// /// Mapping function /// Mapped functor public K Map(Func f) => Fnctr.Map(f, ma); /// /// Functor map operation /// /// /// Invokes the functor, extracting its bound value, which it then ignores and /// then maps to the default `value` provided. This is useful for side-effecting /// monads that have an effect on the world, which the result value can be ignored, /// to then return a default. /// /// Ignore the bound value and map to this /// Mapped functor public K ConstMap(B value) => Fnctr.ConstMap(value, ma); /// /// Functor map operation /// /// /// Unwraps the value within the functor, passes it to the map function `f` provided, and /// then takes the mapped value and wraps it back up into a new functor. /// /// Mapping function /// Mapped functor public K Select(Func f) => Fnctr.Map(f, ma); } extension(Func f) where Fnctr : Functor { /// /// Functor map operation /// /// /// Unwraps the value within the functor, passes it to the map function `f` provided, and /// then takes the mapped value and wraps it back up into a new functor. /// /// Functor to map /// Mapped functor public K Map(K ma) => Fnctr.Map(f, ma); } /// Mapping function /// Trait of the functor extension(Func f) where Fnctr : Functor { /// /// Functor map operation /// /// /// Unwraps the value within the functor, passes it to the map function `f` provided, and /// then takes the mapped value and wraps it back up into a new functor. /// /// /// This variant takes the passed function and partially applies it, so the result is a /// functor within the value being the partially applied function. If `Fnctr` is also an /// `Applicative`, which is often the case, you can provide further arguments to the /// partially applied function by calling `.Apply()` on the resulting functor. /// /// You can continue this until all arguments have been provided and then you'll have /// a functor within the result of the function wrapped up inside. /// /// Functor to map /// Mapped functor public K> Map(K ma) => curry(f) * ma; } extension(Func f) where Fnctr : Functor { /// /// Functor map operation /// /// /// Unwraps the value within the functor, passes it to the map function `f` provided, and /// then takes the mapped value and wraps it back up into a new functor. /// /// /// This variant takes the passed function and partially applies it, so the result is a /// functor within the value being the partially applied function. If `Fnctr` is also an /// `Applicative`, which is often the case, you can provide further arguments to the /// partially applied function by calling `.Apply()` on the resulting functor. /// /// You can continue this until all arguments have been provided and then you'll have /// a functor within the result of the function wrapped up inside. /// /// Functor to map /// Mapped functor public K>> Map(K ma) => curry(f) * ma; } extension(Func f) where Fnctr : Functor { /// /// Functor map operation /// /// /// Unwraps the value within the functor, passes it to the map function `f` provided, and /// then takes the mapped value and wraps it back up into a new functor. /// /// /// This variant takes the passed function and partially applies it, so the result is a /// functor within the value being the partially applied function. If `Fnctr` is also an /// `Applicative`, which is often the case, you can provide further arguments to the /// partially applied function by calling `.Apply()` on the resulting functor. /// /// You can continue this until all arguments have been provided and then you'll have /// a functor within the result of the function wrapped up inside. /// /// Functor to map /// Mapped functor public K>>> Map(K ma) => curry(f) * ma; } /// Mapping function /// Trait of the functor extension(Func f) where Fnctr : Functor { /// /// Functor map operation /// /// /// Unwraps the value within the functor, passes it to the map function `f` provided, and /// then takes the mapped value and wraps it back up into a new functor. /// /// /// This variant takes the passed function and partially applies it, so the result is a /// functor within the value being the partially applied function. If `Fnctr` is also an /// `Applicative`, which is often the case, you can provide further arguments to the /// partially applied function by calling `.Apply()` on the resulting functor. /// /// You can continue this until all arguments have been provided and then you'll have /// a functor within the result of the function wrapped up inside. /// /// Functor to map /// Mapped functor public K>>>> Map( K ma) => curry(f) * ma; } extension(Func f) where Fnctr : Functor { /// /// Functor map operation /// /// /// Unwraps the value within the functor, passes it to the map function `f` provided, and /// then takes the mapped value and wraps it back up into a new functor. /// /// /// This variant takes the passed function and partially applies it, so the result is a /// functor within the value being the partially applied function. If `Fnctr` is also an /// `Applicative`, which is often the case, you can provide further arguments to the /// partially applied function by calling `.Apply()` on the resulting functor. /// /// You can continue this until all arguments have been provided and then you'll have /// a functor within the result of the function wrapped up inside. /// /// Functor to map /// Mapped functor public K>>>>> Map( K ma) => curry(f) * ma; } extension(Func f) where Fnctr : Functor { /// /// Functor map operation /// /// /// Unwraps the value within the functor, passes it to the map function `f` provided, and /// then takes the mapped value and wraps it back up into a new functor. /// /// /// This variant takes the passed function and partially applies it, so the result is a /// functor within the value being the partially applied function. If `Fnctr` is also an /// `Applicative`, which is often the case, you can provide further arguments to the /// partially applied function by calling `.Apply()` on the resulting functor. /// /// You can continue this until all arguments have been provided and then you'll have /// a functor within the result of the function wrapped up inside. /// /// Functor to map /// Mapped functor public K>>>>>> Map( K ma) => curry(f) * ma; } extension(Func f) where Fnctr : Functor { /// /// Functor map operation /// /// /// Unwraps the value within the functor, passes it to the map function `f` provided, and /// then takes the mapped value and wraps it back up into a new functor. /// /// /// This variant takes the passed function and partially applies it, so the result is a /// functor within the value being the partially applied function. If `Fnctr` is also an /// `Applicative`, which is often the case, you can provide further arguments to the /// partially applied function by calling `.Apply()` on the resulting functor. /// /// You can continue this until all arguments have been provided and then you'll have /// a functor within the result of the function wrapped up inside. /// /// Functor to map /// Mapped functor public K>>>>>>> Map( K ma) => curry(f) * ma; } extension(Func f) where Fnctr : Functor { /// /// Functor map operation /// /// /// Unwraps the value within the functor, passes it to the map function `f` provided, and /// then takes the mapped value and wraps it back up into a new functor. /// /// /// This variant takes the passed function and partially applies it, so the result is a /// functor within the value being the partially applied function. If `Fnctr` is also an /// `Applicative`, which is often the case, you can provide further arguments to the /// partially applied function by calling `.Apply()` on the resulting functor. /// /// You can continue this until all arguments have been provided and then you'll have /// a functor within the result of the function wrapped up inside. /// /// Functor to map /// Mapped functor public K>>>>>>>> Map( K ma) => curry(f) * ma; } extension(Func f) where Fnctr : Functor { /// /// Functor map operation /// /// /// Unwraps the value within the functor, passes it to the map function `f` provided, and /// then takes the mapped value and wraps it back up into a new functor. /// /// /// This variant takes the passed function and partially applies it, so the result is a /// functor within the value being the partially applied function. If `Fnctr` is also an /// `Applicative`, which is often the case, you can provide further arguments to the /// partially applied function by calling `.Apply()` on the resulting functor. /// /// You can continue this until all arguments have been provided and then you'll have /// a functor within the result of the function wrapped up inside. /// /// Functor to map /// Mapped functor public K>>>>>>>>> Map( K ma) => curry(f) * ma; } } ================================================ FILE: LanguageExt.Core/Traits/Functor/Extensions/Functor.ExtensionsT.cs ================================================ using System; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; public static partial class FunctorExtensions { /// /// Runs a functor map operation on the nested functors /// /// /// If you're working with an inner functor that is concrete then you will first need to /// call `KindT` to cast the functor to a more general `K` version. This enables the /// `T` variant extensions (like `BindT`, `MapT, etc.) to work without providing /// excessive generic arguments all the way down the chain. /// /// /// /// var mx = Seq〈Option〈int〉〉(Some(1), Some(2), Some(3)); /// var ma = mx.MapT(a => a + 1); /// /// /// Nested functor value /// Bind function /// `N〈A〉` /// Outer functor trait /// Inner functor trait /// Input bound value /// Output bound value /// Mapped value public static K> MapT(this K> mna, Func f) where M : Functor where N : Functor => M.Map(na => N.Map(f, na), mna); } ================================================ FILE: LanguageExt.Core/Traits/Functor/Extensions/Functor.MemoExtensions.cs ================================================ using System; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; public static partial class FunctorExtensions { extension(Memo self) where FF : Functor { public K Map(Func f) => FF.Map(f, self); } extension(Func self) where FF : Functor { public K Map(Memo fa) => FF.Map(self, fa); } extension(Memo self) where FF : Functor { public K> Map(Func f) => FF.Map(curry(f), self); } extension(Func self) where FF : Functor { public K> Map(Memo fa) => FF.Map(curry(self), fa); } extension(Memo self) where FF : Functor { public K>> Map(Func f) => FF.Map(curry(f), self); } extension(Func self) where FF : Functor { public K>> Map(Memo fa) => FF.Map(curry(self), fa); } extension(Memo self) where FF : Functor { public K>>> Map(Func f) => FF.Map(curry(f), self); } extension(Func self) where FF : Functor { public K>>> Map(Memo fa) => FF.Map(curry(self), fa); } extension(Memo self) where FF : Functor { public K>>>> Map(Func f) => FF.Map(curry(f), self); } extension(Func self) where FF : Functor { public K>>>> Map(Memo fa) => FF.Map(curry(self), fa); } extension(Memo self) where FF : Functor { public K>>>>> Map(Func f) => FF.Map(curry(f), self); } extension(Func self) where FF : Functor { public K>>>>> Map(Memo fa) => FF.Map(curry(self), fa); } extension(Memo self) where FF : Functor { public K>>>>>> Map(Func f) => FF.Map(curry(f), self); } extension(Func self) where FF : Functor { public K>>>>>> Map(Memo fa) => FF.Map(curry(self), fa); } extension(Memo self) where FF : Functor { public K>>>>>>> Map(Func f) => FF.Map(curry(f), self); } extension(Func self) where FF : Functor { public K>>>>>>> Map(Memo fa) => FF.Map(curry(self), fa); } extension(Memo self) where FF : Functor { public K>>>>>>>> Map(Func f) => FF.Map(curry(f), self); } extension(Func self) where FF : Functor { public K>>>>>>>> Map(Memo fa) => FF.Map(curry(self), fa); } extension(Memo self) where FF : Functor { public K>>>>>>>>> Map(Func f) => FF.Map(curry(f), self); } extension(Func self) where FF : Functor { public K>>>>>>>>> Map(Memo fa) => FF.Map(curry(self), fa); } } ================================================ FILE: LanguageExt.Core/Traits/Functor/Functor.Laws.cs ================================================ using System; using LanguageExt.Common; using static LanguageExt.Prelude; namespace LanguageExt.Traits; /// /// Functions that test that functor-laws hold for the `F` functor provided. /// /// /// NOTE: `Equals` must be implemented for the `K〈F, *〉` derived-type, so that the laws /// can be proven to be true. If your functor doesn't have `Equals` then you must provide /// the optional `equals` parameter so that the equality of outcomes can be tested. /// /// Functor type public static class FunctorLaw where F : Functor { /// /// Assert that the functor laws hold /// /// /// NOTE: `Equals` must be implemented for the `K〈F, *〉` derived-type, so that the laws /// can be proven to be true. If your functor doesn't have `Equals` then you must provide /// the optional `equals` parameter so that the equality of outcomes can be tested. /// public static Unit assert(K fa, Func, K, bool>? equals = null) => validate(fa, equals) .IfFail(errors => errors.Throw()); /// /// Validate that the functor laws hold /// /// /// NOTE: `Equals` must be implemented for the `K〈F, *〉` derived-type, so that the laws /// can be proven to be true. If your functor doesn't have `Equals` then you must provide /// the optional `equals` parameter so that the equality of outcomes can be tested. /// public static Validation validate(K fa, Func, K, bool>? equals = null) { equals ??= (fa, fb) => fa.Equals(fb); return identityLaw(fa, equals) >> compositionLaw(fa, f, g, equals); } /// /// Validate the identity law /// /// /// NOTE: `Equals` must be implemented for the `K〈F, *〉` derived-type, so that the laws /// can be proven to be true. If your functor doesn't have `Equals` then you must provide /// the optional `equals` parameter so that the equality of outcomes can be tested. /// public static Validation identityLaw(K lhs, Func, K, bool> equals) { var rhs = lhs.Map(identity); return equals(lhs, rhs) ? unit : Error.New($"Functor identity-law does not hold for {typeof(F).Name}"); } /// /// Validate the composition law /// /// /// NOTE: `Equals` must be implemented for the `K〈F, *〉` derived-type, so that the laws /// can be proven to be true. If your functor doesn't have `Equals` then you must provide /// the optional `equals` parameter so that the equality of outcomes can be tested. /// public static Validation compositionLaw( K fa, Func f, Func g, Func, K, bool> equals) { var lhs = fa.Map(a => g(f(a))); var rhs = fa.Map(f).Map(g); return equals(lhs, rhs) ? unit : Error.New($"Functor composition-law does not hold for {typeof(F).Name}"); } static int f(int x) => x + 1; static int g(int x) => x * 2; } ================================================ FILE: LanguageExt.Core/Traits/Functor/Functor.Module.cs ================================================ using System; namespace LanguageExt.Traits; /// /// Functor module /// public static class Functor { /// /// Functor map. Maps all contained values of `A` to values of `B` /// /// Mapping function /// Functor structure /// Functor trait /// Bound value type /// Resulting bound value type /// Mapped functor public static K map(Func f, K fa) where F : Functor => F.Map(f, fa); /// /// Ignores the bound value result and instead maps it to `Unit` /// /// Functor that returns a bound value that should be ignored /// Functor trait /// Bound value type /// Functor with unit bound value public static K ignore(K fa) where F : Functor => fa.Map(_ => default(Unit)); } ================================================ FILE: LanguageExt.Core/Traits/Functor/Functor.Trait.cs ================================================ using System; namespace LanguageExt.Traits; /// /// Functor trait /// /// /// `Map` is used to apply a function of type `Func〈A, B〉` to a value of type `K〈F, A〉` /// where `F` is a functor, to produce a value of type `K〈F, B〉`. /// /// Note that for any type with more than one parameter (e.g., `Either`), only the /// last type parameter can be modified with `Map` (e.g. `R` in `Either〈L, R〉`). /// /// Some types two generic parameters or more have a `Bifunctor` instance that allows both /// the last and the penultimate parameters to be mapped over. /// /// Self referring type public interface Functor where F : Functor { /// /// Functor map operation /// /// /// Unwraps the value within the functor, passes it to the map function `f` provided, and /// then takes the mapped value and wraps it back up into a new functor. /// /// Mapping function /// Functor to map /// Trait of the functor /// Mapped functor public static abstract K Map(Func f, K ma); /// /// Functor map operation /// /// /// Unwraps the value within the functor, passes it to the map function `f` provided, and /// then takes the mapped value and wraps it back up into a new functor. /// /// Mapping function /// Functor to map /// Trait of the functor /// Mapped functor public static virtual K Map(Func f, Memo ma) => ma.Value.Map(f); /// /// Functor map operation with a constant value /// /// Constant value used to override each bound value in the structure /// Functor to map /// Trait of the functor /// Mapped functor public static virtual K ConstMap(A constantValue, K ma) => ma.Map(_ => constantValue); } ================================================ FILE: LanguageExt.Core/Traits/Functor/Operators/Functor.MemoOperators.cs ================================================ using System; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; public static partial class FunctorExtensions { extension(Memo self) where M : Functor { /// /// Functor map operator /// public static K operator * (Func f, Memo ma) => M.Map(f, ma); /// /// Functor map operator /// public static K operator * (Memo ma, Func f) => M.Map(f, ma); } extension(Memo self) where M : Functor { /// /// Functor map operator /// public static K> operator * ( Func f, Memo ma) => curry(f) * ma; /// /// Functor map operator /// public static K> operator * ( Memo ma, Func f) => curry(f) * ma; } extension(Memo self) where M : Functor { /// /// Functor map operator /// public static K>> operator * ( Func f, Memo ma) => curry(f) * ma; /// /// Functor map operator /// public static K>> operator * ( Memo ma, Func f) => curry(f) * ma; } extension(Memo self) where M : Functor { /// /// Functor map operator /// public static K>>> operator * ( Func f, Memo ma) => curry(f) * ma; /// /// Functor map operator /// public static K>>> operator * ( Memo ma, Func f) => curry(f) * ma; } extension(Memo self) where M : Functor { /// /// Functor map operator /// public static K>>>> operator * ( Func f, Memo ma) => curry(f) * ma; /// /// Functor map operator /// public static K>>>> operator * ( Memo ma, Func f) => curry(f) * ma; } extension(Memo self) where M : Functor { /// /// Functor map operator /// public static K>>>>> operator * ( Func f, Memo ma) => curry(f) * ma; /// /// Functor map operator /// public static K>>>>> operator * ( Memo ma, Func f) => curry(f) * ma; } extension(Memo self) where M : Functor { /// /// Functor map operator /// public static K>>>>>> operator * ( Func f, Memo ma) => curry(f) * ma; /// /// Functor map operator /// public static K>>>>>> operator * ( Memo ma, Func f) => curry(f) * ma; } extension(Memo self) where M : Functor { /// /// Functor map operator /// public static K>>>>>>> operator * ( Func f, Memo ma) => curry(f) * ma; /// /// Functor map operator /// public static K>>>>>>> operator * ( Memo ma, Func f) => curry(f) * ma; } extension(Memo self) where M : Functor { /// /// Functor map operator /// public static K>>>>>>>> operator * ( Func f, Memo ma) => curry(f) * ma; /// /// Functor map operator /// public static K>>>>>>>> operator * ( Memo ma, Func f) => curry(f) * ma; } extension(Memo self) where M : Functor { /// /// Functor map operator /// public static K>>>>>>>>> operator * ( Func f, Memo ma) => curry(f) * ma; /// /// Functor map operator /// public static K>>>>>>>>> operator * ( Memo ma, Func f) => curry(f) * ma; } } ================================================ FILE: LanguageExt.Core/Traits/Functor/Operators/Functor.Operators.cs ================================================ using System; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; public static partial class FunctorExtensions { extension(K self) where M : Functor { /// /// Functor map operator /// public static K operator * (Func f, K ma) => M.Map(f, ma); /// /// Functor map operator /// public static K operator * (Pure pb, K ma) => M.ConstMap(pb.Value, ma); /// /// Functor map operator /// public static K operator * (K ma, Func f) => M.Map(f, ma); /// /// Functor map operator /// public static K operator * (K ma, Pure pb) => M.ConstMap(pb.Value, ma); } extension(K self) where M : Functor { /// /// Functor map operator /// public static K> operator * ( Func f, K ma) => curry(f) * ma; /// /// Functor map operator /// public static K> operator * ( K ma, Func f) => curry(f) * ma; } extension(K self) where M : Functor { /// /// Functor map operator /// public static K>> operator * ( Func f, K ma) => curry(f) * ma; /// /// Functor map operator /// public static K>> operator * ( K ma, Func f) => curry(f) * ma; } extension(K self) where M : Functor { /// /// Functor map operator /// public static K>>> operator * ( Func f, K ma) => curry(f) * ma; /// /// Functor map operator /// public static K>>> operator * ( K ma, Func f) => curry(f) * ma; } extension(K self) where M : Functor { /// /// Functor map operator /// public static K>>>> operator * ( Func f, K ma) => curry(f) * ma; /// /// Functor map operator /// public static K>>>> operator * ( K ma, Func f) => curry(f) * ma; } extension(K self) where M : Functor { /// /// Functor map operator /// public static K>>>>> operator * ( Func f, K ma) => curry(f) * ma; /// /// Functor map operator /// public static K>>>>> operator * ( K ma, Func f) => curry(f) * ma; } extension(K self) where M : Functor { /// /// Functor map operator /// public static K>>>>>> operator * ( Func f, K ma) => curry(f) * ma; /// /// Functor map operator /// public static K>>>>>> operator * ( K ma, Func f) => curry(f) * ma; } extension(K self) where M : Functor { /// /// Functor map operator /// public static K>>>>>>> operator * ( Func f, K ma) => curry(f) * ma; /// /// Functor map operator /// public static K>>>>>>> operator * ( K ma, Func f) => curry(f) * ma; } extension(K self) where M : Functor { /// /// Functor map operator /// public static K>>>>>>>> operator * ( Func f, K ma) => curry(f) * ma; /// /// Functor map operator /// public static K>>>>>>>> operator * ( K ma, Func f) => curry(f) * ma; } extension(K self) where M : Functor { /// /// Functor map operator /// public static K>>>>>>>>> operator * ( Func f, K ma) => curry(f) * ma; /// /// Functor map operator /// public static K>>>>>>>>> operator * ( K ma, Func f) => curry(f) * ma; } } ================================================ FILE: LanguageExt.Core/Traits/Functor/Prelude/Functor.Prelude.cs ================================================ using System; using System.Diagnostics.Contracts; using LanguageExt.Traits; namespace LanguageExt; public static partial class Prelude { /// /// Ignores the bound value result and instead maps it to `Unit` /// /// Functor that returns a bound value that should be ignored /// Functor trait /// Bound value type /// Functor with unit bound value public static K ignore(K fa) where F : Functor => map(_ => default(Unit), fa); /// /// Functor map operation /// /// /// Unwraps the value within the functor, passes it to the map function `f` provided, and /// then takes the mapped value and wraps it back up into a new functor. /// /// Functor to map /// Mapping function /// Trait of the functor /// Mapped functor public static K map( Func f, K ma) where Fnctr : Functor => Fnctr.Map(f, ma); /// /// Functor map operation /// /// /// Unwraps the value within the functor, passes it to the map function `f` provided, and /// then takes the mapped value and wraps it back up into a new functor. /// /// /// This variant takes the passed function and partially applies it, so the result is a /// functor within the value being the partially applied function. If `Fnctr` is also an /// `Applicative`, which is often the case, you can provide further arguments to the /// partially applied function by calling `.Apply()` on the resulting functor. /// /// You can continue this until all arguments have been provided and then you'll have /// a functor within the result of the function wrapped up inside. /// /// Functor to map /// Mapping function /// Trait of the functor /// Mapped functor public static K> map( Func f, K ma) where Fnctr : Functor => Fnctr.Map(x => curry(f)(x), ma); /// /// Functor map operation /// /// /// Unwraps the value within the functor, passes it to the map function `f` provided, and /// then takes the mapped value and wraps it back up into a new functor. /// /// /// This variant takes the passed function and partially applies it, so the result is a /// functor within the value being the partially applied function. If `Fnctr` is also an /// `Applicative`, which is often the case, you can provide further arguments to the /// partially applied function by calling `.Apply()` on the resulting functor. /// /// You can continue this until all arguments have been provided and then you'll have /// a functor within the result of the function wrapped up inside. /// /// Functor to map /// Mapping function /// Trait of the functor /// Mapped functor public static K>> map( Func f, K ma) where Fnctr : Functor => Fnctr.Map(x => curry(f)(x), ma); /// /// Functor map operation /// /// /// Unwraps the value within the functor, passes it to the map function `f` provided, and /// then takes the mapped value and wraps it back up into a new functor. /// /// /// This variant takes the passed function and partially applies it, so the result is a /// functor within the value being the partially applied function. If `Fnctr` is also an /// `Applicative`, which is often the case, you can provide further arguments to the /// partially applied function by calling `.Apply()` on the resulting functor. /// /// You can continue this until all arguments have been provided and then you'll have /// a functor within the result of the function wrapped up inside. /// /// Functor to map /// Mapping function /// Trait of the functor /// Mapped functor public static K>>> map( Func f, K ma) where Fnctr : Functor => Fnctr.Map(x => curry(f)(x), ma); /// /// Functor map operation /// /// /// Unwraps the value within the functor, passes it to the map function `f` provided, and /// then takes the mapped value and wraps it back up into a new functor. /// /// /// This variant takes the passed function and partially applies it, so the result is a /// functor within the value being the partially applied function. If `Fnctr` is also an /// `Applicative`, which is often the case, you can provide further arguments to the /// partially applied function by calling `.Apply()` on the resulting functor. /// /// You can continue this until all arguments have been provided and then you'll have /// a functor within the result of the function wrapped up inside. /// /// Functor to map /// Mapping function /// Trait of the functor /// Mapped functor public static K>>>> map( Func f, K ma) where Fnctr : Functor => Fnctr.Map(x => curry(f)(x), ma); /// /// Functor map operation /// /// /// Unwraps the value within the functor, passes it to the map function `f` provided, and /// then takes the mapped value and wraps it back up into a new functor. /// /// /// This variant takes the passed function and partially applies it, so the result is a /// functor within the value being the partially applied function. If `Fnctr` is also an /// `Applicative`, which is often the case, you can provide further arguments to the /// partially applied function by calling `.Apply()` on the resulting functor. /// /// You can continue this until all arguments have been provided and then you'll have /// a functor within the result of the function wrapped up inside. /// /// Functor to map /// Mapping function /// Trait of the functor /// Mapped functor public static K>>>>> map( Func f, K ma) where Fnctr : Functor => Fnctr.Map(x => curry(f)(x), ma); /// /// Functor map operation /// /// /// Unwraps the value within the functor, passes it to the map function `f` provided, and /// then takes the mapped value and wraps it back up into a new functor. /// /// /// This variant takes the passed function and partially applies it, so the result is a /// functor within the value being the partially applied function. If `Fnctr` is also an /// `Applicative`, which is often the case, you can provide further arguments to the /// partially applied function by calling `.Apply()` on the resulting functor. /// /// You can continue this until all arguments have been provided and then you'll have /// a functor within the result of the function wrapped up inside. /// /// Functor to map /// Mapping function /// Trait of the functor /// Mapped functor public static K>>>>>> map( Func f, K ma) where Fnctr : Functor => Fnctr.Map(x => curry(f)(x), ma); /// /// Functor map operation /// /// /// Unwraps the value within the functor, passes it to the map function `f` provided, and /// then takes the mapped value and wraps it back up into a new functor. /// /// /// This variant takes the passed function and partially applies it, so the result is a /// functor within the value being the partially applied function. If `Fnctr` is also an /// `Applicative`, which is often the case, you can provide further arguments to the /// partially applied function by calling `.Apply()` on the resulting functor. /// /// You can continue this until all arguments have been provided and then you'll have /// a functor within the result of the function wrapped up inside. /// /// Functor to map /// Mapping function /// Trait of the functor /// Mapped functor public static K>>>>>>> map( Func f, K ma) where Fnctr : Functor => Fnctr.Map(x => curry(f)(x), ma); /// /// Functor map operation /// /// /// Unwraps the value within the functor, passes it to the map function `f` provided, and /// then takes the mapped value and wraps it back up into a new functor. /// /// /// This variant takes the passed function and partially applies it, so the result is a /// functor within the value being the partially applied function. If `Fnctr` is also an /// `Applicative`, which is often the case, you can provide further arguments to the /// partially applied function by calling `.Apply()` on the resulting functor. /// /// You can continue this until all arguments have been provided and then you'll have /// a functor within the result of the function wrapped up inside. /// /// Functor to map /// Mapping function /// Trait of the functor /// Mapped functor public static K>>>>>>>> map( Func f, K ma) where Fnctr : Functor => Fnctr.Map(x => curry(f)(x), ma); /// /// Functor map operation /// /// /// Unwraps the value within the functor, passes it to the map function `f` provided, and /// then takes the mapped value and wraps it back up into a new functor. /// /// /// This variant takes the passed function and partially applies it, so the result is a /// functor within the value being the partially applied function. If `Fnctr` is also an /// `Applicative`, which is often the case, you can provide further arguments to the /// partially applied function by calling `.Apply()` on the resulting functor. /// /// You can continue this until all arguments have been provided and then you'll have /// a functor within the result of the function wrapped up inside. /// /// Functor to map /// Mapping function /// Trait of the functor /// Mapped functor public static K>>>>>>>>> map( Func f, K ma) where Fnctr : Functor => Fnctr.Map(x => curry(f)(x), ma); /// /// Functor map operation /// /// /// Unwraps the value within the functor, passes it to the map function `f` provided, and /// then takes the mapped value and wraps it back up into a new functor. /// /// Functor to map /// Mapping function /// Trait of the functor /// Mapped functor public static K map( Func f, Memo ma) where Fnctr : Functor => Fnctr.Map(f, ma); /// /// Functor map operation /// /// /// Unwraps the value within the functor, passes it to the map function `f` provided, and /// then takes the mapped value and wraps it back up into a new functor. /// /// /// This variant takes the passed function and partially applies it, so the result is a /// functor within the value being the partially applied function. If `Fnctr` is also an /// `Applicative`, which is often the case, you can provide further arguments to the /// partially applied function by calling `.Apply()` on the resulting functor. /// /// You can continue this until all arguments have been provided and then you'll have /// a functor within the result of the function wrapped up inside. /// /// Functor to map /// Mapping function /// Trait of the functor /// Mapped functor public static K> map( Func f, Memo ma) where Fnctr : Functor => Fnctr.Map(x => curry(f)(x), ma); /// /// Functor map operation /// /// /// Unwraps the value within the functor, passes it to the map function `f` provided, and /// then takes the mapped value and wraps it back up into a new functor. /// /// /// This variant takes the passed function and partially applies it, so the result is a /// functor within the value being the partially applied function. If `Fnctr` is also an /// `Applicative`, which is often the case, you can provide further arguments to the /// partially applied function by calling `.Apply()` on the resulting functor. /// /// You can continue this until all arguments have been provided and then you'll have /// a functor within the result of the function wrapped up inside. /// /// Functor to map /// Mapping function /// Trait of the functor /// Mapped functor public static K>> map( Func f, Memo ma) where Fnctr : Functor => Fnctr.Map(x => curry(f)(x), ma); /// /// Functor map operation /// /// /// Unwraps the value within the functor, passes it to the map function `f` provided, and /// then takes the mapped value and wraps it back up into a new functor. /// /// /// This variant takes the passed function and partially applies it, so the result is a /// functor within the value being the partially applied function. If `Fnctr` is also an /// `Applicative`, which is often the case, you can provide further arguments to the /// partially applied function by calling `.Apply()` on the resulting functor. /// /// You can continue this until all arguments have been provided and then you'll have /// a functor within the result of the function wrapped up inside. /// /// Functor to map /// Mapping function /// Trait of the functor /// Mapped functor public static K>>> map( Func f, Memo ma) where Fnctr : Functor => Fnctr.Map(x => curry(f)(x), ma); /// /// Functor map operation /// /// /// Unwraps the value within the functor, passes it to the map function `f` provided, and /// then takes the mapped value and wraps it back up into a new functor. /// /// /// This variant takes the passed function and partially applies it, so the result is a /// functor within the value being the partially applied function. If `Fnctr` is also an /// `Applicative`, which is often the case, you can provide further arguments to the /// partially applied function by calling `.Apply()` on the resulting functor. /// /// You can continue this until all arguments have been provided and then you'll have /// a functor within the result of the function wrapped up inside. /// /// Functor to map /// Mapping function /// Trait of the functor /// Mapped functor public static K>>>> map( Func f, Memo ma) where Fnctr : Functor => Fnctr.Map(x => curry(f)(x), ma); /// /// Functor map operation /// /// /// Unwraps the value within the functor, passes it to the map function `f` provided, and /// then takes the mapped value and wraps it back up into a new functor. /// /// /// This variant takes the passed function and partially applies it, so the result is a /// functor within the value being the partially applied function. If `Fnctr` is also an /// `Applicative`, which is often the case, you can provide further arguments to the /// partially applied function by calling `.Apply()` on the resulting functor. /// /// You can continue this until all arguments have been provided and then you'll have /// a functor within the result of the function wrapped up inside. /// /// Functor to map /// Mapping function /// Trait of the functor /// Mapped functor public static K>>>>> map( Func f, Memo ma) where Fnctr : Functor => Fnctr.Map(x => curry(f)(x), ma); /// /// Functor map operation /// /// /// Unwraps the value within the functor, passes it to the map function `f` provided, and /// then takes the mapped value and wraps it back up into a new functor. /// /// /// This variant takes the passed function and partially applies it, so the result is a /// functor within the value being the partially applied function. If `Fnctr` is also an /// `Applicative`, which is often the case, you can provide further arguments to the /// partially applied function by calling `.Apply()` on the resulting functor. /// /// You can continue this until all arguments have been provided and then you'll have /// a functor within the result of the function wrapped up inside. /// /// Functor to map /// Mapping function /// Trait of the functor /// Mapped functor public static K>>>>>> map( Func f, Memo ma) where Fnctr : Functor => Fnctr.Map(x => curry(f)(x), ma); /// /// Functor map operation /// /// /// Unwraps the value within the functor, passes it to the map function `f` provided, and /// then takes the mapped value and wraps it back up into a new functor. /// /// /// This variant takes the passed function and partially applies it, so the result is a /// functor within the value being the partially applied function. If `Fnctr` is also an /// `Applicative`, which is often the case, you can provide further arguments to the /// partially applied function by calling `.Apply()` on the resulting functor. /// /// You can continue this until all arguments have been provided and then you'll have /// a functor within the result of the function wrapped up inside. /// /// Functor to map /// Mapping function /// Trait of the functor /// Mapped functor public static K>>>>>>> map( Func f, Memo ma) where Fnctr : Functor => Fnctr.Map(x => curry(f)(x), ma); /// /// Functor map operation /// /// /// Unwraps the value within the functor, passes it to the map function `f` provided, and /// then takes the mapped value and wraps it back up into a new functor. /// /// /// This variant takes the passed function and partially applies it, so the result is a /// functor within the value being the partially applied function. If `Fnctr` is also an /// `Applicative`, which is often the case, you can provide further arguments to the /// partially applied function by calling `.Apply()` on the resulting functor. /// /// You can continue this until all arguments have been provided and then you'll have /// a functor within the result of the function wrapped up inside. /// /// Functor to map /// Mapping function /// Trait of the functor /// Mapped functor public static K>>>>>>>> map( Func f, Memo ma) where Fnctr : Functor => Fnctr.Map(x => curry(f)(x), ma); /// /// Functor map operation /// /// /// Unwraps the value within the functor, passes it to the map function `f` provided, and /// then takes the mapped value and wraps it back up into a new functor. /// /// /// This variant takes the passed function and partially applies it, so the result is a /// functor within the value being the partially applied function. If `Fnctr` is also an /// `Applicative`, which is often the case, you can provide further arguments to the /// partially applied function by calling `.Apply()` on the resulting functor. /// /// You can continue this until all arguments have been provided and then you'll have /// a functor within the result of the function wrapped up inside. /// /// Functor to map /// Mapping function /// Trait of the functor /// Mapped functor public static K>>>>>>>>> map( Func f, Memo ma) where Fnctr : Functor => Fnctr.Map(x => curry(f)(x), ma); } ================================================ FILE: LanguageExt.Core/Traits/Has/Has.Trait.cs ================================================ namespace LanguageExt.Traits; /// /// Makes a value accessible via a property. This allows for _structural property access_. /// /// Higher-kind that supports access to the trait /// Type to return as the bound-value public interface Has { public static abstract K Ask { get; } } ================================================ FILE: LanguageExt.Core/Traits/Has/Has.cs ================================================ namespace LanguageExt.Traits; /// /// Strongly typed way of accessing a `Has` trait interface from an environment /// /// /// When using multiple `Has` traits, the accessor (`Env.Trait`) will cause ambiguity errors. /// This type resolves that by forcing qualification via the type parameters. It also has /// the benefit that we cache the trait interface in a readonly static, saving on allocations. /// /// Higher-kind /// The environment type from which to get the trait-interface value /// Trait interface type public static class Has where Env : Has { /// /// Cached trait interface accessor /// public static readonly K ask = Env.Ask; } ================================================ FILE: LanguageExt.Core/Traits/Hashable/Hashable.Module.cs ================================================ using System.Diagnostics.Contracts; namespace LanguageExt.Traits; public static class Hashable { /// /// Get the hash-code of the provided value /// /// Hash code of x [Pure] public static int code(A x) where A : Hashable => A.GetHashCode(x); } ================================================ FILE: LanguageExt.Core/Traits/Hashable/Hashable.Trait.cs ================================================ using System.Diagnostics.Contracts; using LanguageExt.Attributes; namespace LanguageExt; /// /// Hashable trait /// /// /// The type for which GetHashCode is defined /// [Trait("Hashable*")] public interface Hashable { /// /// Get the hash-code of the provided value /// /// Hash code of x [Pure] public static abstract int GetHashCode(A x); } ================================================ FILE: LanguageExt.Core/Traits/Identifiable/Identifiable.Module.cs ================================================ namespace LanguageExt.Traits; /// /// Identifiable module /// public static class Identifiable { /// /// Identify the structure /// /// Structure to label /// Label to apply /// Structure /// Identifier type /// Bound value type /// Identified structure public static K identify(K fa, L label) where F : Identifiable => F.Identify(fa, new Label(label)); /// /// Identify the structure /// /// Structure to label /// Label to apply /// Structure /// Identifier type /// Bound value type /// Identified structure public static K identify(K fa, Label label) where F : Identifiable => F.Identify(fa, label); } ================================================ FILE: LanguageExt.Core/Traits/Identifiable/Identifiable.Operators.cs ================================================ using LanguageExt.Traits; namespace LanguageExt; /// /// Identifiable module /// public static partial class IdentifiableExtensions { /// Structure /// Identifier type /// Bound value type extension(K self) where F : Identifiable { /// /// Identify the structure /// /// Structure to label /// Label to apply /// Identified structure public static K operator |(K fa, Label label) => F.Identify(fa, label); } } ================================================ FILE: LanguageExt.Core/Traits/Identifiable/Identifiable.Prelude.cs ================================================ namespace LanguageExt; /// /// Identifiable module /// public static partial class Prelude { /// /// Construct a label /// /// Label value /// Label value-type /// public static Label label(L value) => new(value); } ================================================ FILE: LanguageExt.Core/Traits/Identifiable/Identifiable.cs ================================================ namespace LanguageExt.Traits; /// /// Identifiable structure /// /// Structure /// Identifier type public interface Identifiable where F : Identifiable { /// /// Identify the structure /// /// Structure to label /// Label to apply /// Bound value type /// Identified structure public static abstract K Identify(K fa, Label label); } ================================================ FILE: LanguageExt.Core/Traits/Identifiable/Label.cs ================================================ namespace LanguageExt; /// /// Label to be used with Identifiable structures /// /// Label value /// Label type public readonly record struct Label(L Value); ================================================ FILE: LanguageExt.Core/Traits/Indexable/Indexable.cs ================================================ namespace LanguageExt.Traits; public interface Indexable { public static abstract Option TryGet(A ma, KEY key); public static abstract VALUE Get(A ma, KEY key); } ================================================ FILE: LanguageExt.Core/Traits/K.Extensions.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public static class KExtensions { /// /// Get the base kind type. Avoids casts mid-expression /// public static K Kind(this K fa) => fa; /// /// Get the base kind type. Avoids casts mid-expression /// public static K Kind2(this K fab) => fab; public static FA SafeCast(this K fa) where FA : K where F : Functor { try { return (FA)fa; } catch(InvalidCastException) { return (FA)F.Map(x => (A)x, fa); } } /// /// `KindT` converts a nested Kind type (inherits `K〈M, A〉`), where the inner /// type is a concrete type and not `K〈N, A〉` to the more general version - which /// allows the other `T` variant methods to work seamlessly. /// /// /// The casting of nested types is especially problematic for C#'s type-system, /// so even though this isn't ideal, it does allow for a truly generic system /// of working with any nested types as long as there's a `Functor` implementation /// for the outer type. /// /// /// /// var mx = Seq〈Option〈int〉〉(Some(1), Some(2), Some(3)); /// /// var ma = mx.KindT〈Seq, Option, Option〈int〉, int〉() /// .BindT(a => Some(a + 1)) /// .MapT(a => a + 1); /// .AsT〈Seq, Option, Option〈int〉, int〉(); /// /// /// Nested functor value /// Outer functor trait (i.e. `Seq`) /// Inner trait (i.e. `Option`) /// Concrete nested type (i.e. `Option〈int〉`) /// Concrete bound value type (i.e. `int`) /// More general version of the type that can be used with other `T` extensions, like `BindT` public static K> KindT(this K mna) where NA : K where M : Functor => M.Map(na => (K)na, mna); /// /// `AsT` converts a nested Kind type (inherits `K〈M, A〉`), where the inner type /// is a general type (`K〈N, A〉`) to its downcast concrete version. /// /// /// The casting of nested types is especially problematic for C#'s type-system, /// so even though this isn't ideal, it does allow for a truly generic system /// of working with any nested types as long as there's a `Functor` implementation /// for the outer type. /// /// /// /// var mx = Seq〈Option〈int〉〉(Some(1), Some(2), Some(3)); /// /// var ma = mx.KindT〈Seq, Option, Option〈int〉, int〉() /// .BindT(a => Some(a + 1)) /// .MapT(a => a + 1); /// .AsT〈Seq, Option, Option〈int〉, int〉(); /// /// /// Nested functor value /// Outer functor trait (i.e. `Seq`) /// Inner trait (i.e. `Option`) /// Concrete nested type (i.e. `Option〈int〉`) /// Concrete nested-monad bound value type (i.e. `int`) /// Concrete version of the general type. public static K AsT(this K> mna) where NA : K where M : Functor => M.Map(na => (NA)na, mna); } ================================================ FILE: LanguageExt.Core/Traits/K.cs ================================================ namespace LanguageExt.Traits; /// /// Arrow kind: `* -〉*` used to represent higher-kinded types. /// /// /// `K〈F, A〉` should be thought of as `F〈A〉` (where both `F` an `A` are parametric). It currently /// can't be represented in C#, so this allows us to define higher-kinded types and pass them /// around. We can then build traits that expected a `K` where the trait is tied to the `F`. /// /// For example: /// /// K〈F, A〉 where F : Functor〈F〉 /// K〈M, A〉 where M : Monad〈M〉 /// /// That means we can write generic functions that work with monads, functors, etc. /// /// Trait type /// Bound value type public interface K; /// /// Arrow kind: `* -〉* -〉*` used to represent higher-kinded types. /// /// Trait type /// Alternative value type /// Bound value type public interface K; ================================================ FILE: LanguageExt.Core/Traits/Local/Local.Module.cs ================================================ using System; using LanguageExt.Common; namespace LanguageExt.Traits; public static class Local { public static K with(Func f, K ma) where Env : Local => Env.With(f, ma); } ================================================ FILE: LanguageExt.Core/Traits/Local/Local.Trait.cs ================================================ using System; namespace LanguageExt.Traits; /// /// Creates a local environment to run a computation /// /// Structure trait /// The value extracted from an environment public interface Local : Has { public static abstract K With(Func f, K ma); } ================================================ FILE: LanguageExt.Core/Traits/Maybe Traits/MonadIO/MonadIO.Trait.cs ================================================ using System; using LanguageExt.Common; namespace LanguageExt.Traits; public static partial class Maybe { /// /// Monad that is either the IO monad or a transformer with the IO monad in its stack /// /// Self-referring trait public interface MonadIO where M : MonadIO { /// /// Lifts the IO monad into a monad transformer stack. /// /// IO computation to lift /// Bound value type /// The outer monad with the IO monad lifted into it public static virtual K LiftIOMaybe(K ma) => M.LiftIOMaybe(ma.As()); /// /// Lifts the IO monad into a monad transformer stack. /// /// /// IMPLEMENTATION REQUIRED: If this method isn't overloaded in this monad /// or any monad in the stack on the way to the inner-monad, then it will throw /// an exception. /// /// This isn't ideal, it appears to be the only way to achieve this /// kind of functionality in C# without resorting to magic. /// /// IO computation to lift /// Bound value type /// The outer monad with the IO monad lifted into it /// If this method isn't overloaded in /// the inner monad or any monad in the stack on the way to the inner-monad, /// then it will throw an exception. public static virtual K LiftIOMaybe(IO ma) => throw new ExceptionalException(Errors.LiftIONotSupported); } } ================================================ FILE: LanguageExt.Core/Traits/Maybe Traits/MonadUnliftIO/MonadUnliftIO.Extensions.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public static class MaybeMonadUnliftIOExtensions { /// /// Convert an action in `ma`to an action in `IO`. /// public static K> ToIO(this K ma) where M : MonadUnliftIO => M.ToIOMaybe(ma); /// /// Convert an action in `ma`to an action in `IO`. /// public static K MapIO(this K ma, Func, IO> f) where M : MonadUnliftIO => M.MapIOMaybe(ma, f); /// /// Map the underlying IO monad /// public static K MapIO(this Func, IO> f, K ma) where M : MonadUnliftIO => M.MapIOMaybe(ma, f); /// /// Convert an action in `ma`to an action in `IO`. /// public static K> ToIOMaybe(this K ma) where M : MonadIO => M.ToIOMaybe(ma); /// /// Convert an action in `ma`to an action in `IO`. /// public static K MapIOMaybe(this K ma, Func, IO> f) where M : MonadIO => M.MapIOMaybe(ma, f); /// /// Map the underlying IO monad /// public static K MapIOMaybe(this Func, IO> f, K ma) where M : MonadIO => M.MapIOMaybe(ma, f); } ================================================ FILE: LanguageExt.Core/Traits/Maybe Traits/MonadUnliftIO/MonadUnliftIO.Trait.cs ================================================ using System; using LanguageExt.Common; namespace LanguageExt.Traits; public static partial class Maybe { /// /// Monad that is either the IO monad or a transformer with the IO monad in its stack /// /// Self-referring trait public interface MonadUnliftIO : Maybe.MonadIO where M : MonadUnliftIO, Traits.Monad { /// /// Extract the IO monad from within the M monad (usually as part of a monad-transformer stack). /// /// /// IMPLEMENTATION REQUIRED: If this method isn't overloaded in this monad /// or any monad in the stack on the way to the inner-monad, then it will throw /// an exception. /// /// This isn't ideal, it appears to be the only way to achieve this /// kind of functionality in C# without resorting to magic. /// /// If this method isn't overloaded in /// the inner monad or any monad in the stack on the way to the inner-monad, /// then it will throw an exception. public static virtual K> ToIOMaybe(K ma) => throw new ExceptionalException(Errors.ToIONotSupported); /// /// Extract the IO monad from within the `M` monad (usually as part of a monad-transformer stack). Then perform /// a mapping operation on the IO action before lifting the IO back into the `M` monad. /// public static virtual K MapIOMaybe(K ma, Func, IO> f) => M.ToIOMaybe(ma).Bind(io => M.LiftIOMaybe(f(io))); /// /// Queue this IO operation to run on the thread-pool. /// /// Maximum time that the forked IO operation can run for. `None` for no timeout. /// Returns a `ForkIO` data-structure that contains two IO effects that can be used to either cancel /// the forked IO operation or to await the result of it. /// public static virtual K> ForkIOMaybe(K ma, Option timeout = default) => M.MapIOMaybe(ma, io => io.Fork(timeout)); /// /// Await a forked operation /// public static virtual K AwaitMaybe(K> ma) => M.MapIOMaybe(ma, io => io.Bind(f => f.Await)); /// /// Creates a local cancellation environment /// /// /// A local cancellation environment stops other IO computations, that rely on the same /// environmental cancellation token, from being taken down by a regional cancellation. /// /// If an `IO.cancel` is invoked locally, then it will still create an exception that /// propagates upwards and so catching cancellations is still important. /// /// Computation to run within the local context /// Bound value /// Result of the computation public static virtual K LocalIOMaybe(K ma) => M.MapIOMaybe(ma, io => io.Local()); /// /// Make this IO computation run on the `SynchronizationContext` that was captured at the start /// of the IO chain (i.e. the one embedded within the `EnvIO` environment that is passed through /// all IO computations) /// public static virtual K PostIOMaybe(K ma) => M.MapIOMaybe(ma, io => io.Post()); /// /// Timeout operation if it takes too long /// public static virtual K TimeoutIOMaybe(K ma, TimeSpan timeout) => M.MapIOMaybe(ma, io => io.Timeout(timeout)); /// /// The IO monad tracks resources automatically; this creates a local resource environment /// to run this computation in. Once the computation is completed, any resources acquired /// are automatically released. Imagine this as the ultimate `using` statement. /// public static virtual K BracketIOMaybe(K ma) => M.MapIOMaybe(ma, io => io.Bracket()); /// /// When acquiring, using, and releasing various resources, it can be quite convenient to write a function to manage /// the acquisition and releasing, taking a function of the acquired value that specifies an action to be performed /// in between. /// /// Resource acquisition /// Function to use the acquired resource /// Function to invoke to release the resource public static virtual K BracketIOMaybe( K Acq, Func> Use, Func> Fin) => M.MapIOMaybe(Acq, io => io.Bracket(Use, Fin)); /// /// When acquiring, using, and releasing various resources, it can be quite convenient to write a function to manage /// the acquisition and releasing, taking a function of the acquired value that specifies an action to be performed /// in between. /// /// Resource acquisition /// Function to use the acquired resource /// Function to run to handle any exceptions /// Function to invoke to release the resource public static virtual K BracketIOMaybe( K Acq, Func> Use, Func> Catch, Func> Fin) => M.MapIOMaybe(Acq, io => io.Bracket(Use, Catch, Fin)); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Repeating the effect // /// /// Keeps repeating the computation forever, or until an error occurs /// /// /// Any resources acquired within a repeated IO computation will automatically be released. This also means you can't /// acquire resources and return them from within a repeated computation. /// /// The result of the last invocation public static virtual K RepeatIOMaybe(K ma) => M.MapIOMaybe(ma, io => io.Repeat()); /// /// Keeps repeating the computation until the scheduler expires, or an error occurs /// /// /// Any resources acquired within a repeated IO computation will automatically be released. This also means you can't /// acquire resources and return them from within a repeated computation. /// /// Scheduler strategy for repeating /// The result of the last invocation public static virtual K RepeatIOMaybe( K ma, Schedule schedule) => M.MapIOMaybe(ma, io => io.Repeat(schedule)); /// /// Keeps repeating the computation until the predicate returns false, or an error occurs /// /// /// Any resources acquired within a repeated IO computation will automatically be released. This also means you can't /// acquire resources and return them from within a repeated computation. /// /// Keep repeating while this predicate returns `true` for each computed value /// The result of the last invocation public static virtual K RepeatWhileIOMaybe( K ma, Func predicate) => M.MapIOMaybe(ma, io => io.RepeatWhile(predicate)); /// /// Keeps repeating the computation, until the scheduler expires, or the predicate returns false, or an error occurs /// /// /// Any resources acquired within a repeated IO computation will automatically be released. This also means you can't /// acquire resources and return them from within a repeated computation. /// /// Scheduler strategy for repeating /// Keep repeating while this predicate returns `true` for each computed value /// The result of the last invocation public static virtual K RepeatWhileIOMaybe( K ma, Schedule schedule, Func predicate) => M.MapIOMaybe(ma, io => io.RepeatWhile(schedule, predicate)); /// /// Keeps repeating the computation until the predicate returns true, or an error occurs /// /// /// Any resources acquired within a repeated IO computation will automatically be released. This also means you can't /// acquire resources and return them from within a repeated computation. /// /// Keep repeating until this predicate returns `true` for each computed value /// The result of the last invocation public static virtual K RepeatUntilIOMaybe( K ma, Func predicate) => M.MapIOMaybe(ma, io => io.RepeatUntil(predicate)); /// /// Keeps repeating the computation until the scheduler expires, or the predicate returns true, or an error occurs /// /// /// Any resources acquired within a repeated IO computation will automatically be released. This also means you can't /// acquire resources and return them from within a repeated computation. /// /// Scheduler strategy for repeating /// Keep repeating until this predicate returns `true` for each computed value /// The result of the last invocation public static virtual K RepeatUntilIOMaybe( K ma, Schedule schedule, Func predicate) => M.MapIOMaybe(ma, io => io.RepeatUntil(schedule, predicate)); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Retrying the effect when it fails // /// /// Retry if the IO computation fails /// /// /// This variant will retry forever /// /// /// Any resources acquired within a retrying IO computation will automatically be released *if* the operation fails. /// So, successive retries will not grow the acquired resources on each retry iteration. Any successful operation that /// acquires resources will have them tracked in the usual way. /// public static virtual K RetryIOMaybe(K ma) => M.MapIOMaybe(ma, io => io.Retry()); /// /// Retry if the IO computation fails /// /// /// This variant will retry until the schedule expires /// /// /// Any resources acquired within a retrying IO computation will automatically be released *if* the operation fails. /// So, successive retries will not grow the acquired resources on each retry iteration. Any successful operation that /// acquires resources will have them tracked in the usual way. /// public static virtual K RetryIOMaybe( K ma, Schedule schedule) => M.MapIOMaybe(ma, io => io.Retry(schedule)); /// /// Retry if the IO computation fails /// /// /// This variant will keep retrying whilst the predicate returns `true` for the error generated at each iteration; /// at which point the last raised error will be thrown. /// /// /// Any resources acquired within a retrying IO computation will automatically be released *if* the operation fails. /// So, successive retries will not grow the acquired resources on each retry iteration. Any successful operation that /// acquires resources will have them tracked in the usual way. /// public static virtual K RetryWhileIOMaybe( K ma, Func predicate) => M.MapIOMaybe(ma, io => io.RetryWhile(predicate)); /// /// Retry if the IO computation fails /// /// /// This variant will keep retrying whilst the predicate returns `true` for the error generated at each iteration; /// or, until the schedule expires; at which point the last raised error will be thrown. /// /// /// Any resources acquired within a retrying IO computation will automatically be released *if* the operation fails. /// So, successive retries will not grow the acquired resources on each retry iteration. Any successful operation that /// acquires resources will have them tracked in the usual way. /// public static virtual K RetryWhileIOMaybe( K ma, Schedule schedule, Func predicate) => M.MapIOMaybe(ma, io => io.RetryWhile(schedule, predicate)); /// /// Retry if the IO computation fails /// /// /// This variant will keep retrying until the predicate returns `true` for the error generated at each iteration; /// at which point the last raised error will be thrown. /// /// /// Any resources acquired within a retrying IO computation will automatically be released *if* the operation fails. /// So, successive retries will not grow the acquired resources on each retry iteration. Any successful operation that /// acquires resources will have them tracked in the usual way. /// public static virtual K RetryUntilIOMaybe( K ma, Func predicate) => M.MapIOMaybe(ma, io => io.RetryUntil(predicate)); /// /// Retry if the IO computation fails /// /// /// This variant will keep retrying until the predicate returns `true` for the error generated at each iteration; /// or, until the schedule expires; at which point the last raised error will be thrown. /// /// /// Any resources acquired within a retrying IO computation will automatically be released *if* the operation fails. /// So, successive retries will not grow the acquired resources on each retry iteration. Any successful operation that /// acquires resources will have them tracked in the usual way. /// public static virtual K RetryUntilIOMaybe( K ma, Schedule schedule, Func predicate) => M.MapIOMaybe(ma, io => io.RetryUntil(schedule, predicate)); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Folding // public static virtual K FoldIOMaybe( K ma, Schedule schedule, S initialState, Func folder) => M.MapIOMaybe(ma, io => io.Fold(schedule, initialState, folder)); public static virtual K FoldIOMaybe( K ma, S initialState, Func folder) => M.MapIOMaybe(ma, io => io.Fold(initialState, folder)); public static virtual K FoldWhileIOMaybe( K ma, Schedule schedule, S initialState, Func folder, Func stateIs) => M.MapIOMaybe(ma, io => io.FoldWhile(schedule, initialState, folder, stateIs)); public static virtual K FoldWhileIOMaybe( K ma, S initialState, Func folder, Func stateIs) => M.MapIOMaybe(ma, io => io.FoldWhile(initialState, folder, stateIs)); public static virtual K FoldWhileIOMaybe( K ma, Schedule schedule, S initialState, Func folder, Func valueIs) => M.MapIOMaybe(ma, io => io.FoldWhile(schedule, initialState, folder, valueIs)); public static virtual K FoldWhileIOMaybe( K ma, S initialState, Func folder, Func valueIs) => M.MapIOMaybe(ma, io => io.FoldWhile(initialState, folder, valueIs)); public static virtual K FoldWhileIOMaybe( K ma, Schedule schedule, S initialState, Func folder, Func<(S State, A Value), bool> predicate) => M.MapIOMaybe(ma, io => io.FoldWhile(schedule, initialState, folder, predicate)); public static virtual K FoldWhileIOMaybe( K ma, S initialState, Func folder, Func<(S State, A Value), bool> predicate) => M.MapIOMaybe(ma, io => io.FoldWhile(initialState, folder, predicate)); public static virtual K FoldUntilIOMaybe( K ma, Schedule schedule, S initialState, Func folder, Func stateIs) => M.MapIOMaybe(ma, io => io.FoldUntil(schedule, initialState, folder, stateIs)); public static virtual K FoldUntilIOMaybe( K ma, S initialState, Func folder, Func stateIs) => M.MapIOMaybe(ma, io => io.FoldUntil(initialState, folder, stateIs)); public static virtual K FoldUntilIOMaybe( K ma, Schedule schedule, S initialState, Func folder, Func valueIs) => M.MapIOMaybe(ma, io => io.FoldUntil(schedule, initialState, folder, valueIs)); public static virtual K FoldUntilIOMaybe( K ma, S initialState, Func folder, Func valueIs) => M.MapIOMaybe(ma, io => io.FoldUntil(initialState, folder, valueIs)); public static virtual K FoldUntilIOMaybe( K ma, S initialState, Func folder, Func<(S State, A Value), bool> predicate) => M.MapIOMaybe(ma, io => io.FoldUntil(initialState, folder, predicate)); public static virtual K FoldUntilIOMaybe( K ma, Schedule schedule, S initialState, Func folder, Func<(S State, A Value), bool> predicate) => M.MapIOMaybe(ma, io => io.FoldUntil(schedule, initialState, folder, predicate)); } } ================================================ FILE: LanguageExt.Core/Traits/Monads/Monad/Monad.Extensions.cs ================================================ using System; using LanguageExt.Traits; using System.Diagnostics.Contracts; namespace LanguageExt; public static partial class MonadExtensions { /// /// Monad bind operation /// /// Monadic bind function /// Initial bound value type /// Intermediate bound value type /// M〈B〉 [Pure] public static K Bind( this K ma, Func> f) where M : Monad => M.Bind(ma, f); /// /// Monad bind operation /// /// Monadic bind function /// Initial bound value type /// Intermediate bound value type /// M〈B〉 public static K Bind( this K ma, Func> f) where M : Functor => M.Map(x => f(x).Value, ma); /// /// Monad bind operation /// /// Monadic bind function /// Projection function /// Initial bound value type /// Intermediate bound value type /// Target bound value type /// M〈C〉 public static K SelectMany( this K ma, Func> bind, Func project) where M : Monad => M.Bind(ma, a => M.Map(b => project(a, b) , bind(a))); /// /// Monad bind operation /// /// Monadic bind function /// Projection function /// Initial bound value type /// Intermediate bound value type /// Target bound value type /// M〈C〉 public static K SelectMany( this K ma, Func> bind, Func project) where M : Functor => M.Map(a => project(a, bind(a).Value), ma); /// /// Monad bind operation /// /// Monadic bind function /// Projection function /// Monad trait /// Initial bound value type /// Target bound value type /// M〈C〉 public static K SelectMany( this K ma, Func> bind, Func project) where M : Monad, Fallible => M.Bind(ma, a => bind(a) switch { { Flag: true } => M.Pure(project(a, default)), var guard => M.Fail(guard.OnFalse()) }); /// /// Monad bind operation /// /// Monadic bind function /// Projection function /// Monad trait /// Initial bound value type /// Target bound value type /// M〈C〉 public static K SelectMany( this K ma, Func, Unit>> bind, Func project) where M : Monad, Fallible => M.Bind(ma, a => bind(a) switch { { Flag: true } => M.Pure(project(a, default)), var guard => M.Fail(guard.OnFalse().Value) }); /// /// Monadic join operation /// /// /// Monad trait /// Bound value type /// Joined monad public static K Flatten(this K> mma) where M : Monad => M.Bind(mma, Prelude.identity); /// Computation to recursively run /// Monad /// Bound value type extension(K ma) where M : Monad { /// /// This is equivalent to the infinite loop below without the stack-overflow issues: /// /// K〈M, A〉go => /// ma.Bind(_ => go); /// /// /// 'Result' type, there will never be a result of `B`, but the monad rules may exit the loop /// with an alternative value; and so `B` is still valid /// A looped computation [Pure] public K Forever() => Monad.forever(ma); /// /// This is equivalent to the infinite loop below without the stack-overflow issues: /// /// K〈M, A〉go => /// ma.Bind(_ => go); /// /// /// 'Result' type, there will never be a result of `B`, but the monad rules may exit the loop /// with an alternative value; and so `B` is still valid /// A looped computation [Pure] public K Forever() => Monad.forever(ma); /// /// Running the monadic computation `ma` a fixed number of times (`count`) collecting the results /// /// Number of times to replicate monadic computation /// A lifted iterable of values collected [Pure] public K> Replicate(int count) => Monad.replicate(ma, count); /// /// Keep running the monadic computation `ma` collecting the result values until a result value /// yielded triggers a `true` value when passed to the `f` predicate /// /// Predicate /// A lifted iterable of values collected [Pure] public K> AccumUntil(Func f) => Monad.accumWhile(ma, f); /// /// Keep running the monadic computation `ma` collecting the result values until a result value /// yielded triggers a `true` value when passed to the `f` predicate /// /// Predicate /// A lifted iterable of values collected [Pure] public K> AccumUntilM(Func> f) => Monad.accumUntilM(ma, f); /// /// Keep running the monadic computation `ma` collecting the result values until a result value /// yielded triggers a `true` value when passed to the `f` predicate /// /// Predicate /// A lifted iterable of values collected [Pure] public K> AccumWhile(Func f) => Monad.accumWhile(ma, f); /// /// Keep running the monadic computation `ma` collecting the result values until a result value /// yielded triggers a `true` value when passed to the `f` predicate /// /// Predicate /// A lifted iterable of values collected [Pure] public K> AccumWhileM(Func> f) => Monad.accumWhileM(ma, f); } } ================================================ FILE: LanguageExt.Core/Traits/Monads/Monad/Monad.ExtensionsT.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; /// /// Monad module /// public static partial class MonadExtensions { /// /// Runs a monadic bind operation on the nested monads /// /// /// If you're working with an inner monad that is concrete then you will first need to /// call `KindT` to cast the monad to a more general `K` version. This enables the /// `T` variant extensions (like `BindT`, `MapT, etc.) to work without providing /// excessive generic arguments all the way down the chain. /// /// /// /// var mx = Seq〈Option〈int〉〉(Some(1), Some(2), Some(3)); /// /// var ma = mx.MapT(a => a + 1); /// /// /// Nested monadic value /// Bind function /// Outer monad trait /// Inner monad trait /// Input bound value /// Output bound value /// Mapped value public static K> BindT(this K> mna, Func> f) where M : Functor where N : Monad => M.Map(na => N.Bind(na, f), mna); /// /// Runs a monadic bind operation on the nested monads /// /// /// If you're working with an inner monad that is concrete then you will first need to /// call `KindT` to cast the monad to a more general `K` version. This enables the /// `T` variant extensions (like `BindT`, `MapT, etc.) to work without providing /// excessive generic arguments all the way down the chain. /// /// /// /// var mx = Seq〈Option〈int〉〉(Some(1), Some(2), Some(3)); /// /// var ma = mx.BindT(a => Seq(Some(a + 1))) /// .MapT(a => a + 1);; /// /// /// Nested monadic value /// Bind function /// Outer monad trait /// Inner monad trait /// Input bound value /// Output bound value /// Mapped value public static K> BindT(this K> mna, Func>> f) where M : Monad where N : Monad, Traversable => mna.Bind(na => na.Map(f).SequenceM()).Map(nna => nna.Flatten()); } ================================================ FILE: LanguageExt.Core/Traits/Monads/Monad/Monad.Laws.cs ================================================ using System; using LanguageExt.Common; using static LanguageExt.Prelude; namespace LanguageExt.Traits; /// /// Functions that test that monad laws hold for the `F` monad provided. /// /// /// * Homomorphism /// * Identity /// * Interchange /// * Monad /// * Composition /// /// /// NOTE: `Equals` must be implemented for the `K〈F, *〉` derived-type, so that the laws /// can be proven to be true. If your monad doesn't have `Equals` then you must provide /// the optional `equals` parameter so that the equality of outcomes can be tested. /// /// Functor type public static class MonadLaw where F : Monad { /// /// Assert that the monad laws hold /// /// /// NOTE: `Equals` must be implemented for the `K〈F, *〉` derived-type, so that the laws /// can be proven to be true. If your monad doesn't have `Equals` then you must provide /// the optional `equals` parameter so that the equality of outcomes can be tested. /// public static Unit assert(Func, K, bool>? equals = null) => validate(equals) .IfFail(errors => errors.Throw()); /// /// Validate that the monad laws hold /// /// /// NOTE: `Equals` must be implemented for the `K〈F, *〉` derived-type, so that the laws /// can be proven to be true. If your monad doesn't have `Equals` then you must provide /// the optional `equals` parameter so that the equality of outcomes can be tested. /// public static Validation validate(Func, K, bool>? equals = null) { equals ??= (fa, fb) => fa.Equals(fb); return ApplicativeLaw.validate(equals) >>> leftIdentityLaw(equals) >>> rightIdentityLaw(equals) >>> associativityLaw(equals) >>> recurIsSameAsBind(equals); } public static Validation recurIsSameAsBind(Func, K, bool>? equals = null) { var example = Seq(1, 2, 3, 4, 5, 6, 7, 8, 9); var result1 = Monad.recur((0, example), rec); var result2 = Monad.unsafeRecur((0, example), rec); var result3 = bind(example); equals ??= (fa, fb) => fa.Equals(fb); if(!equals(result1, result2)) throw new Exception("Bug in MonadLaw or Monad.unsafeRecur. Contact language-ext maintainer via the repo."); if (!equals(result1, result3)) { return Validation.Fail( Error.New($"Monad trait implementation for {typeof(F).Name}.Recur gives a different " + $"result to the equivalent recursive {typeof(F).Name}.Bind. This suggests " + $"an implementation bug, most likely in {typeof(F).Name}.Recur, but possibly " + $"in {typeof(F).Name}.Bind.")); } return Validation.Success(unit); K Values), int>> rec((int Total, Seq Values) pair) => pair.Values switch { [] => F.Pure(Next.Done<(int, Seq), int>(pair.Total)), var (x, xs) => F.Pure(Next.Loop<(int, Seq), int>((pair.Total + x, xs))) }; K bind(Seq values) => values switch { [var x] => F.Pure(x), var (x, xs) => bind(xs) * (t => x + t) }; } /// /// Validate the left-identity law /// /// /// NOTE: `Equals` must be implemented for the `K〈F, *〉` derived-type, so that the laws /// can be proven to be true. If your monad doesn't have `Equals` then you must provide /// the optional `equals` parameter so that the equality of outcomes can be tested. /// public static Validation leftIdentityLaw(Func, K, bool> equals) { var a = 100; var h = (int x) => F.Pure(x); var lhs = F.Pure(a).Bind(h); var rhs = h(a); return equals(lhs, rhs) ? unit : Error.New($"Monad left-identity law does not hold for {typeof(F).Name}"); } /// /// Validate the right-identity law /// /// /// NOTE: `Equals` must be implemented for the `K〈F, *〉` derived-type, so that the laws /// can be proven to be true. If your monad doesn't have `Equals` then you must provide /// the optional `equals` parameter so that the equality of outcomes can be tested. /// public static Validation rightIdentityLaw(Func, K, bool> equals) { var a = 100; var m = F.Pure(a); var lhs = m.Bind(F.Pure); var rhs = m; return equals(lhs, rhs) ? unit : Error.New($"Monad right-identity law does not hold for {typeof(F).Name}"); } /// /// Validate the associativity law /// /// /// NOTE: `Equals` must be implemented for the `K〈F, *〉` derived-type, so that the laws /// can be proven to be true. If your monad doesn't have `Equals` then you must provide /// the optional `equals` parameter so that the equality of outcomes can be tested. /// public static Validation associativityLaw(Func, K, bool> equals) { var m = F.Pure(100); var g = (int x) => F.Pure(x * 10); var h = (int x) => F.Pure(x + 83); var lhs = m.Bind(g).Bind(h); var rhs = m.Bind(x => g(x).Bind(h)); return equals(lhs, rhs) ? unit : Error.New($"Monad associativity law does not hold for {typeof(F).Name}"); } } ================================================ FILE: LanguageExt.Core/Traits/Monads/Monad/Monad.Module.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics.Contracts; using System.Runtime.CompilerServices; using System.Threading; using LanguageExt.Common; namespace LanguageExt.Traits; /// /// Monad module /// public static partial class Monad { [Pure] public static K pure(A value) where M : Monad => M.Pure(value); [Pure] public static K flatten(K> mma) where M : Monad => M.Flatten(mma); [Pure] public static K bind(K ma, Func> f) where M : Monad => M.Bind(ma, f); [Pure] public static MB bind(K ma, Func f) where MB : K where M : Monad => (MB)bind(ma, x => f(x)); /// /// This is equivalent to the infinite loop below without the stack-overflow issues: /// /// K〈M, A〉go => /// ma.Bind(_ => go); /// /// /// Computation to recursively run /// Monad /// Bound value type /// 'Result' type, there will never be a result of `B`, but the monad rules may exit the loop /// with an alternative value; and so `B` is still valid /// A looped computation [Pure] public static K forever(K ma) where M : Monad => forever(ma); /// /// This is equivalent to the infinite loop below without the stack-overflow issues: /// /// K〈M, A〉go => /// ma.Bind(_ => go); /// /// /// Monadic computation to recursively run /// Monad /// Bound value type /// 'Result' type, there will never be a result of `B`, but the monad rules may exit the loop /// with an alternative value; and so `B` is still valid /// A looped computation [Pure] public static K forever(K ma) where M : Monad => recur(default!, _ => ma.Map(_ => Next.UnsafeDefault)); /// /// Running the monadic computation `ma` a fixed number of times (`count`) collecting the results /// /// Monadic computation to repeatedly run /// Number of times to replicate monadic computation /// Monad /// Bound value type /// A lifted iterable of values collected [Pure] public static K> replicate(K ma, int count) where M : Monad => recur Items, int Remain), Iterable>( ([], count), acc => ma * (a => acc.Remain > 0 ? Next.Loop<(Iterable Items, int Remain), Iterable>((acc.Items.Add(a), acc.Remain - 1)) : Next.Done<(Iterable Items, int Remain), Iterable>(acc.Items.Add(a)))); /// /// Keep running the monadic computation `ma` collecting the result values until a result value /// yielded triggers a `true` value when passed to the `f` predicate /// /// Monadic computation to repeatedly run /// Predicate /// Monad /// Bound value type /// A lifted iterable of values collected [Pure] public static K> accumUntil(K ma, Func f) where M : Monad => recur, Iterable>( [], acc => ma * (a => f(a) ? Next.Done, Iterable>(acc) : Next.Loop, Iterable>(acc.Add(a)))); /// /// Keep running the monadic computation `ma` collecting the result values until a result value /// yielded triggers a `true` value when passed to the `f` predicate /// /// Monadic computation to repeatedly run /// Predicate /// Monad /// Bound value type /// A lifted iterable of values collected [Pure] public static K> accumUntilM(K ma, Func> f) where M : Monad => recur, Iterable>( [], acc => ma >> (a => f(a) * (r => r ? Next.Done, Iterable>(acc) : Next.Loop, Iterable>(acc.Add(a))))); /// /// Keep running the monadic computation `ma` collecting the result values until a result value /// yielded triggers a `true` value when passed to the `f` predicate /// /// Monadic computation to repeatedly run /// Predicate /// Monad /// Bound value type /// A lifted iterable of values collected [Pure] public static K> accumWhile(K ma, Func f) where M : Monad => recur, Iterable>( [], acc => ma * (a => f(a) ? Next.Loop, Iterable>(acc.Add(a)) : Next.Done, Iterable>(acc))); /// /// Keep running the monadic computation `ma` collecting the result values until a result value /// yielded triggers a `true` value when passed to the `f` predicate /// /// Monadic computation to repeatedly run /// Predicate /// Monad /// Bound value type /// A lifted iterable of values collected [Pure] public static K> accumWhileM(K ma, Func> f) where M : Monad => recur, Iterable>( [], acc => ma >> (a => f(a) * (r => r ? Next.Loop, Iterable>(acc.Add(a)) : Next.Done, Iterable>(acc)))); /// /// Lift a Next.Done value into the monad /// [Pure] public static K> done(B value) where M : Monad => M.Pure(Next.Done(value)); /// /// Lift a Next.Done value into the monad /// [Pure] public static K> loop(A value) where M : Monad => M.Pure(Next.Loop(value)); /// /// Allow for tail-recursion by using a trampoline function that returns a monad with the bound value /// wrapped by `Next`, which enables decision-making about whether to keep the computation going or not. /// /// /// It is expected that the implementor of the `Monad` trait has made a 'stack-neutral' implementation /// of `Monad.Recur` /// /// Initial value to start the recursive process /// Bind function that returns a monad with the bound value wrapped by `Next`, which /// enables decision-making about whether to recur, or not. /// Monad type /// Loop value /// Done value /// Monad structure [Pure] public static K recur(A value, Func>> f) where M : Monad => M.Recur(value, f); /// /// This is a default implementation of `Monad.Recur` that doesn't use the trampoline. /// It's here to use as a placeholder implementation for the trampoline version and for types /// where it's unlikely that recursion will be a problem. NOTE: Using this isn't declarative, /// and the users of `Monad.recur` would rightly be miffed if an implementation yielded a /// stack-overflow, so use this function with caution. /// /// Initial value to start the recursive process /// Bind function that returns a monad with the bound value wrapped by `Next`, which /// enables decision-making about whether to recur, or not. /// Monad type /// Loop value /// Done value /// Monad structure /// [Pure] public static K unsafeRecur(A value, Func>> f) where M : Monad => f(value).Bind(n => n switch { { IsLoop: true, Loop: var v } => unsafeRecur(v, f), { IsDone: true, Done: var v } => M.Pure(v), _ => throw new BottomException() }); /// /// Allow for tail-recursion by using a trampoline function that returns a monad with the bound value /// wrapped by `Next`, which enables decision-making about whether to keep the computation going or not. /// /// /// This is a handy pre-built version of `Monad.Recur` that works with `Iterable` (a lazy stream that supports /// both synchronicity and asynchronicity). The `Natural` and `CoNatural` constraints allow any type that can /// convert to and from `Iterable` to gain this prebuilt stack-protecting recursion. /// /// Initial value to start the recursive process /// Bind function that returns a monad with the bound value wrapped by `Next`, which /// enables decision-making about whether to recur, or not. /// Monad type /// Loop value /// Done value /// Monad structure [Pure] public static K iterableRecur(A value, Func>> f) where M : Natural, CoNatural { var iterable = Iterable.createRange(IO.lift(e => go(e.Token))); return CoNatural.transform(iterable); async IAsyncEnumerable go([EnumeratorCancellation] CancellationToken token) { List values = [value]; List next = []; while (true) { foreach (var x in values) { var iterable1 = Natural.transform>(f(x)).As().AsAsyncEnumerable(token); await foreach (var mb in iterable1) { if (mb.IsDone) { yield return mb.Done; } else { next.Add(mb.Loop); } } } if (next.Count == 0) { break; } else { (values, next) = (next, values); next.Clear(); } } } } /// /// Allow for tail-recursion by using a trampoline function that returns an enumerable monad with the bound value /// wrapped by `Next`, which enables decision-making about whether to keep the computation going or not. /// /// /// This is a handy pre-built version of `Monad.Recur` that works with `IEnumerable` (a lazy stream that supports /// synchronicity only) /// /// Initial value to start the recursive process /// Bind function that returns a monad with the bound value wrapped by `Next`, which /// enables decision-making about whether to recur, or not. /// Monad type /// Loop value /// Done value /// Monad structure [Pure] public static IEnumerable enumerableRecur(A value, Func>> f) { List values = [value]; List next = []; while (true) { foreach (var x in values) { var iterable1 = f(x); foreach (var mb in iterable1) { if (mb.IsDone) { yield return mb.Done; } else { next.Add(mb.Loop); } } } if (next.Count == 0) { break; } else { (values, next) = (next, values); next.Clear(); } } } /// /// When the predicate evaluates to `true`, compute `Then` /// /// Predicate /// Computation /// Monad /// Unit monad [Pure] public static K when(K Pred, K Then) where M : Monad => Pred.Bind(f => Applicative.when(f, Then)); /// /// When the predicate evaluates to `true`, compute `Then` /// /// Predicate /// Computation /// Monad /// Unit monad [Pure] public static K when(K Pred, Pure Then) where M : Monad => Pred.Bind(f => Applicative.when(f, M.Pure(Prelude.unit))); /// /// When the predicate evaluates to `false`, compute `Then` /// /// Predicate /// Computation /// Monad /// Unit monad [Pure] public static K unless(K Pred, K Then) where M : Monad => Pred.Bind(f => Applicative.unless(f, Then)); /// /// When the predicate evaluates to `false`, compute `Then` /// /// Predicate /// Computation /// Monad /// Unit monad [Pure] public static K unless(K Pred, Pure Then) where M : Monad => Pred.Bind(f => Applicative.unless(f, M.Pure(Prelude.unit))); /// /// Compute the predicate and depending on its state compute `Then` or `Else` /// /// Predicate /// Computation /// Computation /// Monad /// Unit monad [Pure] public static K iff(K Pred, K Then, K Else) where M : Monad => Pred.Bind(f => f ? Then : Else); /// /// Compute the predicate and depending on its state compute `Then` or `Else` /// /// Predicate /// Computation /// Computation /// Monad /// Unit monad [Pure] public static K iff(K Pred, K Then, K Else) where M : Monad => Pred.Bind(f => f ? Then : M.LiftIOMaybe(Else)); /// /// Compute the predicate and depending on its state compute `Then` or `Else` /// /// Predicate /// Computation /// Computation /// Monad /// Unit monad [Pure] public static K iff(K Pred, K Then, K Else) where M : Monad => Pred.Bind(f => f ? M.LiftIOMaybe(Then) : Else); /// /// Compute the predicate and depending on its state compute `Then` or `Else` /// /// Predicate /// Computation /// Computation /// Monad /// Unit monad [Pure] public static K iff(K Pred, K Then, K Else) where M : Monad => Pred.Bind(f => f ? M.LiftIOMaybe(Then) : M.LiftIOMaybe(Else)); /// /// Compute the predicate and depending on its state compute `Then` or `Else` /// /// Predicate /// Computation /// Computation /// Monad /// Unit monad [Pure] public static K iff(K Pred, K Then, Pure Else) where M : Monad => Pred.Bind(f => f ? Then : M.Pure(Else.Value)); /// /// Compute the predicate and depending on its state compute `Then` or `Else` /// /// Predicate /// Computation /// Computation /// Monad /// Unit monad [Pure] public static K iff(K Pred, Pure Then, K Else) where M : Monad => Pred.Bind(f => f ? M.Pure(Then.Value) : Else); /// /// Compute the predicate and depending on its state compute `Then` or `Else` /// /// Predicate /// Computation /// Computation /// Monad /// Unit monad [Pure] public static K iff(K Pred, Pure Then, Pure Else) where M : Monad => Pred.Bind(f => f ? M.Pure(Then.Value) : M.Pure(Else.Value)); /// /// Compute the predicate and depending on its state compute `Then` or `Else` /// /// Predicate /// Computation /// Computation /// Monad /// Unit monad [Pure] public static K iff(K Pred, Pure Then, K Else) where M : Monad => Pred.Bind(f => f ? M.Pure(Then.Value) : M.LiftIOMaybe(Else)); /// /// Compute the predicate and depending on its state compute `Then` or `Else` /// /// Predicate /// Computation /// Computation /// Monad /// Unit monad [Pure] public static K iff(K Pred, K Then, Pure Else) where M : Monad => Pred.Bind(f => f ? M.LiftIOMaybe(Then) : M.Pure(Else.Value)); } ================================================ FILE: LanguageExt.Core/Traits/Monads/Monad/Monad.Operators.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public static partial class MonadExtensions { extension(K self) where M : Monad { /// /// Monad bind operator /// /// Monad to bind /// Binding function /// Mapped monad public static K operator >> (K ma, Func> f) => ma.Bind(f); /// /// Sequentially compose two actions, discarding any value produced by the first, like sequencing operators /// (such as the semicolon) in C#. /// /// First action to run /// Second action to run /// Result of the second action public static K operator >> (K lhs, K rhs) => lhs >> (_ => rhs); /// /// Sequentially compose two actions, discarding any value produced by the second, like sequencing operators /// (such as the semicolon) in C#. /// /// First action to run /// Second action to run /// Result of the second action public static K operator << (K lhs, K rhs) => lhs >> (x => rhs.Map(_ => x)); } extension(K self) where M : MonadIO { /// /// Monad bind operator /// /// Monad to bind /// Binding function /// Mapped monad public static K operator >> (K ma, Func> f) => ma.Bind(x => M.LiftIO(f(x))); /// /// Sequentially compose two actions, discarding any value produced by the first, like sequencing operators (such /// as the semicolon) in C#. /// /// First action to run /// Second action to run /// Result of the second action public static K operator >> (K lhs, K rhs) => lhs >> (_ => rhs); } extension(K self) where M : MonadIO { /// /// Monad bind operator /// /// Monad to bind /// Binding function /// Mapped monad public static K operator >> (K ma, Func> f) => M.LiftIO(ma).Bind(f); /// /// Sequentially compose two actions, discarding any value produced by the first, like sequencing operators (such /// as the semicolon) in C#. /// /// First action to run /// Second action to run /// Result of the second action public static K operator >> (K lhs, K rhs) => lhs >> (_ => rhs); } extension(K self) where M : Monad { /// /// Sequentially compose two actions. The second action is a unit-returning action, so the result of the /// first action is propagated. /// /// First action to run /// Second action to run /// Result of the first action public static K operator >> (K lhs, K rhs) => lhs >> (x => (_ => x) * rhs); } extension(K self) where M : MonadIO { /// /// Sequentially compose two actions. The second action is a unit-returning action, so the result of the /// first action is propagated. /// /// First action to run /// Second action to run /// Result of the first action public static K operator >> (K lhs, K rhs) => lhs >> (x => (_ => x) * rhs); } } ================================================ FILE: LanguageExt.Core/Traits/Monads/Monad/Monad.Trait.cs ================================================ using System; using System.Collections.Generic; using LanguageExt.Common; using static LanguageExt.Prelude; namespace LanguageExt.Traits; /// /// Monad trait /// /// Self-referring trait public interface Monad : Applicative, Maybe.MonadUnliftIO where M : Monad { //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Abstract members // public static abstract K Bind(K ma, Func> f); /// /// Tail-recursive bind /// /// Initial value to the bind expression /// Bind function /// Continuation value type /// Completed value type /// Result of repeatedly invoking `f` until Right(b) is returned public static abstract K Recur(A value, Func>> f); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Default implementations // /// /// Lift a Next.Done value into the monad /// public static virtual K> Done(B value) => M.Pure(Next.Done(value)); /// /// Lift a Next.Done value into the monad /// public static virtual K> Loop(A value) => M.Pure(Next.Loop(value)); public static virtual K SelectMany(K ma, Func> bind, Func project) => ma.Bind(x => bind(x).Map(y => project(x, y))); public static virtual K SelectMany(K ma, Func> bind, Func project) => M.Map(x => project(x, bind(x).Value), ma); public static virtual K Flatten(K> mma) => M.Bind(mma, identity); static K Applicative.Actions(IterableNE> mas) { // TODO: Check if this implementation is valid return M.Recur(mas.GetIterator(), go).Flatten(); K>, K>> go(Iterator> iter) => iter switch { (var head, { IsEmpty : true }) => M.Pure(Next.Done>, K>(head)), var (head, tail) => head.Map(_ => Next.Loop>, K>(tail.Clone())) }; } } ================================================ FILE: LanguageExt.Core/Traits/Monads/MonadIO/MonadIO.Extensions.cs ================================================ using System; using LanguageExt.DSL; using LanguageExt.Traits; namespace LanguageExt; public static class MonadIOExtensions { /// /// Monad bind operation /// public static K Bind( this K ma, Func> f) where M : MonadIO, Monad => M.Bind(ma, x => M.LiftIO(f(x))); /// /// Monad bind operation /// public static K Bind( this IO ma, Func> f) where M : MonadIO, Monad => M.Bind(M.LiftIO(ma), f); /// /// Monad bind operation /// public static K SelectMany(this K ma, Func> bind, Func project) where M : MonadIO => ma.Bind(x => IOTail.resolve(x, bind(x), project)); /// /// Monad bind operation /// public static K SelectMany(this IO ma, Func> bind, Func project) where M : MonadIO, Monad => M.SelectMany(M.LiftIOMaybe(ma), bind, project); /// /// Queue this IO operation to run on the thread-pool. /// /// Maximum time that the forked IO operation can run for. `None` for no timeout. /// Returns a `ForkIO` data-structure that contains two IO effects that can be used to either cancel /// the forked IO operation or to await the result of it. /// public static K> ForkIO(this K ma, Option timeout = default) where M : MonadUnliftIO => M.ForkIO(ma, timeout); /// /// Queue this IO operation to run on the thread-pool. /// /// Maximum time that the forked IO operation can run for. `None` for no timeout. /// Returns a `ForkIO` data-structure that contains two IO effects that can be used to either cancel /// the forked IO operation or to await the result of it. /// public static K> ForkIOMaybe(this K ma, Option timeout = default) where M : MonadIO => M.ForkIOMaybe(ma, timeout); } ================================================ FILE: LanguageExt.Core/Traits/Monads/MonadIO/MonadIO.Module.cs ================================================ using System; using System.Diagnostics.Contracts; using System.Threading; using LanguageExt.Common; namespace LanguageExt.Traits; /// /// Monad module /// public static class MonadIO { /// /// When the predicate evaluates to `true`, compute `Then` /// /// Predicate /// Computation /// Monad /// Unit monad [Pure] public static K when(K Pred, K Then) where M : MonadIO => Pred.Bind(f => Applicative.when(f, Then).As()); /// /// When the predicate evaluates to `false`, compute `Then` /// /// Predicate /// Computation /// Monad /// Unit monad [Pure] public static K unless(K Pred, K Then) where M : MonadIO => Pred.Bind(f => Applicative.unless(f, Then).As()); /// /// Embeds the `IO` monad into the `M〈A〉` monad. NOTE: This will fail if the monad transformer /// stack doesn't have an `IO` monad as its innermost monad. /// [Pure] public static K liftIO(IO ma) where M : Maybe.MonadIO, Monad => M.LiftIOMaybe(ma); /// /// Embeds the `IO` monad into the `M〈A〉` monad. NOTE: This will fail if the monad transformer /// stack doesn't have an `IO` monad as its innermost monad. /// [Pure] public static K liftIO(K ma) where M : Maybe.MonadIO, Monad => M.LiftIOMaybe(ma); /// /// Get the environment value threaded through the IO computation /// /// Trait /// Lifted environment value [Pure] public static K envIO() where M : MonadIO, Monad => M.EnvIO; /// /// Get the cancellation token threaded through the IO computation /// /// Trait /// Lifted cancellation token [Pure] public static K token() where M : MonadIO, Monad => M.Token; /// /// Get the cancellation token-source threaded through the IO computation /// /// Trait /// Lifted cancellation token-source [Pure] public static K tokenSource() where M : MonadIO, Monad => M.TokenSource; /// /// Get the synchronisation-context threaded through the IO computation /// /// Trait /// Lifted synchronisation-context [Pure] public static K> syncContext() where M : MonadIO, Monad => M.SyncContext; } ================================================ FILE: LanguageExt.Core/Traits/Monads/MonadIO/MonadIO.Trait.cs ================================================ using System; using System.Threading; using LanguageExt.Common; using LanguageExt.DSL; namespace LanguageExt.Traits; /// /// Monad that is either the IO monad or a transformer with the IO monad in its stack /// /// Self-referring trait public interface MonadIO : Monad where M : MonadIO { /// /// Lifts the IO monad into a monad transformer stack. /// /// IO computation to lift /// Bound value type /// The outer monad with the IO monad lifted into it public static virtual K LiftIO(K ma) => M.LiftIO(ma.As()); /// /// Lifts the IO monad into a monad transformer stack. /// /// /// IMPLEMENTATION REQUIRED: If this method isn't overloaded in this monad /// or any monad in the stack on the way to the inner-monad, then it will throw /// an exception. /// /// This isn't ideal, it appears to be the only way to achieve this /// kind of functionality in C# without resorting to magic. /// /// IO computation to lift /// Bound value type /// The outer monad with the IO monad lifted into it /// If this method isn't overloaded in /// the inner monad or any monad in the stack on the way to the inner-monad, /// then it will throw an exception. public static abstract K LiftIO(IO ma); static K Maybe.MonadIO.LiftIOMaybe(IO ma) => M.LiftIO(ma); static K Maybe.MonadIO.LiftIOMaybe(K ma) => M.LiftIO(ma); public static virtual K EnvIO => M.LiftIOMaybe(IO.env); public static virtual K Token => M.LiftIOMaybe(IO.token); public static virtual K TokenSource => M.LiftIOMaybe(IO.source); public static virtual K> SyncContext => M.LiftIOMaybe(IO.syncContext); } ================================================ FILE: LanguageExt.Core/Traits/Monads/MonadT/MonadT.Extensions.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; ================================================ FILE: LanguageExt.Core/Traits/Monads/MonadT/MonadT.Module.cs ================================================ namespace LanguageExt.Traits; public static partial class MonadT { /// /// Lift a monad into a transformer /// /// Monad to lift /// Transformer /// Monad /// Bound value /// Monad transformer with the monad lifted into it public static K lift(K ma) where M : Monad where MTran : MonadT => MTran.Lift(ma); } ================================================ FILE: LanguageExt.Core/Traits/Monads/MonadT/MonadT.Trait.cs ================================================ namespace LanguageExt.Traits; /// /// MonadT trait /// /// Self referring trait /// Inner monad trait public interface MonadT : Monad where T : MonadT where M : Monad { public static abstract K Lift(K ma); } ================================================ FILE: LanguageExt.Core/Traits/Monads/MonadUnliftIO/MonadUnliftIO.Extensions.cs ================================================ using System; using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt; public static class MonadUnliftIOExtensions { /// /// Await a forked operation /// public static K Await(this K> ma) where M : MonadUnliftIO => M.Await(ma); /// Resource acquisition extension(K ma) where M : MonadUnliftIO { /// /// Creates a local cancellation environment /// /// /// A local cancellation environment stops other IO computations, that rely on the same /// environmental cancellation token, from being taken down by a regional cancellation. /// /// If an `IO.cancel` is invoked locally, then it will still create an exception that /// propagates upwards and so catching cancellations is still important. /// /// Result of the computation public K LocalIO() => M.LocalIO(ma); /// /// Wraps this computation in a local-environment that ignores any cancellation-token cancellation requests. /// /// An uninterruptible computation public K UninterruptibleIO() => M.UninterruptibleIO(ma); /// /// Make this IO computation run on the `SynchronizationContext` that was captured at the start /// of the IO chain (i.e. the one embedded within the `EnvIO` environment that is passed through /// all IO computations) /// public K PostIO() => M.PostIO(ma); /// /// Timeout operation if it takes too long /// public K TimeoutIO(TimeSpan timeout) => M.TimeoutIO(ma, timeout); /// /// The IO monad tracks resources automatically, this creates a local resource environment /// to run this computation in. Once the computation has completed any resources acquired /// are automatically released. Imagine this as the ultimate `using` statement. /// public K BracketIO() => M.BracketIO(ma); /// /// When acquiring, using, and releasing various resources, it can be quite convenient to write a function to manage /// the acquisition and releasing, taking a function of the acquired value that specifies an action to be performed /// in between. /// /// Function to use the acquired resource /// Function to invoke to release the resource public K BracketIO(Func> Use, Func> Fin) => M.BracketIO(ma, Use, Fin); /// /// When acquiring, using, and releasing various resources, it can be quite convenient to write a function to manage /// the acquisition and releasing, taking a function of the acquired value that specifies an action to be performed /// in between. /// /// Function to use the acquired resource /// Function to run to handle any exceptions /// Function to invoke to release the resource public K BracketIO(Func> Use, Func> Catch, Func> Fin) => M.BracketIO(ma, Use, Catch, Fin); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Repeating the effect // /// /// Keeps repeating the computation forever, or until an error occurs /// /// /// Any resources acquired within a repeated IO computation will automatically be released. This also means you can't /// acquire resources and return them from within a repeated computation. /// /// The result of the last invocation public K RepeatIO() => M.RepeatIO(ma); /// /// Keeps repeating the computation, until the scheduler expires, or an error occurs /// /// /// Any resources acquired within a repeated IO computation will automatically be released. This also means you can't /// acquire resources and return them from within a repeated computation. /// /// Scheduler strategy for repeating /// The result of the last invocation public K RepeatIO(Schedule schedule) => M.RepeatIO(ma, schedule); /// /// Keeps repeating the computation until the predicate returns false, or an error occurs /// /// /// Any resources acquired within a repeated IO computation will automatically be released. This also means you can't /// acquire resources and return them from within a repeated computation. /// /// Keep repeating while this predicate returns `true` for each computed value /// The result of the last invocation public K RepeatWhileIO(Func predicate) => M.RepeatWhileIO(ma, predicate); /// /// Keeps repeating the computation, until the scheduler expires, or the predicate returns false, or an error occurs /// /// /// Any resources acquired within a repeated IO computation will automatically be released. This also means you can't /// acquire resources and return them from within a repeated computation. /// /// Scheduler strategy for repeating /// Keep repeating while this predicate returns `true` for each computed value /// The result of the last invocation public K RepeatWhileIO(Schedule schedule, Func predicate) => M.RepeatWhileIO(ma, schedule, predicate); /// /// Keeps repeating the computation until the predicate returns true, or an error occurs /// /// /// Any resources acquired within a repeated IO computation will automatically be released. This also means you can't /// acquire resources and return them from within a repeated computation. /// /// Keep repeating until this predicate returns `true` for each computed value /// The result of the last invocation public K RepeatUntilIO(Func predicate) => M.RepeatUntilIO(ma, predicate); /// /// Keeps repeating the computation, until the scheduler expires, or the predicate returns true, or an error occurs /// /// /// Any resources acquired within a repeated IO computation will automatically be released. This also means you can't /// acquire resources and return them from within a repeated computation. /// /// Scheduler strategy for repeating /// Keep repeating until this predicate returns `true` for each computed value /// The result of the last invocation public K RepeatUntilIO(Schedule schedule, Func predicate) => M.RepeatUntilIO(ma, schedule, predicate); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Retrying the effect when it fails // /// /// Retry if the IO computation fails /// /// /// This variant will retry forever /// /// /// Any resources acquired within a retrying IO computation will automatically be released *if* the operation fails. /// So, successive retries will not grow the acquired resources on each retry iteration. Any successful operation that /// acquires resources will have them tracked in the usual way. /// public K RetryIO() => M.RetryIO(ma); /// /// Retry if the IO computation fails /// /// /// This variant will retry until the schedule expires /// /// /// Any resources acquired within a retrying IO computation will automatically be released *if* the operation fails. /// So, successive retries will not grow the acquired resources on each retry iteration. Any successful operation that /// acquires resources will have them tracked in the usual way. /// public K RetryIO(Schedule schedule) => M.RetryIO(ma, schedule); /// /// Retry if the IO computation fails /// /// /// This variant will keep retrying whilst the predicate returns `true` for the error generated at each iteration; /// at which point the last raised error will be thrown. /// /// /// Any resources acquired within a retrying IO computation will automatically be released *if* the operation fails. /// So, successive retries will not grow the acquired resources on each retry iteration. Any successful operation that /// acquires resources will have them tracked in the usual way. /// public K RetryWhileIO(Func predicate) => M.RetryWhileIO(ma, predicate); /// /// Retry if the IO computation fails /// /// /// This variant will keep retrying whilst the predicate returns `true` for the error generated at each iteration; /// or, until the schedule expires; at which point the last raised error will be thrown. /// /// /// Any resources acquired within a retrying IO computation will automatically be released *if* the operation fails. /// So, successive retries will not grow the acquired resources on each retry iteration. Any successful operation that /// acquires resources will have them tracked in the usual way. /// public K RetryWhileIO(Schedule schedule, Func predicate) => M.RetryWhileIO(ma, schedule, predicate); /// /// Retry if the IO computation fails /// /// /// This variant will keep retrying until the predicate returns `true` for the error generated at each iteration; /// at which point the last raised error will be thrown. /// /// /// Any resources acquired within a retrying IO computation will automatically be released *if* the operation fails. /// So, successive retries will not grow the acquired resources on each retry iteration. Any successful operation that /// acquires resources will have them tracked in the usual way. /// public K RetryUntilIO(Func predicate) => M.RetryUntilIO(ma, predicate); /// /// Retry if the IO computation fails /// /// /// This variant will keep retrying until the predicate returns `true` for the error generated at each iteration; /// or, until the schedule expires; at which point the last raised error will be thrown. /// /// /// Any resources acquired within a retrying IO computation will automatically be released *if* the operation fails. /// So, successive retries will not grow the acquired resources on each retry iteration. Any successful operation that /// acquires resources will have them tracked in the usual way. /// public K RetryUntilIO(Schedule schedule, Func predicate) => M.RetryUntilIO(ma, schedule, predicate); } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Folding // public static K FoldIO( this K ma, Schedule schedule, S initialState, Func folder) where M : MonadUnliftIO => M.FoldIO(ma, schedule, initialState, folder); public static K FoldIO( this K ma, S initialState, Func folder) where M : MonadUnliftIO => M.FoldIO(ma, initialState, folder); public static K FoldWhileIO( this K ma, Schedule schedule, S initialState, Func folder, Func stateIs) where M : MonadUnliftIO => M.FoldWhileIO(ma, schedule, initialState, folder, stateIs); public static K FoldWhileIO( this K ma, S initialState, Func folder, Func stateIs) where M : MonadUnliftIO => M.FoldWhileIO(ma, initialState, folder, stateIs); public static K FoldWhileIO( this K ma, Schedule schedule, S initialState, Func folder, Func valueIs) where M : MonadUnliftIO => M.FoldWhileIO(ma, schedule, initialState, folder, valueIs); public static K FoldWhileIO( this K ma, S initialState, Func folder, Func valueIs) where M : MonadUnliftIO => M.FoldWhileIO(ma, initialState, folder, valueIs); public static K FoldWhileIO( this K ma, Schedule schedule, S initialState, Func folder, Func<(S State, A Value), bool> predicate) where M : MonadUnliftIO => M.FoldWhileIO(ma, schedule, initialState, folder, predicate); public static K FoldWhileIO( this K ma, S initialState, Func folder, Func<(S State, A Value), bool> predicate) where M : MonadUnliftIO => M.FoldWhileIO(ma, initialState, folder, predicate); public static K FoldUntilIO( this K ma, Schedule schedule, S initialState, Func folder, Func stateIs) where M : MonadUnliftIO => M.FoldUntilIO(ma, schedule, initialState, folder, stateIs); public static K FoldUntilIO( this K ma, S initialState, Func folder, Func stateIs) where M : MonadUnliftIO => M.FoldUntilIO(ma, initialState, folder, stateIs); public static K FoldUntilIO( this K ma, Schedule schedule, S initialState, Func folder, Func valueIs) where M : MonadUnliftIO => M.FoldUntilIO(ma, schedule, initialState, folder, valueIs); public static K FoldUntilIO( this K ma, S initialState, Func folder, Func valueIs) where M : MonadUnliftIO => M.FoldUntilIO(ma, initialState, folder, valueIs); public static K FoldUntilIO( this K ma, S initialState, Func folder, Func<(S State, A Value), bool> predicate) where M : MonadUnliftIO => M.FoldUntilIO(ma, initialState, folder, predicate); public static K FoldUntilIO( this K ma, Schedule schedule, S initialState, Func folder, Func<(S State, A Value), bool> predicate) where M : MonadUnliftIO => M.FoldUntilIO(ma, schedule, initialState, folder, predicate); } ================================================ FILE: LanguageExt.Core/Traits/Monads/MonadUnliftIO/MonadUnliftIO.Module.cs ================================================ using System; using System.Diagnostics.Contracts; namespace LanguageExt.Traits; /// /// Monad module /// public static class MonadUnliftIO { /// /// Get the `IO` monad from within the `M` monad /// /// /// This only works if the `M` trait implements `MonadIO.ToIO`. /// [Pure] public static K> toIO(K ma) where M : MonadUnliftIO => M.ToIOMaybe(ma); /// /// Map the underlying IO monad /// [Pure] public static K mapIO(Func, IO> f, K ma) where M : MonadUnliftIO => M.MapIOMaybe(ma, f); } ================================================ FILE: LanguageExt.Core/Traits/Monads/MonadUnliftIO/MonadUnliftIO.Trait.cs ================================================ using System; using LanguageExt.Common; namespace LanguageExt.Traits; /// /// Monad that is either the IO monad or a transformer with the IO monad in its stack. /// /// 'Unlifting' allows us to get at the nested `IO` monad and work on it, then to repackage it /// wherever it is in the transformer stack. This allows all IO functionality to work on any /// type that encapsulates the `IO` monad. /// /// This opens up a ton of default functionality for monads that are able to support unlifting. /// It must be stated that not all monads are capable of supporting unlifting. It's usually the /// case that if they have a complex return type (like a union) then they can't support unlifting /// without compromising the integrity of the monad. /// /// Self-referring trait public interface MonadUnliftIO : MonadIO where M : MonadUnliftIO { /// /// Extract the IO monad from within the M monad (usually as part of a monad-transformer stack). /// /// /// IMPLEMENTATION REQUIRED: If this method isn't overloaded in this monad /// or any monad in the stack on the way to the inner-monad, then it will throw /// an exception. /// /// This isn't ideal, it appears to be the only way to achieve this /// kind of functionality in C# without resorting to magic. /// /// If this method isn't overloaded in /// the inner monad or any monad in the stack on the way to the inner-monad, /// then it will throw an exception. public static abstract K> ToIO(K ma); /// /// Extract the IO monad from within the `M` monad (usually as part of a monad-transformer stack). Then perform /// a mapping operation on the IO action before lifting the IO back into the `M` monad. /// public static virtual K MapIO(K ma, Func, IO> f) => M.ToIO(ma).Bind(io => M.LiftIO(f(io))); /// /// Queue this IO operation to run on the thread-pool. /// /// Maximum time that the forked IO operation can run for. `None` for no timeout. /// Returns a `ForkIO` data-structure that contains two IO effects that can be used to either cancel /// the forked IO operation or to await the result of it. /// public static virtual K> ForkIO(K ma, Option timeout) => M.MapIO(ma, io => io.Fork(timeout)); /// /// Await a forked operation /// public static virtual K Await(K> ma) => M.MapIO(ma, io => io.Bind(f => f.Await)); /// /// Creates a local cancellation environment /// /// /// A local cancellation environment stops other IO computations, that rely on the same /// environmental cancellation token, from being taken down by a regional cancellation. /// /// If an `IO.cancel` is invoked locally, then it will still create an exception that /// propagates upwards and so catching cancellations is still important. /// /// Computation to run within the local context /// Bound value /// Result of the computation public static virtual K LocalIO(K ma) => M.MapIO(ma, io => io.Local()); /// /// Wraps this computation in a local-environment that ignores any cancellation-token cancellation requests. /// /// An uninterruptible computation public static virtual K UninterruptibleIO(K ma) => M.MapIO(ma, io => io.Uninterruptible()); /// /// Make this IO computation run on the `SynchronizationContext` that was captured at the start /// of the IO chain (i.e. the one embedded within the `EnvIO` environment that is passed through /// all IO computations) /// public static virtual K PostIO(K ma) => M.MapIO(ma, io => io.Post()); /// /// Timeout operation if it takes too long /// public static virtual K TimeoutIO(K ma, TimeSpan timeout) => M.MapIO(ma, io => io.Timeout(timeout)); /// /// The IO monad tracks resources automatically; this creates a local resource environment /// to run this computation in. Once the computation is completed, any resources acquired /// are automatically released. Imagine this as the ultimate `using` statement. /// public static virtual K BracketIO(K ma) => M.MapIO(ma, io => io.Bracket()); /// /// When acquiring, using, and releasing various resources, it can be quite convenient to write a function to manage /// the acquisition and releasing, taking a function of the acquired value that specifies an action to be performed /// in between. /// /// Resource acquisition /// Function to use the acquired resource /// Function to invoke to release the resource public static virtual K BracketIO( K Acq, Func> Use, Func> Fin) => M.MapIO(Acq, io => io.Bracket(Use, Fin)); /// /// When acquiring, using, and releasing various resources, it can be quite convenient to write a function to manage /// the acquisition and releasing, taking a function of the acquired value that specifies an action to be performed /// in between. /// /// Resource acquisition /// Function to use the acquired resource /// Function to run to handle any exceptions /// Function to invoke to release the resource public static virtual K BracketIO( K Acq, Func> Use, Func> Catch, Func> Fin) => M.MapIO(Acq, io => io.Bracket(Use, Catch, Fin)); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Repeating the effect // /// /// Keeps repeating the computation forever, or until an error occurs /// /// /// Any resources acquired within a repeated IO computation will automatically be released. This also means you can't /// acquire resources and return them from within a repeated computation. /// /// The result of the last invocation public static virtual K RepeatIO(K ma) => M.MapIO(ma, io => io.Repeat()); /// /// Keeps repeating the computation until the scheduler expires, or an error occurs /// /// /// Any resources acquired within a repeated IO computation will automatically be released. This also means you can't /// acquire resources and return them from within a repeated computation. /// /// Scheduler strategy for repeating /// The result of the last invocation public static virtual K RepeatIO( K ma, Schedule schedule) => M.MapIO(ma, io => io.Repeat(schedule)); /// /// Keeps repeating the computation until the predicate returns false, or an error occurs /// /// /// Any resources acquired within a repeated IO computation will automatically be released. This also means you can't /// acquire resources and return them from within a repeated computation. /// /// Keep repeating while this predicate returns `true` for each computed value /// The result of the last invocation public static virtual K RepeatWhileIO( K ma, Func predicate) => M.MapIO(ma, io => io.RepeatWhile(predicate)); /// /// Keeps repeating the computation until the scheduler expires, or the predicate returns false, or an error occurs /// /// /// Any resources acquired within a repeated IO computation will automatically be released. This also means you can't /// acquire resources and return them from within a repeated computation. /// /// Scheduler strategy for repeating /// Keep repeating while this predicate returns `true` for each computed value /// The result of the last invocation public static virtual K RepeatWhileIO( K ma, Schedule schedule, Func predicate) => M.MapIO(ma, io => io.RepeatWhile(schedule, predicate)); /// /// Keeps repeating the computation until the predicate returns true, or an error occurs /// /// /// Any resources acquired within a repeated IO computation will automatically be released. This also means you can't /// acquire resources and return them from within a repeated computation. /// /// Keep repeating until this predicate returns `true` for each computed value /// The result of the last invocation public static virtual K RepeatUntilIO( K ma, Func predicate) => M.MapIO(ma, io => io.RepeatUntil(predicate)); /// /// Keeps repeating the computation until the scheduler expires, or the predicate returns true, or an error occurs /// /// /// Any resources acquired within a repeated IO computation will automatically be released. This also means you can't /// acquire resources and return them from within a repeated computation. /// /// Scheduler strategy for repeating /// Keep repeating until this predicate returns `true` for each computed value /// The result of the last invocation public static virtual K RepeatUntilIO( K ma, Schedule schedule, Func predicate) => M.MapIO(ma, io => io.RepeatUntil(schedule, predicate)); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Retrying the effect when it fails // /// /// Retry if the IO computation fails /// /// /// This variant will retry forever /// /// /// Any resources acquired within a retrying IO computation will automatically be released *if* the operation fails. /// So, successive retries will not grow the acquired resources on each retry iteration. Any successful operation that /// acquires resources will have them tracked in the usual way. /// public static virtual K RetryIO(K ma) => M.MapIO(ma, io => io.Retry()); /// /// Retry if the IO computation fails /// /// /// This variant will retry until the schedule expires /// /// /// Any resources acquired within a retrying IO computation will automatically be released *if* the operation fails. /// So, successive retries will not grow the acquired resources on each retry iteration. Any successful operation that /// acquires resources will have them tracked in the usual way. /// public static virtual K RetryIO( K ma, Schedule schedule) => M.MapIO(ma, io => io.Retry(schedule)); /// /// Retry if the IO computation fails /// /// /// This variant will keep retrying whilst the predicate returns `true` for the error generated at each iteration; /// at which point the last raised error will be thrown. /// /// /// Any resources acquired within a retrying IO computation will automatically be released *if* the operation fails. /// So, successive retries will not grow the acquired resources on each retry iteration. Any successful operation that /// acquires resources will have them tracked in the usual way. /// public static virtual K RetryWhileIO( K ma, Func predicate) => M.MapIO(ma, io => io.RetryWhile(predicate)); /// /// Retry if the IO computation fails /// /// /// This variant will keep retrying whilst the predicate returns `true` for the error generated at each iteration; /// or, until the schedule expires; at which point the last raised error will be thrown. /// /// /// Any resources acquired within a retrying IO computation will automatically be released *if* the operation fails. /// So, successive retries will not grow the acquired resources on each retry iteration. Any successful operation that /// acquires resources will have them tracked in the usual way. /// public static virtual K RetryWhileIO( K ma, Schedule schedule, Func predicate) => M.MapIO(ma, io => io.RetryWhile(schedule, predicate)); /// /// Retry if the IO computation fails /// /// /// This variant will keep retrying until the predicate returns `true` for the error generated at each iteration; /// at which point the last raised error will be thrown. /// /// /// Any resources acquired within a retrying IO computation will automatically be released *if* the operation fails. /// So, successive retries will not grow the acquired resources on each retry iteration. Any successful operation that /// acquires resources will have them tracked in the usual way. /// public static virtual K RetryUntilIO( K ma, Func predicate) => M.MapIO(ma, io => io.RetryUntil(predicate)); /// /// Retry if the IO computation fails /// /// /// This variant will keep retrying until the predicate returns `true` for the error generated at each iteration; /// or, until the schedule expires; at which point the last raised error will be thrown. /// /// /// Any resources acquired within a retrying IO computation will automatically be released *if* the operation fails. /// So, successive retries will not grow the acquired resources on each retry iteration. Any successful operation that /// acquires resources will have them tracked in the usual way. /// public static virtual K RetryUntilIO( K ma, Schedule schedule, Func predicate) => M.MapIO(ma, io => io.RetryUntil(schedule, predicate)); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Folding // public static virtual K FoldIO( K ma, Schedule schedule, S initialState, Func folder) => M.MapIO(ma, io => io.Fold(schedule, initialState, folder)); public static virtual K FoldIO( K ma, S initialState, Func folder) => M.MapIO(ma, io => io.Fold(initialState, folder)); public static virtual K FoldWhileIO( K ma, Schedule schedule, S initialState, Func folder, Func stateIs) => M.MapIO(ma, io => io.FoldWhile(schedule, initialState, folder, stateIs)); public static virtual K FoldWhileIO( K ma, S initialState, Func folder, Func stateIs) => M.MapIO(ma, io => io.FoldWhile(initialState, folder, stateIs)); public static virtual K FoldWhileIO( K ma, Schedule schedule, S initialState, Func folder, Func valueIs) => M.MapIO(ma, io => io.FoldWhile(schedule, initialState, folder, valueIs)); public static virtual K FoldWhileIO( K ma, S initialState, Func folder, Func valueIs) => M.MapIO(ma, io => io.FoldWhile(initialState, folder, valueIs)); public static virtual K FoldWhileIO( K ma, Schedule schedule, S initialState, Func folder, Func<(S State, A Value), bool> predicate) => M.MapIO(ma, io => io.FoldWhile(schedule, initialState, folder, predicate)); public static virtual K FoldWhileIO( K ma, S initialState, Func folder, Func<(S State, A Value), bool> predicate) => M.MapIO(ma, io => io.FoldWhile(initialState, folder, predicate)); public static virtual K FoldUntilIO( K ma, Schedule schedule, S initialState, Func folder, Func stateIs) => M.MapIO(ma, io => io.FoldUntil(schedule, initialState, folder, stateIs)); public static virtual K FoldUntilIO( K ma, S initialState, Func folder, Func stateIs) => M.MapIO(ma, io => io.FoldUntil(initialState, folder, stateIs)); public static virtual K FoldUntilIO( K ma, Schedule schedule, S initialState, Func folder, Func valueIs) => M.MapIO(ma, io => io.FoldUntil(schedule, initialState, folder, valueIs)); public static virtual K FoldUntilIO( K ma, S initialState, Func folder, Func valueIs) => M.MapIO(ma, io => io.FoldUntil(initialState, folder, valueIs)); public static virtual K FoldUntilIO( K ma, S initialState, Func folder, Func<(S State, A Value), bool> predicate) => M.MapIO(ma, io => io.FoldUntil(initialState, folder, predicate)); public static virtual K FoldUntilIO( K ma, Schedule schedule, S initialState, Func folder, Func<(S State, A Value), bool> predicate) => M.MapIO(ma, io => io.FoldUntil(schedule, initialState, folder, predicate)); static K Maybe.MonadUnliftIO.MapIOMaybe(K ma, Func, IO> f) => M.MapIO(ma, f); static K> Maybe.MonadUnliftIO.ToIOMaybe(K ma) => M.ToIO(ma); static K> Maybe.MonadUnliftIO.ForkIOMaybe(K ma, Option timeout) => M.ForkIO(ma, timeout); static K Maybe.MonadUnliftIO.AwaitMaybe(K> ma) => M.Await(ma); static K Maybe.MonadUnliftIO.LocalIOMaybe(K ma) => M.LocalIO(ma); static K Maybe.MonadUnliftIO.PostIOMaybe(K ma) => M.PostIO(ma); static K Maybe.MonadUnliftIO.TimeoutIOMaybe(K ma, TimeSpan timeout) => M.TimeoutIO(ma, timeout); static K Maybe.MonadUnliftIO.BracketIOMaybe(K ma) => M.BracketIO(ma); static K Maybe.MonadUnliftIO.BracketIOMaybe( K Acq, Func> Use, Func> Fin) => M.BracketIO(Acq, Use, Fin); static K Maybe.MonadUnliftIO.BracketIOMaybe( K Acq, Func> Use, Func> Catch, Func> Fin) => M.BracketIO(Acq, Use, Catch, Fin); static K Maybe.MonadUnliftIO.RepeatIOMaybe(K ma) => M.RepeatIO(ma); static K Maybe.MonadUnliftIO.RepeatIOMaybe( K ma, Schedule schedule) => M.RepeatIO(ma, schedule); static K Maybe.MonadUnliftIO.RepeatWhileIOMaybe( K ma, Func predicate) => M.RepeatWhileIO(ma, predicate); static K Maybe.MonadUnliftIO.RepeatWhileIOMaybe( K ma, Schedule schedule, Func predicate) => M.RepeatWhileIO(ma, schedule, predicate); static K Maybe.MonadUnliftIO.RepeatUntilIOMaybe( K ma, Func predicate) => M.RepeatUntilIO(ma, predicate); static K Maybe.MonadUnliftIO.RepeatUntilIOMaybe( K ma, Schedule schedule, Func predicate) => M.RepeatUntilIO(ma, schedule, predicate); static K Maybe.MonadUnliftIO.RetryIOMaybe(K ma) => M.RetryIO(ma); static K Maybe.MonadUnliftIO.RetryIOMaybe( K ma, Schedule schedule) => M.RetryIO(ma, schedule); static K Maybe.MonadUnliftIO.RetryWhileIOMaybe( K ma, Func predicate) => M.RetryWhileIO(ma, predicate); static K Maybe.MonadUnliftIO.RetryWhileIOMaybe( K ma, Schedule schedule, Func predicate) => M.RetryWhileIO(ma, schedule, predicate); static K Maybe.MonadUnliftIO.RetryUntilIOMaybe( K ma, Func predicate) => M.RetryUntilIO(ma, predicate); static K Maybe.MonadUnliftIO.RetryUntilIOMaybe( K ma, Schedule schedule, Func predicate) => M.RetryUntilIO(ma, schedule, predicate); static K Maybe.MonadUnliftIO.FoldIOMaybe( K ma, Schedule schedule, S initialState, Func folder) => M.FoldIO(ma, schedule, initialState, folder); static K Maybe.MonadUnliftIO.FoldIOMaybe( K ma, S initialState, Func folder) => M.FoldIO(ma, initialState, folder); static K Maybe.MonadUnliftIO.FoldWhileIOMaybe( K ma, Schedule schedule, S initialState, Func folder, Func stateIs) => M.FoldWhileIO(ma, schedule, initialState, folder, stateIs); static K Maybe.MonadUnliftIO.FoldWhileIOMaybe( K ma, S initialState, Func folder, Func stateIs) => M.FoldWhileIO(ma, initialState, folder, stateIs); static K Maybe.MonadUnliftIO.FoldWhileIOMaybe( K ma, Schedule schedule, S initialState, Func folder, Func valueIs) => M.FoldWhileIO(ma, schedule, initialState, folder, valueIs); static K Maybe.MonadUnliftIO.FoldWhileIOMaybe( K ma, S initialState, Func folder, Func valueIs) => M.FoldWhileIO(ma, initialState, folder, valueIs); static K Maybe.MonadUnliftIO.FoldWhileIOMaybe( K ma, Schedule schedule, S initialState, Func folder, Func<(S State, A Value), bool> predicate) => M.FoldWhileIO(ma, schedule, initialState, folder, predicate); static K Maybe.MonadUnliftIO.FoldWhileIOMaybe( K ma, S initialState, Func folder, Func<(S State, A Value), bool> predicate) => M.FoldWhileIO(ma, initialState, folder, predicate); static K Maybe.MonadUnliftIO.FoldUntilIOMaybe( K ma, Schedule schedule, S initialState, Func folder, Func stateIs) => M.FoldUntilIO(ma, schedule, initialState, folder, stateIs); static K Maybe.MonadUnliftIO.FoldUntilIOMaybe( K ma, S initialState, Func folder, Func stateIs) => M.FoldUntilIO(ma, initialState, folder, stateIs); static K Maybe.MonadUnliftIO.FoldUntilIOMaybe( K ma, Schedule schedule, S initialState, Func folder, Func valueIs) => M.FoldUntilIO(ma, schedule, initialState, folder, valueIs); static K Maybe.MonadUnliftIO.FoldUntilIOMaybe( K ma, S initialState, Func folder, Func valueIs) => M.FoldUntilIO(ma, initialState, folder, valueIs); static K Maybe.MonadUnliftIO.FoldUntilIOMaybe( K ma, S initialState, Func folder, Func<(S State, A Value), bool> predicate) => M.FoldUntilIO(ma, initialState, folder, predicate); static K Maybe.MonadUnliftIO.FoldUntilIOMaybe( K ma, Schedule schedule, S initialState, Func folder, Func<(S State, A Value), bool> predicate) => M.FoldUntilIO(ma, schedule, initialState, folder, predicate); } ================================================ FILE: LanguageExt.Core/Traits/Monads/README.md ================================================ ### Why do we even need monads? * Monads _encapsulate impure side effects and other effects_, making them pure. * Monads allow sequencing of operations without resorting to statements. * Monads (with LINQ) allow us to write _pure_ code that looks a lot like statements in imperative code. * Monads de-clutter your code making it more declarative. > **Quite simply**: _Monads are the 'statements' of pure functional programming and they encapsulate messy boilerplate and side-effects._ For a deeper dive into the 'why?' of monads, then check out [Paul Louth's Higher-Kinds series](https://paullouth.com/higher-kinds-in-csharp-with-language-ext-part-7-monads/). * [Monad](Monad) is the home of the main `Monad` trait as well as its extension methods and `Monad` module type. * [MonadIO](MonadIO) has the IO variant of the monad that allows lifting of the `IO` monad into a monad-transformer stack. * [MonadT](MonadT) has the `MonadT` monad-transformer trait, it allows the lifting of a monad `M` into a monad-transformer `T`. ================================================ FILE: LanguageExt.Core/Traits/Monoid/Monoid.Instance.cs ================================================ using System; using static LanguageExt.Prelude; namespace LanguageExt.Traits; /// /// Contains the trait in record form. This allows the trait to be passed /// around as a value rather than resolved as a type. It helps us get around limitations /// in the C# constraint system. /// /// Identity /// An associative binary operation. /// Trait type public record MonoidInstance(A Empty, Func Combine) : SemigroupInstance(Combine) { /// /// The `A` type should derive from `Monoid〈A〉`. If so, we can get a `MonoidInstance〈A〉` that we can /// pass around as a value. If not, then we will get `None`, which means the type is not a monoid. /// public new static Option> Instance { get; } = // NOTE: I don't like this, but it's the only way I can think of to do ad hoc trait resolution Try.lift(GetInstance) .ToOption() .Bind(x => x is null ? None : Some(x)); static MonoidInstance? GetInstance() { var type = typeof(Monoid<>).MakeGenericType(typeof(A)); var prop = type.GetProperty("Instance"); var value = prop?.GetValue(null); return (MonoidInstance?)value; } } ================================================ FILE: LanguageExt.Core/Traits/Monoid/Monoid.Prelude.cs ================================================ using LanguageExt.Traits; using System.Collections.Generic; using System.Diagnostics.Contracts; namespace LanguageExt; public static class Monoid { /// /// The identity of append /// [Pure] public static A empty() where A : Monoid => A.Empty; /// /// Combine two structures /// [Pure] public static A combine(A x, A y) where A : Monoid => x.Combine(y); /// /// Fold a list using the monoid. /// [Pure] public static A combine(A mx, A my, A mz, params A[] xs) where A : Monoid => xs.AsIterable().Fold(combine(combine(mx, my), mz), combine); /// /// Fold a list using the monoid. /// [Pure] public static A combine(IEnumerable xs) where A : Monoid => xs.AsIterable().Fold(A.Empty, combine); /// /// Fold a list using the monoid. /// [Pure] public static A combine(Seq xs) where A : Monoid => xs.Fold(A.Empty, combine); /// /// Get a concrete monoid instance value from a monoid supporting trait-type /// /// Monoid type /// Monoid instance that can be passed around as a value [Pure] public static MonoidInstance instance() where A : Monoid => A.Instance; } ================================================ FILE: LanguageExt.Core/Traits/Monoid/Monoid.cs ================================================ using System; using System.Diagnostics.Contracts; namespace LanguageExt.Traits; /// /// Monoid trait /// /// A monoid is a type with an identity and an associative binary operation. /// /// /// The type being described as a monoid public interface Monoid : Semigroup where A : Monoid { /// /// Identity /// [Pure] public static abstract A Empty { get; } /// /// Property that contains the trait in record form. This allows the trait to be passed /// around as a value rather than resolved as a type. It helps us get around limitations /// in the C# constraint system. /// public new static virtual MonoidInstance Instance { get; } = new (Empty: A.Empty, Combine: Semigroup.combine); } ================================================ FILE: LanguageExt.Core/Traits/MonoidK/MonoidK.Extensions.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; /// /// A monoid on applicative functors /// /// Applicative functor public static class MonoidKExtensions { /// /// Results in Empty if the predicate results in `false` /// public static K Filter(this K ma, Func predicate) where M : MonoidK, Monad => M.Bind(ma, a => predicate(a) ? M.Pure(a) : M.Empty()); /// /// Results in Empty if the predicate results in `false` /// public static K Where(this K ma, Func predicate) where M : MonoidK, Monad => M.Bind(ma, a => predicate(a) ? M.Pure(a) : M.Empty()); /// /// Chooses whether an element of the structure should be propagated through and if so /// maps the resulting value at the same time. /// public static K Choose(this K ma, Func> selector) where M : MonoidK, Monad => M.Bind(ma, a => selector(a).Match(Some: M.Pure, None: M.Empty)); } ================================================ FILE: LanguageExt.Core/Traits/MonoidK/MonoidK.Module.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics.Contracts; using static LanguageExt.Prelude; namespace LanguageExt.Traits; /// /// A monoid on higher-kinds /// public static partial class MonoidK { /// /// Identity /// /// /// [Pure] public static K empty() where F : MonoidK => F.Empty(); /// /// Associative binary operator /// [Pure] public static K combine(K ma, K mb) where F : MonoidK => F.Combine(ma, mb); /// /// Fold a list using the monoid. /// [Pure] public static K combine(K mx, K my, K mz, params K[] xs) where M : MonoidK => xs.AsIterable().Fold(combine(combine(mx, my), mz), combine); /// /// Fold a list using the monoid. /// [Pure] public static K combine(IEnumerable> xs) where M : MonoidK => xs.AsIterable().Fold(M.Empty(), combine); /// /// Fold a list using the monoid. /// [Pure] public static K combine(Seq> xs) where M : MonoidK => xs.Fold(M.Empty(), combine); /// /// Results in Empty if the predicate results in `false` /// public static K filter(K ma, Func predicate) where M : MonoidK, Monad => M.Bind(ma, a => predicate(a) ? M.Pure(a) : M.Empty()); /// /// Chooses whether an element of the structure should be propagated through and if so /// maps the resulting value at the same time. /// public static K choose(K ma, Func> selector) where M : MonoidK, Monad => M.Bind(ma, a => selector(a).Match(Some: M.Pure, None: M.Empty)); /// /// Conditional failure of `Alternative` computations. Defined by /// /// guard(true) = Applicative.pure /// guard(false) = Alternative.empty /// /// /// /// /// [Pure] public static K guard(bool flag) where F : MonoidK, Applicative => flag ? Applicative.pure(default) : empty(); } ================================================ FILE: LanguageExt.Core/Traits/MonoidK/MonoidK.cs ================================================ using System.Diagnostics.Contracts; namespace LanguageExt.Traits; /// /// A monoid for higher-kinds /// /// Higher kind public interface MonoidK : SemigroupK where M : MonoidK { /// /// Identity /// [Pure] public static abstract K Empty(); } ================================================ FILE: LanguageExt.Core/Traits/MonoidK/README.md ================================================ `MonoidK` inherits `SemigroupK`. The way to think of `MonoidK` is a monoid for higher-kinds (`K` types, rather than `A` types). What that means is that any type that implements the `MonoidK` trait gains an `Empty()` (a 'zero'/identity element ) value as well as the ability to `Combine` two `K` structures together into one. Many implementations of `MonoidK` use `Combine` to catch errors and propagate. So, if the first `K` argument to `Combine` fails, it simply returns the second argument. If it succeeds, then the result of the first is returned. This works a bit like `null` propagation with the `??` operator. And while this isn't always how `MonoidK` is implemented, it's useful to know. The `MonoidK` trait combines with `Applicative` and `Monad` traits to provide the following default functionality: * `Filter` | `filter` - if your type supports `Monad` and `MonoidK` you get free filtering and `Where` LINQ extension * `Choose` | `choose` - if your type supports `Monad` and `MonoidK` then `Choose` does filtering and mapping * `OneOf` | `oneOf` - takes a collection of `K` structures, returns the first one to succeed. * `Some` | `some` - evaluates a `K` structure repeatedly, collecting the `A` values, until it fails (at least one must succeed). Returns the `K>` * `Many` | `many` - evaluates a `K` structure repeatedly, collecting the `A` values, until it fails. Returns the `K>` * `guard` - conditional failure Some of these you might recognise from the `Parsec` library. This completely generalises the concept of alternative structure coalescing. ================================================ FILE: LanguageExt.Core/Traits/Mutates/Mutates.Module.cs ================================================ using System; using LanguageExt.Common; namespace LanguageExt.Traits; public static class Mutates { /// /// Mutate an atomic value in an environment /// /// Function to mapping the value atomically /// Readable & Monad trait /// Environment type /// Type of the value to read from the environment /// Result of mutation, or original value if the underlying Atom's validator failed public static K mutate(Func f) where F : Functor where Env : Mutates => Env.Mutable.Map(m => m.Swap(f)); } ================================================ FILE: LanguageExt.Core/Traits/Mutates/Mutates.Trait.cs ================================================ namespace LanguageExt.Traits; /// /// Makes an atomic value within an environment available for mutation /// /// Structure trait /// The value extracted from an environment public interface Mutates : Has where M : Functor { /// /// Extracts the `A` from the `Env`, passes it to the `f` /// mapping functions, and then wraps it back up into the generic `M〈Unit〉` type. /// /// Mapping function /// Mapped value public static abstract K> Mutable { get; } } ================================================ FILE: LanguageExt.Core/Traits/Natural/CoNatural.Module.cs ================================================ namespace LanguageExt.Traits; public static class CoNatural { /// /// Co-natural transformation /// /// /// If functor `map` operations transform the bound-values within the structure, then /// natural-transformations transform the structure itself. /// /// /// Functors are referenced, because that's the true definition in category-theory, but /// there is no requirement in language-ext for FA or GA to be functors. It is just typically /// true that FA and GA will also be functors. /// /// Functor to transform /// Source functor type /// Target functor type /// Bound value type /// Transformed functor public static K transform(K fa) where F : CoNatural => F.CoTransform(fa); } ================================================ FILE: LanguageExt.Core/Traits/Natural/CoNatural.cs ================================================ namespace LanguageExt.Traits; /// /// Natural transformation /// /// /// If functor `map` operations transform the bound-values within the structure, then /// natural-transformations transform the structure itself. /// /// /// Functors are referenced, because that's the true definition in category-theory, but /// there is no requirement in language-ext for FA or GA to be functors. It is just typically /// true that FA and GA will also be functors. /// /// From functor /// To functor public interface CoNatural { /// /// Perform a natural transformation from `FA -> GA` /// /// /// If functor `map` operations transform the bound-values within the structure, then /// natural-transformations transform the structure itself. /// /// /// Functors are referenced, because that's the true definition in category-theory, but /// there is no requirement in language-ext for FA or GA to be functors. It is just typically /// true that FA and GA will also be functors. /// /// Functor to transform /// Bound value type /// Transformed functor public static abstract K CoTransform(K fa); } ================================================ FILE: LanguageExt.Core/Traits/Natural/Natural.Module.cs ================================================ namespace LanguageExt.Traits; public static class Natural { /// /// Natural transformation /// /// /// If functor `map` operations transform the bound-values within the structure, then /// natural-transformations transform the structure itself. /// /// /// Functors are referenced, because that's the true definition in category-theory, but /// there is no requirement in language-ext for FA or GA to be functors. It is just typically /// true that FA and GA will also be functors. /// /// Functor to transform /// Source functor type /// Target functor type /// Bound value type /// Transformed functor public static K transform(K fa) where F : Natural => F.Transform(fa); } ================================================ FILE: LanguageExt.Core/Traits/Natural/Natural.cs ================================================ namespace LanguageExt.Traits; /// /// Natural transformation /// /// /// If functor `map` operations transform the bound-values within the structure, then /// natural-transformations transform the structure itself. /// /// /// Functors are referenced, because that's the true definition in category-theory, but /// there is no requirement in language-ext for FA or GA to be functors. It is just typically /// true that FA and GA will also be functors. /// /// From functor /// To functor public interface Natural { /// /// Perform a natural transformation from `FA -> GA` /// /// /// If functor `map` operations transform the bound-values within the structure, then /// natural-transformations transform the structure itself. /// /// /// Functors are referenced, because that's the true definition in category-theory, but /// there is no requirement in language-ext for FA or GA to be functors. It is just typically /// true that FA and GA will also be functors. /// /// Functor to transform /// Bound value type /// Transformed functor public static abstract K Transform(K fa); } ================================================ FILE: LanguageExt.Core/Traits/Natural/NaturalEpi.cs ================================================ namespace LanguageExt.Traits; /// /// Natural epimorphic transformation /// /// /// Epimorphism is the dual of monomorphism. So, `NaturalEpi` is the dual of `NaturalMono`. /// /// /// Functors are referenced, because that's the true definition in category-theory, but /// there is no requirement in language-ext for FA or GA to be functors. It is just typically /// true that FA and GA will also be functors. /// /// From functor /// To functor public interface NaturalEpi : CoNatural, Natural where F : CoNatural { static K Natural.Transform(K fa) => F.CoTransform(fa); } ================================================ FILE: LanguageExt.Core/Traits/Natural/NaturalIso.cs ================================================ namespace LanguageExt.Traits; /// /// Natural isomorphism /// /// Functor /// Functor public interface NaturalIso : Natural, CoNatural; ================================================ FILE: LanguageExt.Core/Traits/Natural/NaturalMono.cs ================================================ namespace LanguageExt.Traits; /// /// Natural monomorphic transformation /// /// /// Monomorphism means that there's one arrow between `F` and `G`. Therefore, there's also a `CoNatural` between /// `G` and `F` (`CoNatural` is the dual of `Natural`, so its arrows are flipped, so a `CoNatural` between `G` and `F` /// is isomorphic to a `Natural` between `F` and `G`). That's why `NaturalMono` derives from both `Natural` and /// `CoNatural` and can provide a default implementation for `CoTransform`. /// /// /// This type wouldn't need to exist is C# was better at type-unification. Use this when you want a unidirectional /// natural-transformation. Use `NaturalIso` when you want a bidirectional natural-transformation. /// /// /// Functors are referenced, because that's the true definition in category-theory, but /// there is no requirement in language-ext for FA or GA to be functors. It is just typically /// true that FA and GA will also be functors. /// /// From functor /// To functor public interface NaturalMono : Natural, CoNatural where F : Natural { static K CoNatural.CoTransform(K fa) => F.Transform(fa); } ================================================ FILE: LanguageExt.Core/Traits/Natural/README.md ================================================ ## Natural transformations Natural transformations are operations that transform the _structure_ of a type. If `Functor` is _structure **preserving**_ then natural-transformations are _structure **transforming**_. A concrete example is that if we call `Map` on a `Seq〈A〉`, then the structure (the `Seq`) is preserved, but the values within the structure, `A`, are transformed: and that defines a `Functor`, whereas with natural-transformations we could call `AsEnumerable()` on the `Seq〈A〉`. The result would preserve value-type, `A`, but transform the structure `Seq` to `IEnumerable`. This common pattern of structure transformation is a natural-transformation. It is captured by the type `Natural〈F, G〉`. ## `Natural` `Natural〈F, G〉` defines a single method, `Transform`, that maps a `K` to a `K`. This could be `K` to `K` for example. ## `CoNatural` There is also `CoNatural〈F, G〉` which is the dual of `Natural〈F, G〉`. It has the `Transform` arrow reversed, which means it maps a `K` to a `K`. It is _functionally exactly the same_ as `Natural`. The reason it exists is listed below. ## `NaturalIso` A natural-isomorphism (`NaturalIso〈F, G〉`) is a natural-transformation that can map forwards and backwards: F〈A〉-> G〈A〉 And the dual: G〈A〉-> F〈A〉 `NaturalIso` derives from: Natural〈F, G〉 CoNatural〈F, G〉 > The reason it doesn't derive from `Natural〈F, G〉` and `Natural〈G, F〉` is because C# can't type-check when `F == G` and so the dual needs a separate type: `CoNatural〈F, G〉`. ## `NaturalMono` A natural _monomorphic_ transformation means there's one arrow between `F` and `G`. Therefore, there's also a `CoNatural` between `G` and `F` (`CoNatural` is the dual of `Natural`, so its arrows are flipped, so a `CoNatural` between `G` and `F` is isomorphic to a `Natural` between `F` and `G`). `NaturalMono` derives from: Natural〈F, G〉 CoNatural〈G, F〉 The `CoTransform` method has a default implementation. ## `NaturalEpi` An _epimorphism_ is the _dual_ of a _monomorphism_. So, `NaturalEpi` is the dual of `NaturalMono`. `NaturalEpi` derives from: Natural〈G, F〉 CoNatural〈F, G〉 The `Transform` method has a default implementation. ================================================ FILE: LanguageExt.Core/Traits/Num/Num.Prelude.cs ================================================ using LanguageExt.Traits; using System.Diagnostics.Contracts; namespace LanguageExt; public static partial class Trait { /// /// Divide two numbers /// /// left hand side of the division operation /// right hand side of the division operation /// x / y [Pure] public static A divide(A x, A y) where NUM : Num => NUM.Divide(x, y); /// /// Find the absolute value of a number /// /// The value to find the absolute value of /// The non-negative absolute value of x [Pure] public static A abs(A x) where NUM : Num => NUM.Abs(x); /// /// Find the sign of x /// /// The value to find the sign of /// -1, 0, or +1 [Pure] public static A signum(A x) where NUM : Num => NUM.Signum(x); /// /// Generate a numeric value from an integer /// /// The integer to use /// The equivalent of x in the Num〈A〉 [Pure] public static A fromInteger(int x) where NUM : Num => NUM.FromInteger(x); /// /// Generate a numeric value from a float /// /// The float to use /// The equivalent of x in the Num〈A〉 [Pure] public static A fromDecimal(decimal x) where NUM : Num => NUM.FromDecimal(x); /// /// Generate a numeric value from a double /// /// The double to use /// The equivalent of x in the Num〈A〉 [Pure] public static A fromFloat(float x) where NUM : Num => NUM.FromFloat(x); /// /// Generate a numeric value from a decimal /// /// The decimal to use /// The equivalent of x in the Num〈A〉 [Pure] public static A fromDouble(double x) where NUM : Num => NUM.FromDouble(x); } ================================================ FILE: LanguageExt.Core/Traits/Num/Num.cs ================================================ using System.Diagnostics.Contracts; using LanguageExt.Attributes; namespace LanguageExt.Traits; /// /// Numerical value trait /// /// The type for which the number operations are /// defined. [Trait("Num*")] public interface Num : Ord, Arithmetic { /// /// Find the absolute value of a number /// /// The value to find the absolute value of /// The non-negative absolute value of x [Pure] public static abstract A Abs(A x); /// /// Find the sign of x /// /// The value to find the sign of /// -1, 0, or +1 [Pure] public static abstract A Signum(A x); /// /// Generate a numeric value from an integer /// /// The integer to use /// The equivalent of x in the Num〈A〉 [Pure] public static abstract A FromInteger(int x); /// /// Generate a numeric value from a decimal /// /// The decimal to use /// The equivalent of x in the Num〈A〉 [Pure] public static abstract A FromDecimal(decimal x); /// /// Generate a numeric value from a float /// /// The float to use /// The equivalent of x in the Num〈A〉 [Pure] public static abstract A FromFloat(float x); /// /// Generate a numeric value from a double /// /// The double to use /// The equivalent of x in the Num〈A〉 [Pure] public static abstract A FromDouble(double x); /// /// Divide two numbers /// /// left hand side of the division operation /// right hand side of the division operation /// x / y [Pure] public static abstract A Divide(A x, A y); } ================================================ FILE: LanguageExt.Core/Traits/Ord/Ord.Module.cs ================================================ using LanguageExt.ClassInstances; using LanguageExt.Traits; using System.Diagnostics.Contracts; using System.Runtime.CompilerServices; namespace LanguageExt; public static partial class Ord { /// /// Returns true if x is greater than y /// /// The first item to compare /// The second item to compare /// True if x is greater than y [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool greaterThan(A x, A y) where A : Ord => A.Compare(x, y) > 0; /// /// Returns true if x is greater than or equal to y /// /// The first item to compare /// The second item to compare /// True if x is greater than or equal to y [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool greaterOrEq(A x, A y) where A : Ord => A.Compare(x, y) >= 0; /// /// Returns true if x is less than y /// /// The first item to compare /// The second item to compare /// True if x is less than y [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool lessThan(A x, A y) where A : Ord => A.Compare(x, y) < 0; /// /// Returns true if x is less than or equal to y /// /// The first item to compare /// The second item to compare /// True if x is less than or equal to y [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool lessOrEq(A x, A y) where A : Ord => A.Compare(x, y) <= 0; /// /// Compare one item to another to ascertain ordering /// /// The first item to compare /// The second item to compare /// /// 0 if x is equal to y /// -1 if x greater than y /// 1 if x less than y /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int compare(A x, A y) where A : Ord => A.Compare(x, y); /// /// Find the maximum value between any two values /// /// First value /// Second value /// When ordering the two values in ascending order, this is the last of those [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static A max(A x, A y) where OrdA : Ord => OrdA.Compare(x, y) > 0 ? x : y; /// /// Find the minimum value between any two values /// /// First value /// Second value /// When ordering the two values in ascending order, this is the first of those [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static A min(A x, A y) where OrdA : Ord => OrdA.Compare(x, y) < 0 ? x : y; /// /// Find the minimum value between a set of values /// /// First value /// Second value /// Remaining values /// When ordering the values in ascending order, this is the first of those [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static A min(A x, params A[] tail) where OrdA : Ord { var min = x; foreach (var v in tail) { if (OrdA.Compare(v, x) < 0) { min = v; } } return min; } /// /// Find the maximum value between a set of values /// /// First value /// Second value /// Remaining values /// When ordering the values in ascending order, this is the last of those [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static A max(A x, params A[] tail) where OrdA : Ord { var max = x; foreach (var v in tail) { if (OrdA.Compare(v, x) > 0) { max = v; } } return max; } } ================================================ FILE: LanguageExt.Core/Traits/Ord/Ord.Prelude.cs ================================================ using LanguageExt.ClassInstances; using LanguageExt.Traits; using System.Diagnostics.Contracts; using System.Runtime.CompilerServices; namespace LanguageExt; public static partial class Prelude { /// /// Returns true if x is greater than y /// /// The first item to compare /// The second item to compare /// True if x is greater than y [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool greaterThan(A x, A y) where ORD : Ord => ORD.Compare(x, y) > 0; /// /// Returns true if x is greater than or equal to y /// /// The first item to compare /// The second item to compare /// True if x is greater than or equal to y [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool greaterOrEq(A x, A y) where ORD : Ord => ORD.Compare(x, y) >= 0; /// /// Returns true if x is less than y /// /// The first item to compare /// The second item to compare /// True if x is less than y [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool lessThan(A x, A y) where ORD : Ord => ORD.Compare(x, y) < 0; /// /// Returns true if x is less than or equal to y /// /// The first item to compare /// The second item to compare /// True if x is less than or equal to y [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool lessOrEq(A x, A y) where ORD : Ord => ORD.Compare(x, y) <= 0; /// /// Compare one item to another to ascertain ordering /// /// The first item to compare /// The second item to compare /// /// 0 if x is equal to y /// -1 if x greater than y /// 1 if x less than y /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int compare(A x, A y) where ORD : Ord => ORD.Compare(x, y); /// /// Compare one item to another to ascertain ordering /// /// The first item to compare /// The second item to compare /// /// 0 if x is equal to y /// -1 if x greater than y /// 1 if x less than y /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int compare(Option x, Option y) where ORD : Ord => x.CompareTo(y); /// /// Compare one item to another to ascertain ordering /// /// The first item to compare /// The second item to compare /// /// 0 if x is equal to y /// -1 if x greater than y /// 1 if x less than y /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int compare(Either x, Either y) where ORDA : Ord where ORDB : Ord => x.CompareTo(y); /// /// Compare one item to another to ascertain ordering /// /// The first item to compare /// The second item to compare /// /// 0 if x is equal to y /// -1 if x greater than y /// 1 if x less than y /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int compare(Validation x, Validation y) where A : Monoid where ORDA : Ord, Eq where ORDB : Ord => x.CompareTo(y); /// /// Compare one item to another to ascertain ordering /// /// The first item to compare /// The second item to compare /// /// 0 if x is equal to y /// -1 if x greater than y /// 1 if x less than y /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int compare(A[] x, A[] y) where ORD : Ord => OrdArray.Compare(x, y); /// /// Compare one item to another to ascertain ordering /// /// The first item to compare /// The second item to compare /// /// 0 if x is equal to y /// -1 if x greater than y /// 1 if x less than y /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int compare(Lst x, Lst y) where ORD : Ord => OrdLst.Compare(x, y); /// /// Compare one item to another to ascertain ordering /// /// The first item to compare /// The second item to compare /// /// 0 if x is equal to y /// -1 if x greater than y /// 1 if x less than y /// [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int compare(Either x, Either y) where ORD : Ord => x.CompareTo(y); } ================================================ FILE: LanguageExt.Core/Traits/Ord/Ord.cs ================================================ using LanguageExt.Attributes; using System.Diagnostics.Contracts; namespace LanguageExt.Traits; [Trait("Ord*")] public interface Ord : Eq { /// /// Compare two values /// /// Left hand side of the compare operation /// Right hand side of the compare operation /// /// if x greater than y : 1 /// /// if x less than y : -1 /// /// if x equals y : 0 /// [Pure] public static abstract int Compare(A x, A y); } ================================================ FILE: LanguageExt.Core/Traits/Ord/OrdComparer.cs ================================================ using System; using System.Collections.Generic; using LanguageExt.Traits; namespace LanguageExt; /// /// Hosts a standard .NET `IComparer` from an `Ord〈A〉` instance (in the static `Default` property) /// public class OrdComparer : IComparer where OrdA : Ord { public static readonly IComparer Default = new OrdComparer(); public int Compare(A? x, A? y) => (x, y) switch { (null, null) => 0, (null, _) => -1, (_, null) => 1, var (x1, y1) => OrdA.Compare(x1, y1) }; } internal class OrdComparer(Func Comparer) : IComparer { public int Compare(A? x, A? y) => (x, y) switch { (null, null) => 0, (null, _) => -1, (_, null) => 1, var (x1, y1) => Comparer(x1, y1) }; } ================================================ FILE: LanguageExt.Core/Traits/Predicate/Predicate.cs ================================================ #nullable enable using System.Diagnostics.Contracts; using LanguageExt.Attributes; namespace LanguageExt.Traits; /// /// Predicate trait /// /// Type of value to run the predication operation against [Trait("Pred*")] public interface Pred : Trait { /// /// The predicate operation. Returns true if the source value /// fits the predicate. /// /// /// [Pure] public static abstract bool True(A value); } ================================================ FILE: LanguageExt.Core/Traits/README.md ================================================ A recent C# feature is `static` interface members – this opens up some new possibilities for bending C# to make trait like functionality work. You may have already seen the technique: ```c# public interface Addable where SELF : Addable { public static abstract SELF Add(SELF x, SELF y); } ``` Note, how the Add member is `static abstract` and that the interface has a constraint that `SELF` is forced to inherit `Addable`. We can then create two distinct types that inherit the Addable trait: ```c# public record MyList(A[] values) : Addable> { public static MyList Add(MyList x, MyList y) => new (x.values.Append(y.values).ToArray()); } public record MyString(string value) : Addable { public static MyString Add(MyString x, MyString y) => new (x.value + y.value); } ``` Language-Ext takes this idea and uses it to implement 'higher-kinded traits' (with the `K` type being the anchor for them all). To continue reading about how this works, check out [Paul Louth's Higher-Kinds series](https://paullouth.com/higher-kinds-in-c-with-language-ext/). ================================================ FILE: LanguageExt.Core/Traits/Range/Range.Trait.cs ================================================ using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics.Contracts; namespace LanguageExt.Traits.Range; public interface Range : IEnumerable, K where SELF : Range where NumOrdA : Ord, Num { public static abstract SELF New(A from, A to, A step); /// /// Zero range /// public static readonly SELF Zero = FromMinMax(NumOrdA.FromInteger(0), NumOrdA.FromInteger(0), NumOrdA.FromInteger(0)); /// /// First value in the range /// [Pure] public A From { get; } /// /// Last (inclusive) value in the range /// [Pure] public A To { get; } /// /// Step size to the next item in the range /// [Pure] public A Step { get; } /// /// True if adding `Step` to `n` makes the resulting value greater than `n` /// [Pure] public bool StepIsAscending => NumOrdA.Compare(NumOrdA.Add(From, Step), From) >= 0; /// /// Reference version for use in pattern-matching /// [Pure] public object? Case => Prelude.Seq(this).Case; /// /// Construct a new range /// /// The minimum value in the range /// The maximum value in the range /// The size of each step in the range [Pure] public static SELF FromMinMax(A min, A max, A step) => SELF.New(min, max, step); /// /// Construct a new range /// /// The minimum value in the range /// The number of items in the range /// The size of each step in the range [Pure] public static SELF FromCount(A min, A count, A step) => SELF.New(min, NumOrdA.Add(min, NumOrdA.Subtract(NumOrdA.Multiply(count, step), step)), step); /// /// Returns true if the value provided is in range /// /// Value to test /// True if the value provided is in range [Pure] public bool InRange(A value) { var from = NumOrdA.Compare(From, To) > 0 ? To : From; var to = NumOrdA.Compare(From, To) > 0 ? From : To; return NumOrdA.Compare(value, from) >= 0 && NumOrdA.Compare(value, to) <= 0; } /// /// Returns true if the range provided overlaps this range /// /// The range to test /// True if the range provided overlaps this range [Pure] public bool Overlaps(SELF other) { var xfrom = NumOrdA.Compare(From, To) > 0 ? To : From; var xto = NumOrdA.Compare(From, To) > 0 ? From : To; var yfrom = NumOrdA.Compare(other.From, other.To) > 0 ? other.To : other.From; var yto = NumOrdA.Compare(other.From, other.To) > 0 ? other.From : other.To; return NumOrdA.Compare(xfrom, yto) < 0 && NumOrdA.Compare(yfrom, xto) < 0; } [Pure] public Seq ToSeq() => Prelude.toSeq(AsIterable()); [Pure] public Iterable AsIterable() { return Iterable.createRange(Go()); IEnumerable Go() { if (StepIsAscending) { for (A x = From; NumOrdA.Compare(x, To) <= 0; x = NumOrdA.Add(x, Step)) { yield return x; } } else { for (A x = From; NumOrdA.Compare(x, To) >= 0; x = NumOrdA.Add(x, Step)) { yield return x; } } } } [Pure] IEnumerator IEnumerable.GetEnumerator() => // ReSharper disable once NotDisposedResourceIsReturned AsIterable().GetEnumerator(); [Pure] IEnumerator IEnumerable.GetEnumerator() => AsIterable().GetEnumerator(); [Pure] public S Fold(S state, Func f) { foreach(var x in AsIterable()) { state = f(state, x); } return state; } } ================================================ FILE: LanguageExt.Core/Traits/Readable/Readable.Extensions.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public static class ReadableExtensions { public static K Local(this K ma, Func f) where M : Readable => M.Local(f, ma); } ================================================ FILE: LanguageExt.Core/Traits/Readable/Readable.Module.cs ================================================ using System; namespace LanguageExt.Traits; public static class Readable { public static K ask() where M : Readable => M.Ask; public static K asks(Func f) where M : Readable => M.Asks(f); public static K asksM(Func> f) where M : Readable, Monad => M.Flatten(M.Asks(f)); public static K local(Func f, K ma) where M : Readable => M.Local(f, ma); } ================================================ FILE: LanguageExt.Core/Traits/Readable/Readable.Prelude.cs ================================================ using System; using System.Diagnostics.Contracts; namespace LanguageExt; public static partial class Prelude { /// /// Retrieves the reader monad environment. /// /// Environment /// Reader monad with the environment in as the bound value [Pure] public static Ask ask() => new(identity); /// /// Retrieves a function of the current environment. /// /// Environment /// Bound and mapped value type /// Reader monad with the mapped environment in as the bound value [Pure] public static Ask asks(Func f) => new(f); } ================================================ FILE: LanguageExt.Core/Traits/Readable/Readable.Trait.cs ================================================ using System; namespace LanguageExt.Traits; public interface Readable where M : Readable { public static abstract K Asks(Func f); public static virtual K Ask => M.Asks(Prelude.identity); public static abstract K Local( Func f, K ma); } ================================================ FILE: LanguageExt.Core/Traits/Resolve/EqResolver.cs ================================================ using System; using System.Collections.Generic; using System.Reflection; namespace LanguageExt.Traits.Resolve; public static class EqResolve { public static string? ResolutionError; public static Func GetHashCodeFunc = null!; public static MethodInfo GetHashCodeMethod = null!; public static nint GetHashCodeMethodPtr; public static Func EqualsFunc = null!; public static MethodInfo EqualsMethod = null!; public static nint EqualsMethodPtr; public static int GetHashCode(A value) => GetHashCodeFunc(value); public static bool Equals(A lhs, A rhs) => EqualsFunc(lhs, rhs); public static bool Exists => ResolutionError is null; static EqResolve() { var source = typeof(A); var impl = Resolver.Find(source, "Eq"); if (impl is null) { ResolutionError = $"Trait implementation not found for: {typeof(A).Name}"; MakeDefault(); return; } // Equals var m = Resolver.Method(impl, "Equals", source, source); if (m is null) { ResolutionError = $"`Equals` method not found for: {typeof(A).Name}"; MakeDefault(); return; } EqualsMethod = m; EqualsMethodPtr = m.MethodHandle.GetFunctionPointer(); EqualsFunc = (x, y) => (bool?)EqualsMethod.Invoke(null, [x, y]) ?? throw new InvalidOperationException(); // GetHashCode m = Resolver.Method(impl, "GetHashCode", source); if (m is null) { ResolutionError = $"`GetHashCode` method not found for: {typeof(A).Name}"; MakeDefault(); return; } GetHashCodeMethod = m; GetHashCodeMethodPtr = m.MethodHandle.GetFunctionPointer(); GetHashCodeFunc = x => (int?)GetHashCodeMethod.Invoke(null, [x]) ?? throw new InvalidOperationException(); } static void MakeDefault() { EqualsFunc = EqualityComparer.Default.Equals; EqualsMethod = EqualsFunc.Method; EqualsMethodPtr = EqualsFunc.Method.MethodHandle.GetFunctionPointer(); GetHashCodeFunc = DefaultGetHashCode; GetHashCodeMethod = GetHashCodeFunc.Method; GetHashCodeMethodPtr = GetHashCodeFunc.Method.MethodHandle.GetFunctionPointer(); } static int DefaultGetHashCode(A value) => value is null ? 0 : value.GetHashCode(); } ================================================ FILE: LanguageExt.Core/Traits/Resolve/HashableResolver.cs ================================================ using System; using System.Collections.Generic; using System.Reflection; namespace LanguageExt.Traits.Resolve; public static class HashableResolve { public static string? ResolutionError; public static Func GetHashCodeFunc = null!; public static MethodInfo GetHashCodeMethod = null!; public static nint GetHashCodeMethodPtr; public static int GetHashCode(A value) => GetHashCodeFunc(value); public static bool Exists => ResolutionError is null; static HashableResolve() { var source = typeof(A); var impl = Resolver.Find(source, "Hashable"); if (impl is null) { ResolutionError = $"Trait implementation not found for: {typeof(A).Name}"; MakeDefault(); return; } var m = Resolver.Method(impl, "GetHashCode", source); if (m is null) { ResolutionError = $"`GetHashCode` method not found for: {typeof(A).Name}"; MakeDefault(); return; } GetHashCodeMethod = m; GetHashCodeMethodPtr = m.MethodHandle.GetFunctionPointer(); GetHashCodeFunc = x => (int?)GetHashCodeMethod.Invoke(null, [x]) ?? throw new InvalidOperationException(); } static void MakeDefault() { GetHashCodeFunc = DefaultGetHashCode; GetHashCodeMethod = GetHashCodeFunc.Method; GetHashCodeMethodPtr = GetHashCodeFunc.Method.MethodHandle.GetFunctionPointer(); } static int DefaultGetHashCode(A value) => value is null ? 0 : value.GetHashCode(); } ================================================ FILE: LanguageExt.Core/Traits/Resolve/OrdResolver.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Reflection; namespace LanguageExt.Traits.Resolve; public static class OrdResolve { public static string? ResolutionError; public static Func GetHashCodeFunc = null!; public static MethodInfo GetHashCodeMethod = null!; public static nint GetHashCodeMethodPtr; public static Func EqualsFunc = null!; public static MethodInfo EqualsMethod = null!; public static nint EqualsMethodPtr; public static Func CompareFunc = null!; public static MethodInfo CompareMethod = null!; public static nint CompareMethodPtr; public static int GetHashCode(A value) => GetHashCodeFunc(value); public static bool Equals(A lhs, A rhs) => EqualsFunc(lhs, rhs); public static int Compare(A lhs, A rhs) => CompareFunc(lhs, rhs); public static bool Exists => ResolutionError is null; static OrdResolve() { var source = typeof(A); if(source.FullName?.StartsWith("LanguageExt.Traits.K") ?? false) { MakeTraitDefault(); return; } if (typeof(Delegate).IsAssignableFrom(source)) { MakeDelegateDefault(); return; } MakeComparer(source); } static void MakeComparer(Type source) { var impl = Resolver.Find(source, "Ord"); if (impl is null) { ResolutionError = $"Trait implementation not found for: {source.Name}"; MakeDefault(); return; } // Compare var m = Resolver.Method(impl, "Compare", source, source); if (m is null) { ResolutionError = $"`Compare` method not found for: {source.Name}"; MakeDefault(); return; } CompareMethod = m; CompareMethodPtr = m.MethodHandle.GetFunctionPointer(); CompareFunc = (x, y) => (int?)CompareMethod.Invoke(null, [x, y]) ?? throw new InvalidOperationException(); // Equals m = Resolver.Method(impl, "Equals", source, source); if (m is null) { ResolutionError = $"`Equals` method not found for: {source.Name}"; MakeDefault(); return; } EqualsMethod = m; EqualsMethodPtr = m.MethodHandle.GetFunctionPointer(); EqualsFunc = (x, y) => (bool?)EqualsMethod.Invoke(null, [x, y]) ?? throw new InvalidOperationException(); // GetHashCode m = Resolver.Method(impl, "GetHashCode", source); if (m is null) { ResolutionError = $"`GetHashCode` method not found for: {source.Name}"; MakeDefault(); return; } GetHashCodeMethod = m; GetHashCodeMethodPtr = m.MethodHandle.GetFunctionPointer(); GetHashCodeFunc = x => (int?)GetHashCodeMethod.Invoke(null, [x]) ?? throw new InvalidOperationException(); } static void MakeDefault() { CompareFunc = Comparer.Default.Compare; CompareMethod = CompareFunc.Method; CompareMethodPtr = CompareFunc.Method.MethodHandle.GetFunctionPointer(); EqualsFunc = EqualityComparer.Default.Equals; EqualsMethod = EqualsFunc.Method; EqualsMethodPtr = EqualsFunc.Method.MethodHandle.GetFunctionPointer(); GetHashCodeFunc = DefaultGetHashCode; GetHashCodeMethod = GetHashCodeFunc.Method; GetHashCodeMethodPtr = GetHashCodeFunc.Method.MethodHandle.GetFunctionPointer(); } static void MakeDelegateDefault() { CompareFunc = (x, y) => ((object?)x, (object?)y) switch { (Delegate dx, Delegate dy) => dx.Method.MetadataToken.CompareTo(dy.Method.MetadataToken), _ => -1 }; CompareMethod = CompareFunc.Method; CompareMethodPtr = CompareFunc.Method.MethodHandle.GetFunctionPointer(); EqualsFunc = (x, y) => ((object?)x, (object?)y) switch { (Delegate dx, Delegate dy) => dx.Method.MetadataToken == dy.Method.MetadataToken, _ => false }; EqualsMethod = EqualsFunc.Method; EqualsMethodPtr = EqualsFunc.Method.MethodHandle.GetFunctionPointer(); GetHashCodeFunc = DefaultGetHashCode; GetHashCodeMethod = GetHashCodeFunc.Method; GetHashCodeMethodPtr = GetHashCodeFunc.Method.MethodHandle.GetFunctionPointer(); } static void MakeTraitDefault() { var gens = typeof(A).GetGenericArguments(); var fname = gens[0].FullName; var tick = fname?.IndexOf('`') ?? -1; var iname = tick >= 0 ? fname?.Substring(0, tick) ?? "" : fname; var tgens = gens[0].GetGenericArguments(); var gtype = gens[0].Assembly.GetType($"{iname}`{tgens.Length + 1}"); var ngens = tgens.Concat([gens[1]]).ToArray(); var type = gtype!.MakeGenericType(ngens); var resolver = typeof(OrdResolve<>).MakeGenericType(type); var getHashCodeObj = ((Delegate?)resolver.GetField("GetHashCodeFunc")?.GetValue(null) ?? throw new InvalidOperationException()).Target; var equalsObj = ((Delegate?)resolver.GetField("EqualsFunc")?.GetValue(null) ?? throw new InvalidOperationException()).Target; var compareObj = ((Delegate?)resolver.GetField("CompareFunc")?.GetValue(null) ?? throw new InvalidOperationException()).Target; var getHashCodeMethod = (MethodInfo?)resolver.GetField("GetHashCodeMethod")?.GetValue(null) ?? throw new InvalidOperationException(); var equalsMethod = (MethodInfo?)resolver.GetField("EqualsMethod")?.GetValue(null) ?? throw new InvalidOperationException(); var compareMethod = (MethodInfo?)resolver.GetField("CompareMethod")?.GetValue(null) ?? throw new InvalidOperationException(); GetHashCodeFunc = x => (int?)getHashCodeMethod.Invoke(getHashCodeObj, [x]) ?? throw new InvalidOperationException(); GetHashCodeMethod = GetHashCodeFunc.Method; GetHashCodeMethodPtr = GetHashCodeFunc.Method.MethodHandle.GetFunctionPointer(); EqualsFunc = (x, y) => (bool?)equalsMethod.Invoke(equalsObj, [x, y]) ?? throw new InvalidOperationException(); EqualsMethod = EqualsFunc.Method; EqualsMethodPtr = EqualsFunc.Method.MethodHandle.GetFunctionPointer(); CompareFunc = (x, y) => (int?)compareMethod.Invoke(compareObj, [x, y]) ?? throw new InvalidOperationException(); CompareMethod = CompareFunc.Method; CompareMethodPtr = CompareFunc.Method.MethodHandle.GetFunctionPointer(); } static int DefaultGetHashCode(A value) => value is null ? 0 : value.GetHashCode(); } ================================================ FILE: LanguageExt.Core/Traits/Resolve/Resolver.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Reflection; namespace LanguageExt.Traits.Resolve; internal static class Resolver { public static MethodInfo? Method(Type? type, string name, params Type[] types) => type?.GetMethod(name, BindingFlags.Static | BindingFlags.Public, types); public static Type? Find(Type type, string prefix = "") { var n = $"{prefix}{type.Name}"; var t = FindType(type.Assembly, n); if (t is not null) return MakeGeneric(t, type); var typeAsmName = type.Assembly.GetName(); foreach (var name in GetAssemblies().Where(asm => asm != typeAsmName)) { t = FindType(LoadAssembly(name), n); if (t != null) return MakeGeneric(t, type); } return null; } static Type MakeGeneric(Type generic, Type concrete) => generic.IsGenericType ? generic.MakeGenericType(concrete.GetGenericArguments()) : concrete; static Type? FindType(Assembly? asm, string name) { if (asm is null) return null; var types = asm.DefinedTypes .Where(t => t.IsClass || t.IsValueType) .Where(t => t.Name == name) .ToArray(); return types.Length switch { 0 => null, 1 => types[0], _ => null }; } static Assembly? LoadAssembly(AssemblyName name) { try { return Assembly.Load(name); } catch { return null; } } static IEnumerable GetAssemblies() { var asmNames = Assembly.GetExecutingAssembly().GetReferencedAssemblies() .Concat(Assembly.GetCallingAssembly().GetReferencedAssemblies()) .Concat(Assembly.GetEntryAssembly()?.GetReferencedAssemblies() ?? []) .Distinct(); var init = new[] { Assembly.GetExecutingAssembly().GetName(), Assembly.GetCallingAssembly().GetName(), Assembly.GetEntryAssembly()?.GetName() }; foreach (var asm in init.Concat(asmNames).Where(n => n is not null).Distinct()) { if (asm is not null) yield return asm; } } public static MethodInfo? GetHashCodeMethod(Type type) { Type[] traits = [typeof(HashableResolve<>), typeof(EqResolve<>), typeof(OrdResolve<>)]; foreach (var trait in traits) { var impl = trait.MakeGenericType(type); var exists = (bool?)impl.GetProperty("Exists")?.GetValue(null) ?? false; if (exists) { var method = impl.GetMethod("GetHashCode", BindingFlags.Static | BindingFlags.Public, [type]); if (method is not null) return method; } } return null; } public static MethodInfo GetHashCodeMethodAlways(Type type) { Type[] traits = [typeof(HashableResolve<>), typeof(EqResolve<>), typeof(OrdResolve<>)]; foreach (var trait in traits) { var impl = trait.MakeGenericType(type); var exists = (bool?)impl.GetProperty("Exists")?.GetValue(null) ?? false; if (exists) { var method = impl.GetMethod("GetHashCode", BindingFlags.Static | BindingFlags.Public, [type]); if (method is not null) return method; } } var impl2 = typeof(HashableResolve<>).MakeGenericType(type); return impl2.GetMethod("GetHashCode", BindingFlags.Static | BindingFlags.Public, [type]) ?? throw new InvalidOperationException(); } public static MethodInfo? GetEqualsMethod(Type type) { Type[] traits = [typeof(EqResolve<>), typeof(OrdResolve<>)]; foreach (var trait in traits) { var impl = trait.MakeGenericType(type); var exists = (bool?)impl.GetProperty("Exists")?.GetValue(null) ?? false; if (exists) { var method = impl.GetMethod("Equals", BindingFlags.Static | BindingFlags.Public, [type, type]); if (method is not null) return method; } } return null; } public static MethodInfo GetEqualsMethodAlways(Type type) { Type[] traits = [typeof(EqResolve<>), typeof(OrdResolve<>)]; foreach (var trait in traits) { var impl = trait.MakeGenericType(type); var exists = (bool?)impl.GetProperty("Exists")?.GetValue(null) ?? false; if (exists) { var method = impl.GetMethod("Equals", BindingFlags.Static | BindingFlags.Public, [type, type]); if (method is not null) return method; } } var impl2 = typeof(EqResolve<>).MakeGenericType(type); return impl2.GetMethod("Equals", BindingFlags.Static | BindingFlags.Public, [type, type]) ?? throw new InvalidOperationException(); } public static MethodInfo? GetCompareMethod(Type type) { Type[] traits = [typeof(OrdResolve<>)]; foreach (var trait in traits) { var impl = trait.MakeGenericType(type); var exists = (bool?)impl.GetProperty("Exists")?.GetValue(null) ?? false; if (exists) { var method = impl.GetMethod("Compare", BindingFlags.Static | BindingFlags.Public, [type, type]); if (method is not null) return method; } } return null; } public static MethodInfo GetCompareMethodAlways(Type type) { var impl = typeof(OrdResolve<>).MakeGenericType(type); var method = impl.GetMethod("Compare", BindingFlags.Static | BindingFlags.Public, [type, type]); return method ?? throw new InvalidOperationException(); } } ================================================ FILE: LanguageExt.Core/Traits/Semigroup/Semigroup.Instance.cs ================================================ using System; using static LanguageExt.Prelude; namespace LanguageExt.Traits; /// /// Contains the trait in record form. This allows the trait to be passed /// around as a value rather than resolved as a type. It helps us get around limitations /// in the C# constraint system. /// /// An associative binary operation. /// Trait type public record SemigroupInstance(Func Combine) { /// /// The `A` type should derive from `Semigroup〈A〉`. If so, we can get a `SemigroupInstance〈A〉` that we can /// pass around as a value. If not, then we will get `None`, which means the type is not a semigroup. /// public static Option> Instance { get; } = // NOTE: I don't like this, but it's the only way I can think of to do ad hoc trait resolution Try.lift(GetInstance) .ToOption() .Bind(x => x is null ? None : Some(x)); static SemigroupInstance? GetInstance() { var type = typeof(Semigroup<>).MakeGenericType(typeof(A)); var prop = type.GetProperty("Instance"); var value = prop?.GetValue(null); return (SemigroupInstance?)value; } } ================================================ FILE: LanguageExt.Core/Traits/Semigroup/Semigroup.Module.cs ================================================ using LanguageExt.Traits; using System.Collections.Generic; using System.Diagnostics.Contracts; namespace LanguageExt; public static partial class Semigroup { /// /// An associative binary operation /// /// The left hand side of the operation /// The right hand side of the operation /// The result of the operation [Pure] public static A combine(A x, A y) where A : Semigroup => x + y; /// /// Get a concrete semigroup instance value from a semigroup supporting trait-type /// /// Semigroup type /// Semigroup instance that can be passed around as a value [Pure] public static SemigroupInstance instance() where A : Semigroup => A.Instance; } ================================================ FILE: LanguageExt.Core/Traits/Semigroup/Semigroup.Operators.cs ================================================ using LanguageExt.Traits; namespace LanguageExt; public static class SemigroupExtensions { extension(A _) where A : Semigroup { /// /// Semigroup combine operator: an associative binary operation. /// /// Left-hand side operand /// Right-hand side operand /// public static A operator +(A lhs, A rhs) => lhs.Combine(rhs); } } ================================================ FILE: LanguageExt.Core/Traits/Semigroup/Semigroup.Prelude.cs ================================================ using LanguageExt.Traits; using System.Collections.Generic; using System.Diagnostics.Contracts; namespace LanguageExt; public static partial class Prelude { /// /// An associative binary operation /// /// The left hand side of the operation /// The right hand side of the operation /// The result of the operation [Pure] public static A combine(A x, A y) where A : Semigroup => x + y; /// /// An associative binary operation /// /// Left-hand side of the operation /// Right-hand side of the operation /// lhs + rhs [Pure] public static Either combine(Either lhs, Either rhs) where R : Semigroup => from x in lhs from y in rhs select x + y; /// /// An associative binary operation /// /// The left hand side of the operation /// The right hand side of the operation /// The result of the operation [Pure] public static Option combine(Option x, Option y) where A : Semigroup => from a in x from b in y select a + b; /// /// An associative binary operation /// /// The left hand side of the operation /// The right hand side of the operation /// The result of the operation [Pure] public static IEnumerable combine(IEnumerable x, IEnumerable y) where A : Semigroup { foreach (var a in x) foreach (var b in y) yield return a + b; } } ================================================ FILE: LanguageExt.Core/Traits/Semigroup/Semigroup.cs ================================================ using System.Diagnostics.Contracts; namespace LanguageExt.Traits; public interface Semigroup where A : Semigroup { /// /// An associative binary operation. /// /// The first operand to the operation /// The second operand to the operation /// The result of the operation [Pure] public A Combine(A rhs); /// /// An associative binary operation. /// /// The first operand to the operation /// The second operand to the operation /// The result of the operation [Pure] public static virtual A operator +(A lhs, A rhs) => lhs.Combine(rhs); /// /// Property that contains the trait in record form. This allows the trait to be passed /// around as a value rather than resolved as a type. It helps us get around limitations /// in the C# constraint system. /// [Pure] public static virtual SemigroupInstance Instance { get; } = new(Combine: Semigroup.combine); } ================================================ FILE: LanguageExt.Core/Traits/SemigroupK/SemigroupK.Extensions.cs ================================================ using LanguageExt.Traits; using System.Collections.Generic; using System.Diagnostics.Contracts; namespace LanguageExt; public static partial class SemigroupKExtensions { /// /// An associative binary operation /// /// The left-hand side of the operation /// The right-hand side of the operation /// The result of the operation [Pure] public static K Combine(this K mx, K my) where M : SemigroupK => M.Combine(mx, my); } ================================================ FILE: LanguageExt.Core/Traits/SemigroupK/SemigroupK.Module.cs ================================================ using LanguageExt.Traits; using System.Collections.Generic; using System.Diagnostics.Contracts; namespace LanguageExt; public static partial class SemigroupK { /// /// An associative binary operation /// /// The left-hand side of the operation /// The right-hand side of the operation /// The result of the operation [Pure] public static K combine(K mx, K my) where M : SemigroupK => M.Combine(mx, my); } ================================================ FILE: LanguageExt.Core/Traits/SemigroupK/SemigroupK.Operators.cs ================================================ using LanguageExt.Traits; namespace LanguageExt; public static partial class SemigroupKExtensions { extension(K _) where F : SemigroupK { /// /// Semigroup combine operator: an associative binary operation. /// /// Left-hand side operand /// Right-hand side operand /// public static K operator +(K lhs, K rhs) => lhs.Combine(rhs); } } ================================================ FILE: LanguageExt.Core/Traits/SemigroupK/SemigroupK.Prelude.cs ================================================ using LanguageExt.Traits; using System.Collections.Generic; using System.Diagnostics.Contracts; namespace LanguageExt; public static partial class Prelude { /// /// An associative binary operation /// /// The left hand side of the operation /// The right hand side of the operation /// The result of the operation [Pure] public static K combine(K mx, K my) where M : SemigroupK => M.Combine(mx, my); } ================================================ FILE: LanguageExt.Core/Traits/SemigroupK/SemigroupK.cs ================================================ using System.Diagnostics.Contracts; namespace LanguageExt.Traits; /// /// Equivalent of semigroups for working with higher-kinded types /// /// Higher kind public interface SemigroupK where M : SemigroupK { /// /// An associative binary operation. /// /// The first operand to the operation /// The second operand to the operation /// The result of the operation [Pure] public static abstract K Combine(K lhs, K rhs); } ================================================ FILE: LanguageExt.Core/Traits/Stateful/Stateful.Extensions.cs ================================================ using LanguageExt.Traits; namespace LanguageExt; public static partial class StatefulExtensions { /// /// Runs the `stateSetter` to update the state-monad's inner state. Then runs the /// `operation`. And finally, resets the state to how it was before running `stateSetter`. /// /// /// The result of `operation` /// public static K Local(this K stateSetter, K operation) where M : Stateful, Monad => from s in M.Get from _ in stateSetter from r in operation from u in M.Put(s) select r; } ================================================ FILE: LanguageExt.Core/Traits/Stateful/Stateful.Module.cs ================================================ using System; namespace LanguageExt.Traits; public static partial class Stateful { public static K put(S value) where M : Stateful => M.Put(value); public static K modify(Func modify) where M : Stateful => M.Modify(modify); public static K modifyM(Func> modify) where M : Stateful, Monad => from s in M.Get from t in modify(s) from _ in M.Put(t) select default(Unit); public static K get() where M : Stateful => M.Get; public static K gets(Func f) where M : Stateful => M.Gets(f); public static K getsM(Func> f) where M : Stateful, Monad => M.Flatten(M.Gets(f)); public static K state(Func f) where M : Stateful, Monad => from s in M.Get let r = f(s) from _ in M.Put(r.State) select r.Value; /// /// Runs the `stateSetter` to update the state-monad's inner state. Then runs the /// `operation`. And finally, resets the state to how it was before running `stateSetter`. /// /// /// The result of `operation` /// public static K local(K stateSetter, K operation) where M : Stateful, Monad => from s in M.Get from _ in stateSetter from r in operation from u in M.Put(s) select r; /// /// Runs the `stateSetter` to update the state-monad's inner state. Then runs the /// `operation`. And finally, resets the state to how it was before running `stateSetter`. /// /// /// The result of `operation` /// public static K local(Func stateSetter, K operation) where M : Stateful, Monad => from s in M.Get from _ in M.Put(stateSetter(s)) from r in operation from u in M.Put(s) select r; } ================================================ FILE: LanguageExt.Core/Traits/Stateful/Stateful.Trait.cs ================================================ using System; using static LanguageExt.Prelude; namespace LanguageExt.Traits; public interface Stateful where M : Stateful { public static abstract K Put(S value); public static abstract K Modify(Func modify) ; public static abstract K Gets(Func f); public static virtual K Get => Stateful.gets(identity); } ================================================ FILE: LanguageExt.Core/Traits/TokenStream/TokenStream.Module.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public class TokenStream { /// /// Lift a single token to chunk of the stream /// public static S tokenToChunk(in A token) where S : TokenStream => S.TokenToChunk(token); /// /// Lift many tokens to chunk of the stream /// public static S tokensToChunk(in ReadOnlySpan token) where S : TokenStream => S.TokensToChunk(token); /// /// Turn a chunk into a sequence of tokens /// public static ReadOnlySpan chunkToTokens(in S tokens) where S : TokenStream => S.ChunkToTokens(tokens); /// /// Get the length of a chunk /// public static int chunkLength(in S tokens) where S : TokenStream => S.ChunkLength(tokens); /// /// Is the chunk empty? /// public static bool chunkEmpty(in S tokens) where S : TokenStream => S.ChunkLength(tokens) <= 0; /// /// Take the first element of the stream if it exists. /// /// Stream /// Head element taken from the stream and a Tail of remaining stream items public static bool take1(in S stream, out A head, out S tail) where S : TokenStream => S.Take1(stream, out head, out tail); /// /// `Take` should try to extract a chunk of length `amount`, or if the /// stream is too short, the rest of the stream. Valid implementation /// should follow the rules: /// /// * If the requested length `amount` is `0` (or less), `None` should never be returned, instead `Some([], s)` /// should be returned, where `[]` stands for the empty chunk, and `s` is the original stream (second argument). /// * If the requested length is greater than `0` and the stream is empty, `None` should be returned indicating end-of-input. /// * In other cases, take chunk of length `amount` (or shorter if the stream is not long enough) from the input /// stream and return the chunk along with the rest of the stream. /// /// Number of elements to take /// Stream /// Head element taken from the stream and a Tail of remaining stream items public static bool take(int amount, in S stream, out S head, out S tail) where S : TokenStream => S.Take(amount, stream, out head, out tail); /// /// Extract chunk of the stream taking tokens while the supplied predicate returns 'True'. Return the chunk and the /// rest of the stream. /// /// For many types of streams, the method allows for significant performance improvements, although it is not /// strictly necessary from a conceptual point-of-view. /// /// Token testing predicate /// Stream to read from /// public static void takeWhile(Func predicate, in S stream, out S head, out S tail) where S : TokenStream => S.TakeWhile(predicate, stream, out head, out tail); } ================================================ FILE: LanguageExt.Core/Traits/TokenStream/TokenStream.cs ================================================ using System; namespace LanguageExt.Traits; /// /// Low-level streaming trait. Used primarily by Megaparsec. /// /// Token type public interface TokenStream where TOKENS : TokenStream { /// /// If the stream supports the concept of tabs, then this function /// should return true if the token is a tab. /// /// Token to test /// True if a tab public static abstract bool IsTab(TOKEN token); /// /// If the stream supports the concept of newlines, then this function /// should return true if the token is a newline. /// /// Token to test /// True if a newline public static abstract bool IsNewline(TOKEN token); /// /// Create a textual respresentation of the token /// /// Token /// Text public static abstract ReadOnlySpan TokenToString(TOKEN token); /// /// Lift a single token to chunk of the stream /// public static abstract TOKENS TokenToChunk(in TOKEN token); /// /// Lift many tokens to chunk of the stream /// public static abstract TOKENS TokensToChunk(in ReadOnlySpan token); /// /// Turn a chunk into a sequence of tokens /// public static abstract ReadOnlySpan ChunkToTokens(in TOKENS tokens); /// /// Get the length of a chunk /// public static abstract int ChunkLength(in TOKENS tokens); /// /// Take the first element of the stream if it exists. /// /// Stream /// Head element taken from the stream and a Tail of remaining stream items public static abstract bool Take1(in TOKENS stream, out TOKEN head, out TOKENS tail); /// /// `Take` should try to extract a chunk of length `amount`, or if the /// stream is too short, the rest of the stream. Valid implementation /// should follow the rules: /// /// * If the requested length `amount` is `0` (or less), `None` should never be returned, instead `Some([], s)` /// should be returned, where `[]` stands for the empty chunk, and `s` is the original stream (second argument). /// * If the requested length is greater than `0` and the stream is empty, `None` should be returned indicating end-of-input. /// * In other cases, take chunk of length `amount` (or shorter if the stream is not long enough) from the input /// stream and return the chunk along with the rest of the stream. /// /// Number of elements to take /// Stream /// Head element taken from the stream and a Tail of remaining stream items public static abstract bool Take(int amount, in TOKENS stream, out TOKENS head, out TOKENS tail); /// /// Extract chunk of the stream taking tokens while the supplied predicate returns 'True'. Return the chunk and the /// rest of the stream. /// /// For many types of streams, the method allows for significant performance improvements, although it is not /// strictly necessary from a conceptual point-of-view. /// /// Token testing predicate /// Stream to read from /// public static abstract void TakeWhile(Func predicate, in TOKENS stream, out TOKENS head, out TOKENS tail); } ================================================ FILE: LanguageExt.Core/Traits/Trait.cs ================================================ namespace LanguageExt.Traits; public interface Trait; ================================================ FILE: LanguageExt.Core/Traits/TraitAttribute.cs ================================================ using System; namespace LanguageExt.Attributes; [AttributeUsage(AttributeTargets.Interface)] public class TraitAttribute : Attribute { public readonly string NameFormat; public TraitAttribute(string nameFormat) => NameFormat = nameFormat; } ================================================ FILE: LanguageExt.Core/Traits/Traversable/README.md ================================================ Traversable structures support element-wise sequencing of `Applicative` effects (thus also `Monad` effects) to construct new structures of the same shape as the input. To illustrate what is meant by same shape, if the input structure is `[a]`, each output structure is a list `[b]` of the same length as the input. If the input is a `Tree`, each output `Tree` has the same graph of intermediate nodes and leaves. Similarly, if the input is a tuple `(x, a)`, each output is a tuple `(x, b)`, and so forth. Every Traversable structure is both a `Functor` and `Foldable`. ================================================ FILE: LanguageExt.Core/Traits/Traversable/Traversable.Extensions.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; /// /// Functors representing data structures that can be transformed to structures of the same /// shape by performing an `Applicative` (or, therefore, `Monad`) action on each element from /// left to right. /// /// A more detailed description of what same shape means, the various methods, how traversals /// are constructed, and example advanced use-cases can be found in the Overview section of Data.Traversable. /// /// public static partial class TraversableExtensions { public static K> Traverse( this K ta, Func> f) where T : Traversable where F : Applicative => Traversable.traverse(f, ta); public static K> Sequence( this K> ta) where T : Traversable where F : Applicative => Traversable.sequence(ta); public static K> TraverseM( this Func> f, K ta) where T : Traversable where M : Monad => Traversable.traverseM(f, ta); public static K> SequenceM( this K> ta) where T : Traversable where F : Monad => Traversable.sequenceM(ta); } ================================================ FILE: LanguageExt.Core/Traits/Traversable/Traversable.Module.cs ================================================ using System; namespace LanguageExt.Traits; public static partial class Traversable { /// /// Map each element of a structure to an action, evaluate these actions from /// left to right, and collect the results. /// /// /// This version of `traverse` works with the lifted `K` types which are /// sometimes difficult to work with when nested. If you need to get concrete /// types out of your traversal operation then use `traverse2` - it needs more /// generic parameters but it retains the concrete types. /// /// /// Traversable structure /// Traversable trait /// Applicative functor trait /// Bound value (input) /// Bound value (output) /// public static K> traverse( Func> f, K ta) where T : Traversable where F : Applicative => T.Traverse(f, ta); /// /// Evaluate each action in the structure from left to right, and collect the results. /// /// Traversable structure /// Traversable trait /// Applicative functor trait /// Bound value (input) /// public static K> sequence( K> ta) where T : Traversable where F : Applicative => T.Sequence(ta); /// /// Map each element of a structure to a monadic action, evaluate these actions from /// left to right, and collect the results. /// /// /// Traversable structure /// Traversable trait /// Monad trait /// Bound value (input) /// Bound value (output) /// public static K> traverseM( Func> f, K ta) where T : Traversable where M : Monad => T.TraverseM(f, ta); /// /// Evaluate each monadic action in the structure from left to right, and collect the results. /// /// Traversable structure /// Traversable trait /// Monad trait /// Bound value (input) /// public static K> sequenceM( K> ta) where T : Traversable where M : Monad => T.SequenceM(ta); } ================================================ FILE: LanguageExt.Core/Traits/Traversable/Traversable.Trait.cs ================================================ using System; namespace LanguageExt.Traits; /// /// Functors representing data structures that can be transformed to structures of the same /// shape by performing an `Applicative` (or, therefore, `Monad`) action on each element from /// left to right. /// /// A more detailed description of what same shape means, the various methods, how traversals /// are constructed, and example advanced use-cases can be found in the Overview section of Data.Traversable. /// /// public interface Traversable : Functor, Foldable where T : Traversable, Functor, Foldable { public static abstract K> Traverse( Func> f, K ta) where F : Applicative; public static virtual K> Sequence( K> ta) where F : Applicative => Traversable.traverse(x => x, ta); public static virtual K> TraverseM( Func> f, K ta) where M : Monad => Traversable.traverse(f, ta); public static virtual K> SequenceM( K> ta) where F : Monad => Traversable.traverseM(x => x, ta); public static virtual K> TraverseDefault( Func> f, K ta) where F : Applicative => Traversable.sequence(T.Map(f, ta)); } ================================================ FILE: LanguageExt.Core/Traits/Writable/Writable.Extensions.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public static partial class WritableExtensions { /// /// `pass` is an action that executes the `action`, which returns a value and a /// function; it then returns the value with the output having been applied to /// the function. /// public static K Pass(this K Function)> action) where M : Writable where W : Monoid => M.Pass(action); /// /// `listens` executes the action @m@ and adds the result of applying `f` to the /// output to the value of the computation. /// public static K Listens(this Func f, K ma) where M : Writable, Monad where W : Monoid => M.Bind(M.Listen(ma), aw => M.Pure((aw.Value, f(aw.Output)))); /// /// `listens` executes the action @m@ and adds the result of applying `f` to the /// output to the value of the computation. /// public static K Listens(this K ma, Func f) where M : Writable, Monad where W : Monoid => M.Bind(M.Listen(ma), aw => M.Pure((aw.Value, f(aw.Output)))); /// /// `censor` executes the action `ma` and applies the function `f` to its output, /// leaving the return value unchanged. /// public static K Censor(this K ma, Func f) where M : Writable, Monad where W : Monoid => M.Pass(M.Bind(ma, a => M.Pure((a, f)))); /// /// `censor` executes the action `ma` and applies the function `f` to its output, /// leaving the return value unchanged. /// public static K Censor(this Func f, K ma) where M : Writable, Monad where W : Monoid => M.Pass(M.Bind(ma, a => M.Pure((a, f))));} ================================================ FILE: LanguageExt.Core/Traits/Writable/Writable.Module.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt.Traits; public static partial class Writable { /// /// Tell is an action that produces the writer output /// /// Item to tell /// Writer type /// Structure with the told item public static K tell(W item) where M : Writable where W : Monoid => M.Tell(item); /// /// Writes an item and returns a value at the same time /// public static K write((A, W) item) where M : Writable, Monad where W : Monoid => M.Bind(M.Tell(item.Item2), _ => M.Pure(item.Item1)); /// /// Writes an item and returns a value at the same time /// public static K write(A value, W item) where M : Writable, Monad where W : Monoid => M.Bind(M.Tell(item), _ => M.Pure(value)); /// /// `pass` is an action that executes the `action`, which returns a value and a /// function; it then returns the value with the output having been applied to /// the function. /// public static K pass(K Function)> action) where M : Writable where W : Monoid => M.Pass(action); /// /// Writes an item and returns a value at the same time /// public static K listen(K ma) where M : Writable where W : Monoid => M.Listen(ma); /// /// `listens` executes the action `ma` and adds the result of applying `f` to the /// output to the value of the computation. /// public static K listens(Func f, K ma) where M : Writable, Monad where W : Monoid => M.Bind(M.Listen(ma), aw => M.Pure((aw.Value, f(aw.Output)))); /// /// `censor` executes the action `ma` and applies the function `f` to its output, /// leaving the return value unchanged. /// public static K censor(Func f, K ma) where M : Writable, Monad where W : Monoid => M.Pass(M.Bind(ma, a => M.Pure((a, f)))); } ================================================ FILE: LanguageExt.Core/Traits/Writable/Writable.Trait.cs ================================================ using System; namespace LanguageExt.Traits; /// /// WriterM trait /// /// `Tell` is how you log to the writer's output. The `WriterM` carries /// this 'packet' upwards, merging it if needed (hence the `Monoid` /// requirement). /// /// `Listen` listens to a monad acting, and returns what the monad "said". /// /// `Pass` lets you provide a writer transformer which changes internals of /// the written object. /// /// Writer self trait /// Monoidal output type public interface Writable where M : Writable where W : Monoid { /// /// Tell is an action that produces the writer output /// /// Item to tell /// Writer type /// Structure with the told item public static abstract K Tell(W item); /// /// Writes an item and returns a value at the same time /// public static abstract K Listen(K ma); /// /// `Pass` is an action that executes the `action`, which returns a value and a /// function; it then returns the value with the output having been applied to /// the function. /// /// /// For usage, see `Writer.censor` for how it's used to filter the output. /// public static abstract K Pass(K Function)> action); } ================================================ FILE: LanguageExt.Core/Units of Measure/Accel.cs ================================================ using System; namespace LanguageExt; /// /// Numeric acceleration value /// Handles unit conversions automatically /// Internally all speeds are stored as metres per-second squared /// All standard arithmetic operators work on the Accel /// type. So keep all accelerations wrapped until you need the /// value, then extract using various unit-of-measure /// accessors (MetresPerSecond2, etc.) or divide by 1.MetresPerSecond2() /// public readonly struct Accel : IComparable, IEquatable, IComparable { readonly double Value; internal Accel(double value) => Value = value; public override string ToString() => Value + " m/s²"; public bool Equals(Accel other) => Value.Equals(other.Value); public bool Equals(Accel other, double epsilon) => Math.Abs(other.Value - Value) < epsilon; public override bool Equals(object? obj) => obj is Accel accel && Equals(accel); public override int GetHashCode() => Value.GetHashCode(); public int CompareTo(object? obj) => obj switch { null => 1, Accel other => CompareTo(other), _ => throw new ArgumentException($"must be of type {nameof(Accel)}") }; public int CompareTo(Accel other) => Value.CompareTo(other.Value); public Accel Add(Accel rhs) => new (Value + rhs.Value); public Accel Subtract(Accel rhs) => new (Value - rhs.Value); public Accel Multiply(double rhs) => new (Value * rhs); public Accel Divide(double rhs) => new (Value / rhs); public static Accel operator *(Accel lhs, double rhs) => lhs.Multiply(rhs); public static Accel operator *(double lhs, Accel rhs) => rhs.Multiply(lhs); public static Velocity operator *(Accel lhs, Time rhs) => new (lhs.Value * rhs.Seconds); public static Velocity operator *(Time lhs, Accel rhs) => new (lhs.Seconds * rhs.Value); public static VelocitySq operator *(Accel lhs, Length rhs) => new (lhs.Value * rhs.Metres); public static VelocitySq operator *(Length lhs, Accel rhs) => new (rhs.Value * lhs.Metres); public static Length operator *(Accel lhs, TimeSq rhs) => new (lhs.Value * rhs.Seconds2); public static Length operator *(TimeSq lhs, Accel rhs) => new (rhs.Value * lhs.Seconds2); public static Accel operator +(Accel lhs, Accel rhs) => lhs.Add(rhs); public static Accel operator -(Accel lhs, Accel rhs) => lhs.Subtract(rhs); public static Accel operator /(Accel lhs, double rhs) => lhs.Divide(rhs); public static double operator /(Accel lhs, Accel rhs) => lhs.Value / rhs.Value; public static bool operator ==(Accel lhs, Accel rhs) => lhs.Equals(rhs); public static bool operator !=(Accel lhs, Accel rhs) => !lhs.Equals(rhs); public static bool operator >(Accel lhs, Accel rhs) => lhs.Value > rhs.Value; public static bool operator <(Accel lhs, Accel rhs) => lhs.Value < rhs.Value; public static bool operator >=(Accel lhs, Accel rhs) => lhs.Value >= rhs.Value; public static bool operator <=(Accel lhs, Accel rhs) => lhs.Value <= rhs.Value; public Accel Pow(double power) => new (Math.Pow(Value,power)); public Accel Round() => new (Math.Round(Value)); public Accel Sqrt() => new (Math.Sqrt(Value)); public Accel Abs() => new (Math.Abs(Value)); public Accel Min(Accel rhs) => new (Math.Min(Value, rhs.Value)); public Accel Max(Accel rhs) => new (Math.Max(Value, rhs.Value)); public double MetresPerSecond2 => Value; } public static class UnitsAccelExtensions { public static Accel MetresPerSecond2(this int self) => new (self); public static Accel MetresPerSecond2(this float self) => new (self); public static Accel MetresPerSecond2(this double self) => new (self); } ================================================ FILE: LanguageExt.Core/Units of Measure/Area.cs ================================================ using System; namespace LanguageExt; /// /// Numeric area value /// Handles unit conversions automatically /// Internally all areas are stored as metres^2 /// All standard arithmetic operators work on the Area /// type. So keep all Areas wrapped until you need the /// value, then extract using various unit-of-measure /// accessors (SqMetres, SqCentimetres, etc.) or divide by 1.SqMetre() /// public readonly struct Area : IComparable, IEquatable, IComparable { readonly double Value; internal Area(double value) => Value = value; public override string ToString() => Value + " m²"; public bool Equals(Area other) => Value.Equals(other.Value); public bool Equals(Area other, double epsilon) => Math.Abs(other.Value - Value) < epsilon; public override bool Equals(object? obj) => obj is Area area && Equals(area); public override int GetHashCode() => Value.GetHashCode(); public int CompareTo(object? obj) => obj switch { null => 1, Area other => CompareTo(other), _ => throw new ArgumentException($"must be of type {nameof(Area)}") }; public int CompareTo(Area other) => Value.CompareTo(other.Value); public Area Add(Area rhs) => new (Value + rhs.Value); public Area Subtract(Area rhs) => new (Value - rhs.Value); public Area Multiply(double rhs) => new (Value * rhs); public Area Divide(double rhs) => new (Value / rhs); public static Area operator *(Area lhs, double rhs) => lhs.Multiply(rhs); public static Area operator *(double lhs, Area rhs) => rhs.Multiply(lhs); public static Area operator /(Area lhs, double rhs) => lhs.Divide(rhs); public static Area operator +(Area lhs, Area rhs) => lhs.Add(rhs); public static Area operator -(Area lhs, Area rhs) => lhs.Subtract(rhs); public static Length operator /(Area lhs, Length rhs) => new Length(lhs.Value / rhs.Metres); public static double operator /(Area lhs, Area rhs) => lhs.Value / rhs.Value; public static bool operator ==(Area lhs, Area rhs) => lhs.Equals(rhs); public static bool operator !=(Area lhs, Area rhs) => !lhs.Equals(rhs); public static bool operator >(Area lhs, Area rhs) => lhs.Value > rhs.Value; public static bool operator <(Area lhs, Area rhs) => lhs.Value < rhs.Value; public static bool operator >=(Area lhs, Area rhs) => lhs.Value >= rhs.Value; public static bool operator <=(Area lhs, Area rhs) => lhs.Value <= rhs.Value; public Area Pow(double power) => new Area(Math.Pow(Value,power)); public Area Round() => new Area(Math.Round(Value)); public Area Sqrt() => new Area(Math.Sqrt(Value)); public Length Abs() => new Length(Math.Abs(Value)); public Area Min(Area rhs) => new Area(Math.Min(Value, rhs.Value)); public Area Max(Area rhs) => new Area(Math.Max(Value, rhs.Value)); public double SqKilometres => Value * 0.000001; public double SqMetres => Value; public double SqCentimetres => Value * 10000.0; public double SqMillimetres => Value * 1000000.0; } public static class UnitsAreaExtensions { public static Area SqKilometres(this int self) => new (self / 0.000001); public static Area SqKilometres(this float self) => new (self / 0.000001); public static Area SqKilometres(this double self) => new (self / 0.000001); public static Area SqMetres(this int self) => new (self); public static Area SqMetres(this float self) => new (self); public static Area SqMetres(this double self) => new (self); public static Area SqCentimetres(this int self) => new (self / 10000.0); public static Area SqCentimetres(this float self) => new (self / 10000.0); public static Area SqCentimetres(this double self) => new (self / 10000.0); public static Area SqMillimetres(this int self) => new (self / 1000000.0); public static Area SqMillimetres(this float self) => new (self / 1000000.0); public static Area SqMillimetres(this double self) => new (self / 1000000.0); } ================================================ FILE: LanguageExt.Core/Units of Measure/Length.cs ================================================ using System; using LanguageExt.Traits.Domain; namespace LanguageExt; /// /// Numeric length value /// Handles unit conversions automatically /// Internally all lengths are stored as metres /// All standard arithmetic operators work on the Length /// type. So keep all Lengths wrapped until you need the /// value, then extract using various unit-of-measure /// accessors (Metres, Centimetres, etc.) or divide by 1.Metre() /// public readonly struct Length : IComparable, DomainType, Amount { readonly double Value; internal Length(double value) => Value = value; public override string ToString() => Value + " m"; public bool Equals(Length other) => Value.Equals(other.Value); public bool Equals(Length other, double epsilon) => Math.Abs(other.Value - Value) < epsilon; public override bool Equals(object? obj) => obj is Length length && Equals(length); public override int GetHashCode() => Value.GetHashCode(); public int CompareTo(object? obj) => obj switch { null => 1, Length other => CompareTo(other), _ => throw new ArgumentException($"must be of type {nameof(Length)}") }; public int CompareTo(Length other) => Value.CompareTo(other.Value); public Length Add(Length rhs) => new (Value + rhs.Value); public Length Subtract(Length rhs) => new (Value - rhs.Value); public Length Multiply(double rhs) => new (Value * rhs); public Length Divide(double rhs) => new (Value / rhs); public static Area operator *(Length lhs, Length rhs) => new (lhs.Value * rhs.Value); public static Length operator *(Length lhs, double rhs) => lhs.Multiply(rhs); public static Length operator *(double lhs, Length rhs) => rhs.Multiply(lhs); public static Length operator -(Length self) => new (-self.Value); public static Length operator +(Length lhs, Length rhs) => lhs.Add(rhs); public static Length operator -(Length lhs, Length rhs) => lhs.Subtract(rhs); public static Length operator /(Length lhs, double rhs) => lhs.Divide(rhs); public static double operator /(Length lhs, Length rhs) => lhs.Value / rhs.Value; public static Accel operator /(Length lhs, TimeSq rhs) => new Accel(lhs.Value / rhs.Seconds2); public static Time operator /(Length lhs, Velocity rhs) => new Time(lhs.Metres / rhs.MetresPerSecond); public static Velocity operator /(Length lhs, Time rhs) => new Velocity(lhs.Value / rhs.Seconds); public static bool operator ==(Length lhs, Length rhs) => lhs.Equals(rhs); public static bool operator !=(Length lhs, Length rhs) => !lhs.Equals(rhs); public static bool operator >(Length lhs, Length rhs) => lhs.Value > rhs.Value; public static bool operator <(Length lhs, Length rhs) => lhs.Value < rhs.Value; public static bool operator >=(Length lhs, Length rhs) => lhs.Value >= rhs.Value; public static bool operator <=(Length lhs, Length rhs) => lhs.Value <= rhs.Value; public Length Pow(double power) => new (Math.Pow(Value,power)); public Length Round() => new (Math.Round(Value)); public Length Sqrt() => new (Math.Sqrt(Value)); public Length Abs() => new (Math.Abs(Value)); public Length Min(Length rhs) => new (Math.Min(Value, rhs.Value)); public Length Max(Length rhs) => new (Math.Max(Value, rhs.Value)); public double Miles => Value * 6.2137119223484848484848484848485e-4; public double NauticalMiles => Value * 1852.0; public double Yards => Value * 1.0936132983333333333333333333333; public double Feet => Value * 3.280839895; public double Inches => Value * 39.37007874; public double Kilometres => Value / 1000.0; public double Hectometres => Value / 100.0; public double Decametres => Value / 10.0; public double Metres => Value; public double Centimetres => Value * 100.0; public double Millimetres => Value * 1000.0; public double Micrometres => Value * 1000000.0; public double Nanometres => Value * 1000000000.0; public double Angstroms => Value * 10000000000.0; static Fin DomainType.From(double repr) => new Length(repr); public static Length From(double repr) => new (repr); public double To() => Value; } public static class UnitsLengthExtensions { public static Length Miles(this int self) => new (1609.344000006437376000025749504 * self); public static Length Miles(this float self) => new (1609.344000006437376000025749504 * self); public static Length Miles(this double self) => new (1609.344000006437376000025749504 * self); public static Length NauticalMiles(this int self) => new (self / 1852.0); public static Length NauticalMiles(this float self) => new (self / 1852.0); public static Length NauticalMiles(this double self) => new (self / 1852.0); public static Length Yards(this int self) => new (0.9144000000036576000000146304 * self); public static Length Yards(this float self) => new (0.9144000000036576000000146304 * self); public static Length Yards(this double self) => new (0.9144000000036576000000146304 * self); public static Length Feet(this int self) => new (0.3048000000012192000000048768 * self); public static Length Feet(this float self) => new (0.3048000000012192000000048768 * self); public static Length Feet(this double self) => new (0.3048000000012192000000048768 * self); public static Length Inches(this int self) => new (0.0254000000001016000000004064 * self); public static Length Inches(this float self) => new (0.0254000000001016000000004064 * self); public static Length Inches(this double self) => new (0.0254000000001016000000004064 * self); public static Length Kilometres(this int self) => new (1000.0 * self); public static Length Kilometres(this float self) => new (1000.0 * self); public static Length Kilometres(this double self) => new (1000.0 * self); public static Length Metres(this int self) => new (self); public static Length Metres(this float self) => new (self); public static Length Metres(this double self) => new (self); public static Length Centimetres(this int self) => new (self / 100.0); public static Length Centimetres(this float self) => new (self / 100.0); public static Length Centimetres(this double self) => new (self / 100.0); public static Length Millimetres(this int self) => new (self / 1000.0); public static Length Millimetres(this float self) => new (self / 1000.0); public static Length Millimetres(this double self) => new (self / 1000.0); public static Length Micrometres(this int self) => new (self / 1000000.0); public static Length Micrometres(this float self) => new (self / 1000000.0); public static Length Micrometres(this double self) => new (self / 1000000.0); public static Length Nanometres(this int self) => new (self / 1000000000.0); public static Length Nanometres(this float self) => new (self / 1000000000.0); public static Length Nanometres(this double self) => new (self / 1000000000.0); public static Length Angstroms(this int self) => new (self / 10000000000.0); public static Length Angstroms(this float self) => new (self / 10000000000.0); public static Length Angstroms(this double self) => new (self / 10000000000.0); } ================================================ FILE: LanguageExt.Core/Units of Measure/Mass.cs ================================================ using System; namespace LanguageExt; public readonly struct Mass : IComparable, IEquatable, IComparable { readonly double Value; internal Mass(double value) => Value = value; public override string ToString() => Kilograms + " kg"; public int CompareTo(object? obj) => obj is null ? 1 : obj is Mass other ? CompareTo(other) : throw new ArgumentException($"must be of type {nameof(Mass)}"); public int CompareTo(Mass other) => Kilograms.CompareTo(other.Kilograms); public bool Equals(Mass other) => Kilograms.Equals(other.Kilograms); public bool Equals(Mass other, double epsilon) => Math.Abs(other.Kilograms - Kilograms) < epsilon; public override bool Equals(object? obj) => obj is Mass m && Equals(m); public override int GetHashCode() => Kilograms.GetHashCode(); public Mass Add(Mass rhs) => new (Kilograms + rhs.Kilograms); public Mass Subtract(Mass rhs) => new (Kilograms - rhs.Kilograms); public Mass Multiply(double rhs) => new (Kilograms * rhs); public Mass Divide(double rhs) => new (Kilograms / rhs); public static Mass operator *(Mass lhs, double rhs) => lhs.Multiply(rhs); public static Mass operator *(double lhs, Mass rhs) => rhs.Multiply(lhs); public static Mass operator +(Mass lhs, Mass rhs) => lhs.Add(rhs); public static Mass operator -(Mass lhs, Mass rhs) => lhs.Subtract(rhs); public static Mass operator /(Mass lhs, double rhs) => lhs.Divide(rhs); public static double operator /(Mass lhs, Mass rhs) => lhs.Kilograms / rhs.Kilograms; public static bool operator ==(Mass lhs, Mass rhs) => lhs.Equals(rhs); public static bool operator !=(Mass lhs, Mass rhs) => !lhs.Equals(rhs); public static bool operator >(Mass lhs, Mass rhs) => lhs.Kilograms > rhs.Kilograms; public static bool operator <(Mass lhs, Mass rhs) => lhs.Kilograms < rhs.Kilograms; public static bool operator >=(Mass lhs, Mass rhs) => lhs.Kilograms >= rhs.Kilograms; public static bool operator <=(Mass lhs, Mass rhs) => lhs.Kilograms <= rhs.Kilograms; public Mass Round() => new (Math.Round(Kilograms)); public Mass Sqrt() => new (Math.Sqrt(Kilograms)); public Mass Abs() => new (Math.Abs(Kilograms)); public Mass Min(Mass rhs) => new (Math.Min(Kilograms, rhs.Kilograms)); public Mass Max(Mass rhs) => new (Math.Max(Kilograms, rhs.Kilograms)); public double Grams => Value * 1000.0; public double Kilograms => Value; public double Tonnes => Value / 1000.0; public double Ounces => Pounds * 16.0; public double Pounds => Value * 2.2046226; public double Stones => Pounds / 14.0; public double ImperialTons => Value / 0.000984207; public double ShortTons => Value / 0.00110231; } public static class UnitsMassExtensions { public static Mass Grams(this int self) => new (self / 1000.0); public static Mass Grams(this double self) => new (self / 1000.0); public static Mass Grams(this float self) => new (self / 1000.0); public static Mass Kilograms(this int self) => new (self); public static Mass Kilograms(this double self) => new (self); public static Mass Kilograms(this float self) => new (self); public static Mass Tonnes(this int self) => new (self * 1000.0); public static Mass Tonnes(this double self) => new (self * 1000.0); public static Mass Tonnes(this float self) => new (self * 1000.0); public static Mass Ounces(this int self) => new (self / 35.273961949); public static Mass Ounces(this double self) => new (self / 35.273961949); public static Mass Ounces(this float self) => new (self / 35.273961949); public static Mass Pounds(this int self) => new (self / 2.2046226219); public static Mass Pounds(this double self) => new (self / 2.2046226219); public static Mass Pounds(this float self) => new (self / 2.2046226219); public static Mass Stones(this int self) => new (self / 0.157473044418); public static Mass Stones(this double self) => new (self / 0.157473044418); public static Mass Stones(this float self) => new (self / 0.157473044418); public static Mass ImperialTons(this int self) => new (self / 0.0009842065277); public static Mass ImperialTons(this double self) => new (self / 0.0009842065277); public static Mass ImperialTons(this float self) => new (self / 0.0009842065277); public static Mass ShortTon(this int self) => new (self / 0.00110231131093); public static Mass ShortTon(this double self) => new (self / 0.00110231131093); public static Mass ShortTon(this float self) => new (self / 0.00110231131093); } ================================================ FILE: LanguageExt.Core/Units of Measure/Module.cs ================================================ namespace LanguageExt; public class UnitsOfMeasure { /// /// Millimetre /// /// /// Length x = 10*mm; /// public static readonly Length mm = 1.Millimetres(); /// /// Millimetre /// /// /// Length x = 1*millimetre; /// public static readonly Length millimetre = 1.Millimetres(); /// /// Millimetre /// /// /// Length x = 10*millimetres; /// public static readonly Length millimetres = 1.Millimetres(); /// /// Millimeter /// /// /// Length x = 1*millimeter; /// public static readonly Length millimeter = 1.Millimetres(); /// /// Millimeters /// /// /// Length x = 10*millimeters; /// public static readonly Length millimeters = 1.Millimetres(); /// /// Centimetre /// /// /// Length x = 100*cm; /// public static readonly Length cm = 1.Centimetres(); /// /// Centimetre /// /// /// Length x = 1*centimetre; /// public static readonly Length centimetre = 1.Centimetres(); /// /// Centimetres /// /// /// Length x = 100*centimetres /// public static readonly Length centimetres = 1.Centimetres(); /// /// Centimeter /// /// /// Length x = 1*centimeter; /// public static readonly Length centimeter = 1.Centimetres(); /// /// Centimeters /// /// /// Length x = 100*centimeters; /// public static readonly Length centimeters = 1.Centimetres(); /// /// Metre /// /// /// Length x = 10*m; /// public static readonly Length m = 1.Metres(); /// /// Metre /// /// /// Length x = 1*metre; /// public static readonly Length metre = 1.Metres(); /// /// Metres /// /// /// Length x = 10*metres; /// public static readonly Length metres = 1.Metres(); /// /// Meter /// /// /// Length x = 1*meter; /// public static readonly Length meter = 1.Metres(); /// /// Meters /// /// /// Length x = 10*meters; /// public static readonly Length meters = 1.Metres(); /// /// Kilometre /// /// /// Length x = 7*km; /// public static readonly Length km = 1.Kilometres(); /// /// Kilometre /// /// /// Length x = 1*kilometre; /// public static readonly Length kilometre = 1.Kilometres(); /// /// Kilometres /// /// /// Length x = 7*kilometres; /// public static readonly Length kilometres = 1.Kilometres(); /// /// Kilometer /// /// /// Length x = 1*kilometer; /// public static readonly Length kilometer = 1.Kilometres(); /// /// Kilometers /// /// /// Length x = 7*kilometers; /// public static readonly Length kilometers = 1.Kilometres(); /// /// Inch /// /// /// Length x = 7*inch; /// public static readonly Length inch = 1.Inches(); /// /// Inch /// /// /// Length x = 7*inches; /// public static readonly Length inches = 1.Inches(); /// /// Feet /// /// /// Length x = 7*ft; /// public static readonly Length ft = 1.Feet(); /// /// Feet /// /// /// Length x = 7*foot; /// public static readonly Length foot = 1.Feet(); /// /// Feet /// /// /// Length x = 7*feet; /// public static readonly Length feet = 1.Feet(); /// /// Yard /// /// /// Length x = 7*yd; /// public static readonly Length yd = 1.Yards(); /// /// Yard /// /// /// Length x = 7*yard; /// public static readonly Length yard = 1.Yards(); /// /// Yard /// /// /// Length x = 7*yards; /// public static readonly Length yards = 1.Yards(); /// /// Mile /// /// /// Length x = 7*mile; /// public static readonly Length mile = 1.Miles(); /// /// Mile /// /// /// Length x = 7*miles; /// public static readonly Length miles = 1.Miles(); /// /// NauticalMile /// /// /// Length x = 7*nauticalMile; /// public static readonly Length nauticalMile = 1.NauticalMiles(); /// /// Millimetre squared /// /// /// Area x = 10*mm2; /// public static readonly Area mm2 = 1.SqMillimetres(); /// /// Millimetre squared /// /// /// Area x = 1*millimetre2; /// public static readonly Area millimetre2 = 1.SqMillimetres(); /// /// Millimeter squared /// /// /// Area x = 10*millimeter2; /// public static readonly Area millimeter2 = 1.SqMillimetres(); /// /// Centimetre squared /// /// /// Area x = 100*cm2; /// public static readonly Area cm2 = 1.SqCentimetres(); /// /// Centimetre squared /// /// /// Area x = 100*centimetre2; /// public static readonly Area centimetre2 = 1.SqCentimetres(); /// /// Centimeter squared /// /// /// Area x = 100*centimeter2; /// public static readonly Area centimeter2 = 1.SqCentimetres(); /// /// Metre squared /// /// /// Area x = 10*m2; /// public static readonly Area m2 = 1.SqMetres(); /// /// Metre squared /// /// /// Area x = 10*metre2; /// public static readonly Area metre2 = 1.SqMetres(); /// /// Meter squared /// /// /// Area x = 10*meter2; /// public static readonly Area meter2 = 1.SqMetres(); /// /// Kilometre squared /// /// /// Area x = 7*km2; /// public static readonly Area km2 = 1.SqKilometres(); /// /// Kilometre squared /// /// /// Area x = 7*kilometre2; /// public static readonly Area kilometre2 = 1.SqKilometres(); /// /// Kilometer squared /// /// /// Area x = 7*kilometer2; /// public static readonly Area kilometer2 = 1.SqKilometres(); /// /// Second /// /// /// Time x = 7*s; /// public static readonly Time s = 1.Seconds(); /// /// Second /// /// /// Time x = 7*sec; /// public static readonly Time sec = 1.Seconds(); /// /// Second /// /// /// Time x = 7*second; /// public static readonly Time second = 1.Seconds(); /// /// Second /// /// /// Time x = 7*seconds; /// public static readonly Time seconds = 1.Seconds(); /// /// Minute /// /// /// Time x = 7*min; /// public static readonly Time min = 1.Minutes(); /// /// Minute /// /// /// Time x = 7*mins; /// public static readonly Time mins = 1.Minutes(); /// /// Minute /// /// /// Time x = 7*minute; /// public static readonly Time minute = 1.Minutes(); /// /// Minute /// /// /// Time x = 7*minutes; /// public static readonly Time minutes = 1.Minutes(); /// /// Hour /// /// /// Time x = 7*hr; /// public static readonly Time hr = 1.Hours(); /// /// Hour /// /// /// Time x = 7*hrs; /// public static readonly Time hrs = 1.Hours(); /// /// Hour /// /// /// Time x = 7*hour; /// public static readonly Time hour = 1.Hours(); /// /// Hour /// /// /// Time x = 7*hours; /// public static readonly Time hours = 1.Hours(); /// /// Day /// /// /// Time x = 7*day; /// public static readonly Time day = 1.Days(); /// /// Day /// /// /// Time x = 7*days; /// public static readonly Time days = 1.Days(); /// /// Millisecond /// /// /// Time x = 7*ms; /// public static readonly Time ms = 1.Milliseconds(); /// /// Millisecond /// /// /// Time x = 7*millisecond; /// public static readonly Time millisecond = 1.Milliseconds(); /// /// Millisecond /// /// /// Time x = 7*milliseconds; /// public static readonly Time milliseconds = 1.Milliseconds(); /// /// Miles per hour /// public static readonly Velocity mph = miles /hour; /// /// Kilometres per hour /// public static readonly Velocity kph = km /hour; /// /// Metres per-second squared /// public static readonly Accel ms2 = m /s /s; /// /// Degrees Celsius /// public static readonly Temperature degC = new (Temperature.UnitType.C, 1); /// /// Degrees Fahrenheit /// public static readonly Temperature degF = new (Temperature.UnitType.F, 1); /// /// kelvin /// public static readonly Temperature K = new (Temperature.UnitType.K, 1); /// /// Gram /// /// /// Mass x = 10*g; /// public static readonly Mass g = 1.Grams(); /// /// Gram /// /// /// Mass x = 10*gram; /// public static readonly Mass gram = 1.Grams(); /// /// Kilogram /// /// /// Mass x = 10*kg; /// public static readonly Mass kg = 1.Kilograms(); /// /// Kilogram /// /// /// Mass x = 10*kilogram; /// public static readonly Mass kilogram = 1.Kilograms(); /// /// Tonne /// /// /// Mass x = 10*tonne; /// public static readonly Mass tonne = 1.Tonnes(); /// /// Ounce /// /// /// Mass x = 10*oz; /// public static readonly Mass oz = 1.Ounces(); /// /// Ounce /// /// /// Mass x = 10*ounce; /// public static readonly Mass ounce = 1.Ounces(); /// /// Pound /// /// /// Mass x = 10*lb; /// public static readonly Mass lb = 1.Pounds(); /// /// Pound /// /// /// Mass x = 10*pound; /// public static readonly Mass pound = 1.Pounds(); /// /// Stone /// /// /// Mass x = 10*st; /// public static readonly Mass st = 1.Stones(); /// /// Stone /// /// /// Mass x = 10*stone; /// public static readonly Mass stone = 1.Stones(); /// /// ImperialTons /// /// /// Mass x = 10*ton; /// public static readonly Mass ton = 1.ImperialTons(); /// /// ShortTon /// /// /// Mass x = 10*shortTon; /// public static readonly Mass shortTon = 1.ShortTon(); } ================================================ FILE: LanguageExt.Core/Units of Measure/Temperature.cs ================================================ using System; using System.Runtime.CompilerServices; using static LanguageExt.Prelude; namespace LanguageExt; public readonly struct Temperature : IComparable, IEquatable, IComparable { internal enum UnitType { K, C, F } readonly UnitType Type; readonly double Value; public static Temperature AbsoluteZero = default; public static Temperature ZeroCelsius = new (UnitType.C, 0.0); public static Temperature ZeroFahrenheit = new (UnitType.F, 0.0); internal Temperature(UnitType type, double value) { Type = type; Value = value; if (this < AbsoluteZero) throw new ArgumentOutOfRangeException(nameof(value), $"{value} [{type}]", "Less than absolute zero"); } public static Temperature FromCelcius(double value) => new (UnitType.C, value); public static Temperature FromFahrenheit(double value) => new (UnitType.F, value); public static Temperature FromKelvin(double value) => new (UnitType.K, value); [MethodImpl(MethodImplOptions.AggressiveInlining)] static double CtoK(double x) => x + 273.15; [MethodImpl(MethodImplOptions.AggressiveInlining)] static double KtoC(double x) => x - 273.15; [MethodImpl(MethodImplOptions.AggressiveInlining)] static double FtoK(double x) => (x + 459.67) * 5.0 / 9.0; [MethodImpl(MethodImplOptions.AggressiveInlining)] static double KtoF(double x) => (x * 1.8) - 459.67; [MethodImpl(MethodImplOptions.AggressiveInlining)] static double CtoF(double x) => (x * 1.8) + 32.0; [MethodImpl(MethodImplOptions.AggressiveInlining)] static double FtoC(double x) => (x - 32.0) * 5.0 / 9.0; public override int GetHashCode() => Value.GetHashCode(); public override bool Equals(object? obj) => obj is Temperature t && Equals(t); public bool Equals(Temperature rhs) => Value.Equals(rhs.Value); public override string ToString() => Type switch { UnitType.K => $"{Value} K", UnitType.C => $"{Value} °C", UnitType.F => $"{Value} °F", _ => throw new NotSupportedException(Type.ToString()) }; public Temperature Kelvin => Type switch { UnitType.K => this, UnitType.C => new Temperature(UnitType.K, CtoK(Value)), UnitType.F => new Temperature(UnitType.K, FtoK(Value)), _ => throw new NotSupportedException(Type.ToString()) }; public double KValue => Type switch { UnitType.K => Value, UnitType.C => CtoK(Value), UnitType.F => FtoK(Value), _ => throw new NotSupportedException(Type.ToString()) }; public Temperature Celsius => Type switch { UnitType.K => new Temperature(UnitType.C, KtoC(Value)), UnitType.C => this, UnitType.F => new Temperature(UnitType.C, FtoC(Value)), _ => throw new NotSupportedException(Type.ToString()) }; public Temperature Fahrenheit => Type switch { UnitType.K => new Temperature(UnitType.F, KtoF(Value)), UnitType.C => new Temperature(UnitType.F, CtoF(Value)), UnitType.F => this, _ => throw new NotSupportedException(Type.ToString()) }; public bool Equals(Temperature rhs, double epsilon) => Type switch { UnitType.K => rhs.Type switch { UnitType.K => Math.Abs(rhs.Value - Value) < epsilon, UnitType.C => Math.Abs(CtoK(rhs.Value) - Value) < epsilon, UnitType.F => Math.Abs(FtoK(rhs.Value) - Value) < epsilon, _ => throw new NotSupportedException(Type.ToString()) }, UnitType.C => rhs.Type switch { UnitType.K => Math.Abs(KtoC(rhs.Value) - Value) < epsilon, UnitType.C => Math.Abs(rhs.Value - Value) < epsilon, UnitType.F => Math.Abs(FtoC(rhs.Value) - Value) < epsilon, _ => throw new NotSupportedException(Type.ToString()) }, UnitType.F => rhs.Type switch { UnitType.K => Math.Abs(KtoF(rhs.Value) - Value) < epsilon, UnitType.C => Math.Abs(CtoF(rhs.Value) - Value) < epsilon, UnitType.F => Math.Abs(rhs.Value - Value) < epsilon, _ => throw new NotSupportedException(Type.ToString()) }, _ => throw new NotSupportedException(Type.ToString()) }; public int CompareTo(object? obj) => obj switch { null => 1, Temperature other => CompareTo(other), _ => throw new ArgumentException($"must be of type {nameof(Temperature)}") }; public int CompareTo(Temperature rhs) => Type switch { UnitType.K => rhs.Type switch { UnitType.K => Value.CompareTo(rhs.Value), UnitType.C => Value.CompareTo(CtoK(rhs.Value)), UnitType.F => Value.CompareTo(FtoK(rhs.Value)), _ => throw new NotSupportedException(Type.ToString()) }, UnitType.C => rhs.Type switch { UnitType.K => Value.CompareTo(KtoC(rhs.Value)), UnitType.C => Value.CompareTo(rhs.Value), UnitType.F => Value.CompareTo(FtoC(rhs.Value)), _ => throw new NotSupportedException(Type.ToString()) }, UnitType.F => rhs.Type switch { UnitType.K => Value.CompareTo(KtoF(rhs.Value)), UnitType.C => Value.CompareTo(CtoF(rhs.Value)), UnitType.F => Value.CompareTo(rhs.Value), _ => throw new NotSupportedException(Type.ToString()) }, _ => throw new NotSupportedException(Type.ToString()) }; public Temperature Add(Temperature rhs) => Type switch { UnitType.K => rhs.Type switch { UnitType.K => new Temperature(UnitType.K, Value + rhs.Value), UnitType.C => new Temperature(UnitType.K, Value + CtoK(rhs.Value)), UnitType.F => new Temperature(UnitType.K, Value + FtoK(rhs.Value)), _ => throw new NotSupportedException(Type.ToString()) }, UnitType.C => rhs.Type switch { UnitType.K => new Temperature(UnitType.C, Value + KtoC(rhs.Value)), UnitType.C => new Temperature(UnitType.C, Value + rhs.Value), UnitType.F => new Temperature(UnitType.C, Value + FtoC(rhs.Value)), _ => throw new NotSupportedException(Type.ToString()) }, UnitType.F => rhs.Type switch { UnitType.K => new Temperature(UnitType.F, Value + KtoF(rhs.Value)), UnitType.C => new Temperature(UnitType.F, Value + CtoF(rhs.Value)), UnitType.F => new Temperature(UnitType.F, Value + rhs.Value), _ => throw new NotSupportedException(Type.ToString()) }, _ => throw new NotSupportedException(Type.ToString()) }; public Temperature Add(double rhs) => new (Type, Value + rhs); public Temperature Subtract(Temperature rhs) => Type switch { UnitType.K => rhs.Type switch { UnitType.K => new Temperature(UnitType.K, Value - rhs.Value), UnitType.C => new Temperature(UnitType.K, Value - CtoK(rhs.Value)), UnitType.F => new Temperature(UnitType.K, Value - FtoK(rhs.Value)), _ => throw new NotSupportedException(Type.ToString()) }, UnitType.C => rhs.Type switch { UnitType.K => new Temperature(UnitType.C, Value - KtoC(rhs.Value)), UnitType.C => new Temperature(UnitType.C, Value - rhs.Value), UnitType.F => new Temperature(UnitType.C, Value - FtoC(rhs.Value)), _ => throw new NotSupportedException(Type.ToString()) }, UnitType.F => rhs.Type switch { UnitType.K => new Temperature(UnitType.F, Value - KtoF(rhs.Value)), UnitType.C => new Temperature(UnitType.F, Value - CtoF(rhs.Value)), UnitType.F => new Temperature(UnitType.F, Value - rhs.Value), _ => throw new NotSupportedException(Type.ToString()) }, _ => throw new NotSupportedException(Type.ToString()) }; public Temperature Subtract(double rhs) => new (Type, Value + rhs); public Temperature Multiply(double rhs) => new (Type, Value * rhs); public Temperature Divide(double rhs) => new (Type, Value / rhs); public static Temperature operator *(Temperature lhs, double rhs) => lhs.Multiply(rhs); public static Temperature operator *(double lhs, Temperature rhs) => rhs.Multiply(lhs); public static Temperature operator +(Temperature lhs, Temperature rhs) => lhs.Add(rhs); public static Temperature operator +(Temperature lhs, double rhs) => lhs.Add(rhs); public static Temperature operator -(Temperature lhs, Temperature rhs) => lhs.Subtract(rhs); public static Temperature operator -(Temperature lhs, double rhs) => lhs.Subtract(rhs); public static Temperature operator /(Temperature lhs, double rhs) => lhs.Divide(rhs); public static bool operator ==(Temperature lhs, Temperature rhs) => lhs.Equals(rhs); public static bool operator !=(Temperature lhs, Temperature rhs) => !lhs.Equals(rhs); public static bool operator >(Temperature lhs, Temperature rhs) => lhs.CompareTo(rhs) > 0; public static bool operator <(Temperature lhs, Temperature rhs) => lhs.CompareTo(rhs) < 0; public static bool operator >=(Temperature lhs, Temperature rhs) => lhs.CompareTo(rhs) >= 0; public static bool operator <=(Temperature lhs, Temperature rhs) => lhs.CompareTo(rhs) <= 0; public Temperature Round() => new (Type, Math.Round(Value)); public Temperature Abs() => new (Type, Math.Abs(Value)); public Temperature Min(Temperature rhs) => Type switch { UnitType.K => rhs.Type switch { UnitType.K => new Temperature(UnitType.K, Math.Min(Value, rhs.Value)), UnitType.C => new Temperature(UnitType.K, Math.Min(Value, CtoK(rhs.Value))), UnitType.F => new Temperature(UnitType.K, Math.Min(Value, FtoK(rhs.Value))), _ => throw new NotSupportedException(Type.ToString()) }, UnitType.C => rhs.Type switch { UnitType.K => new Temperature(UnitType.C, Math.Min(Value, KtoC(rhs.Value))), UnitType.C => new Temperature(UnitType.C, Math.Min(Value, rhs.Value)), UnitType.F => new Temperature(UnitType.C, Math.Min(Value, FtoC(rhs.Value))), _ => throw new NotSupportedException(Type.ToString()) }, UnitType.F => rhs.Type switch { UnitType.K => new Temperature(UnitType.F, Math.Min(Value, KtoF(rhs.Value))), UnitType.C => new Temperature(UnitType.F, Math.Min(Value, CtoF(rhs.Value))), UnitType.F => new Temperature(UnitType.F, Math.Min(Value, rhs.Value)), _ => throw new NotSupportedException(Type.ToString()) }, _ => throw new NotSupportedException(Type.ToString()) }; public Temperature Max(Temperature rhs) => Type switch { UnitType.K => rhs.Type switch { UnitType.K => new Temperature(UnitType.K, Math.Max(Value, rhs.Value)), UnitType.C => new Temperature(UnitType.K, Math.Max(Value, CtoK(rhs.Value))), UnitType.F => new Temperature(UnitType.K, Math.Max(Value, FtoK(rhs.Value))), _ => throw new NotSupportedException(Type.ToString()) }, UnitType.C => rhs.Type switch { UnitType.K => new Temperature(UnitType.C, Math.Max(Value, KtoC(rhs.Value))), UnitType.C => new Temperature(UnitType.C, Math.Max(Value, rhs.Value)), UnitType.F => new Temperature(UnitType.C, Math.Max(Value, FtoC(rhs.Value))), _ => throw new NotSupportedException(Type.ToString()) }, UnitType.F => rhs.Type switch { UnitType.K => new Temperature(UnitType.F, Math.Max(Value, KtoF(rhs.Value))), UnitType.C => new Temperature(UnitType.F, Math.Max(Value, CtoF(rhs.Value))), UnitType.F => new Temperature(UnitType.F, Math.Max(Value, rhs.Value)), _ => throw new NotSupportedException(Type.ToString()) }, _ => throw new NotSupportedException(Type.ToString()) }; } public static class UnitsTemperatureExtensions { public static Temperature Celsius(this int self) => new (Temperature.UnitType.C, self); public static Temperature Celsius(this float self) => new (Temperature.UnitType.C, self); public static Temperature Celsius(this double self) => new (Temperature.UnitType.C, self); public static Temperature Fahrenheit(this int self) => new (Temperature.UnitType.F, self); public static Temperature Fahrenheit(this float self) => new (Temperature.UnitType.F, self); public static Temperature Fahrenheit(this double self) => new (Temperature.UnitType.F, self); public static Temperature Kelvin(this int self) => new (Temperature.UnitType.K, self); public static Temperature Kelvin(this float self) => new (Temperature.UnitType.K, self); public static Temperature Kelvin(this double self) => new (Temperature.UnitType.K, self); } ================================================ FILE: LanguageExt.Core/Units of Measure/Time.cs ================================================ using System; using static LanguageExt.Prelude; namespace LanguageExt; /// /// Numeric time-span value /// Handles unit conversions automatically /// All standard arithmetic operators work on the Time /// type. So keep all Times wrapped until you need the /// value, then extract using various unit-of-measure /// accessors (Milliseconds, Seconds, etc.) or divide by 1.Second() /// Implicitly convertible to TimeSpan /// public readonly struct Time : IComparable { public readonly A Value; public Box(A value) => Value = value; public static readonly Func New; public static readonly Func GetValue; static Box() { New = typeof(A).IsValueType ? MakeNewStruct() : MakeNewClass(); GetValue = typeof(A).IsValueType ? GetValueStruct() : GetValueClass(); } static Func GetValueClass() { if (ILCapability.Available) { var dynamic = new DynamicMethod("GetValue_Class", typeof(A), [typeof(object)], typeof(A).Module, true); var il = dynamic.GetILGenerator(); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ret); return (Func)dynamic.CreateDelegate(typeof(Func)); } else { return x => (A)x; } } static Func GetValueStruct() { if (ILCapability.Available) { var field = typeof(Box).GetField("Value"); var dynamic = new DynamicMethod("GetValue_Struct", typeof(A), new[] { typeof(object) }, typeof(A).Module, true); var il = dynamic.GetILGenerator(); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Castclass, typeof(Box)); il.Emit(OpCodes.Ldfld, field!); il.Emit(OpCodes.Ret); return (Func)dynamic.CreateDelegate(typeof(Func)); } else { return (object x) => ((Box)x).Value; } } static Func MakeNewClass() { if (ILCapability.Available) { var dynamic = new DynamicMethod("New_Class", typeof(object), new[] {typeof(A)}, typeof(A).Module, true); var il = dynamic.GetILGenerator(); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ret); return (Func)dynamic.CreateDelegate(typeof(Func)); } else { return static (A x) => (object?)x ?? throw new NullReferenceException(); } } static Func MakeNewStruct() { if (ILCapability.Available) { var ctor = typeof(Box).GetConstructor(new[] {typeof(A)}); var dynamic = new DynamicMethod("New_Struct", typeof(object), new[] {typeof(A)}, typeof(A).Module, true); var il = dynamic.GetILGenerator(); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Newobj, ctor!); il.Emit(OpCodes.Ret); return (Func)dynamic.CreateDelegate(typeof(Func)); } else { return static (A x) => new Box(x); } } } } ================================================ FILE: LanguageExt.Core/Utility/Check.cs ================================================ namespace LanguageExt { internal static class Check { internal static T NullReturn(T value) => Prelude.isnull(value) ? Prelude.raise(new ResultIsNullException()) : value; } } ================================================ FILE: LanguageExt.Core/Utility/CollectionFormat.cs ================================================ using System; using System.Linq; using System.Collections.Generic; namespace LanguageExt { public static class CollectionFormat { /// /// Application wide setting for the maximum number of items /// shown in a call to the `ToString` method of any LanguageExt /// collection type. /// public static int MaxShortItems = 50; internal static string ToShortString(IEnumerable ma, string separator = ", ") { var items = ma.Take(MaxShortItems).ToList(); return items.Count < MaxShortItems ? $"{String.Join(separator, items)}" : $"{String.Join(separator, items)} ..."; } internal static string ToShortString(IEnumerable ma, int count, string separator = ", ") => count <= MaxShortItems ? $"{String.Join(separator, ma)}" : $"{String.Join(separator, ma.Take(MaxShortItems))} ... {count - MaxShortItems} more"; internal static string ToShortArrayString(IEnumerable ma, string separator = ", ") => $"[{ToShortString(ma, separator)}]"; internal static string ToShortArrayString(IEnumerable ma, int count, string separator = ", ") => $"[{ToShortString(ma, count, separator)}]"; internal static string ToFullString(IEnumerable ma, string separator = ", ") => $"{String.Join(separator, ma)}"; internal static string ToFullArrayString(IEnumerable ma, string separator = ", ") => $"[{ToFullString(ma, separator)}]"; } } ================================================ FILE: LanguageExt.Core/Utility/Disposable.cs ================================================ using System; namespace LanguageExt { internal static class Disposable { public static bool IsDisposable = typeof(IDisposable).IsAssignableFrom(typeof(A)); } } ================================================ FILE: LanguageExt.Core/Utility/EnumerableOptimal.cs ================================================ using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; using static LanguageExt.Prelude; namespace LanguageExt; public static class EnumerableOptimal { [MethodImpl(MethodImplOptions.AggressiveInlining)] public static IEnumerable ConcatFast(this IEnumerable ma, IEnumerable mb) => ma is ConcatEnum ca ? ca.Concat(mb) : new ConcatEnum(Seq(ma, mb)); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Iterable ConcatFast(this Iterable ma, IEnumerable mb) => ma.Concat(Iterable.createRange(mb)); [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static IEnumerable BindFast(this IEnumerable? ma, Func> f) => ma is null ? System.Linq.Enumerable.Empty() : new BindEnum(ma, f); [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static IEnumerable BindFast(this IEnumerable ma, Func> f) => ma == null ? System.Linq.Enumerable.Empty() : new BindEnum(ma, a => f(a).AsIterable()); internal class ConcatEnum(Seq> ms) : IEnumerable { internal readonly Seq> ms = ms; public ConcatIter GetEnumerator() => new(ms); public ConcatEnum Concat(IEnumerable cb) => new(ms.Add(cb)); IEnumerator IEnumerable.GetEnumerator() => new ConcatIter(ms); IEnumerator IEnumerable.GetEnumerator() => new ConcatIter(ms); } internal class BindEnum : IEnumerable { readonly IEnumerable ma; readonly Func> f; public BindEnum(IEnumerable ma, Func> f) { this.ma = ma; this.f = f; } public BindIter GetEnumerator() => new BindIter(ma, f); IEnumerator IEnumerable.GetEnumerator() => new BindIter(ma, f); IEnumerator IEnumerable.GetEnumerator() => new BindIter(ma, f); } internal struct ConcatIter : IEnumerator { Seq> ms; IEnumerator iter; int index; A current; public ConcatIter(Seq> ms) { this.ms = ms; this.index = 0; this.iter = ms[0].GetEnumerator(); current = default!; } public readonly A Current => current; readonly object IEnumerator.Current => current!; public void Dispose() => iter?.Dispose(); public bool MoveNext() { if (iter.MoveNext()) { current = iter.Current; return true; } else { current = default!; index++; while(index < ms.Count) { iter.Dispose(); iter = ms[index].GetEnumerator(); if (iter.MoveNext()) { current = iter.Current; return true; } else { index++; continue; } } iter.Dispose(); return false; } } public void Reset() { Dispose(); index = 0; iter = ms[0].GetEnumerator(); } } internal struct BindIter : IEnumerator { readonly Func> f; readonly IEnumerable ema; IEnumerator? ma; IEnumerator? mb; B current; public BindIter(IEnumerable ma, Func> f) { this.ema = ma; this.ma = ema.GetEnumerator(); this.mb = default!; this.f = f; current = default!; } public B Current => current; object IEnumerator.Current => current!; public void Dispose() { ma?.Dispose(); mb?.Dispose(); } public bool MoveNext() { if (ma == null) return false; if (mb == null) { while (ma.MoveNext()) { mb = f(ma.Current).GetEnumerator(); if (mb.MoveNext()) { current = mb.Current; return true; } else { continue; } } ma.Dispose(); ma = null; return false; } else { if (mb.MoveNext()) { current = mb.Current; return true; } else { mb.Dispose(); mb = null; while (ma.MoveNext()) { mb = f(ma.Current).GetEnumerator(); if (mb.MoveNext()) { current = mb.Current; return true; } else { continue; } } ma.Dispose(); ma = null; return false; } } } public void Reset() { Dispose(); ma = ema.GetEnumerator(); } } } ================================================ FILE: LanguageExt.Core/Utility/Fnv.cs ================================================ using System; using System.Collections.Generic; using System.Runtime.CompilerServices; namespace LanguageExt { /// /// Fowler–Noll–Vo 32bit hash function /// /// /// [Fowler–Noll–Vo hash function](https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function) /// internal static class FNV32 { /// /// Offset basis for a FNV-1 or FNV-1a 32 bit hash /// public const int OffsetBasis = -2128831035; /// /// Prime for FNV-1 or FNV-1a 32 bit hash /// public const int Prime = 16777619; [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int Next(int hashA, int hashB) { unchecked { return (hashA ^ hashB) * Prime; } } /// /// Calculate the hash code for an array /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int Hash(A[]? items, int offsetBasis = OffsetBasis) where HashA : Hashable { int hash = offsetBasis; if (items == null) return hash; unchecked { Span span = items; foreach (var item in span) { hash = Next(HashA.GetHashCode(item), hash); } return hash; } } /// /// Calculate the hash code for an array slice /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int Hash(A[]? items, int start, int length, int offsetBasis = OffsetBasis) where HashA : Hashable { int hash = offsetBasis; if (items == null) return hash; unchecked { var span = new Span(items, start, length); foreach (var item in span) { hash = Next(HashA.GetHashCode(item), hash); } return hash; } } /// /// Calculate the hash code for an enumerable /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int Hash(IEnumerable? items, int offsetBasis = OffsetBasis) where HashA : Hashable { int hash = offsetBasis; if (items == null) return hash; unchecked { foreach (var item in items) { hash = Next(HashA.GetHashCode(item), hash); } return hash; } } } } ================================================ FILE: LanguageExt.Core/Utility/IL.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using System.Reflection; using System.Reflection.Emit; using System.Runtime.Serialization; using System.Text; using LanguageExt.Traits.Resolve; using static LanguageExt.Prelude; using static LanguageExt.Reflect; namespace LanguageExt; public static class IL { /// /// Emits the IL to instantiate a type of R with a single argument to /// the constructor /// public static Func Ctor() { var ctorInfo = GetConstructor() .IfNone(() => throw new ArgumentException($"Constructor not found for type {typeof(R).FullName}")); var dynamic = new DynamicMethod("CreateInstance", ctorInfo.DeclaringType, Type.EmptyTypes, typeof(R).Module, true); var il = dynamic.GetILGenerator(); il.Emit(OpCodes.Newobj, ctorInfo); il.Emit(OpCodes.Ret); return (Func)dynamic.CreateDelegate(typeof(Func)); } /// /// Emits the IL to instantiate a type of R with a single argument to /// the constructor /// public static Func Ctor() { var ctorInfo = GetConstructor() .IfNone(() => throw new ArgumentException($"Constructor not found for type {typeof(R).FullName}")); var ctorParams = ctorInfo.GetParameters(); if (ILCapability.Available) { var dynamic = new DynamicMethod("CreateInstance", ctorInfo.DeclaringType, ctorParams.Select(p => p.ParameterType).ToArray(), typeof(R).Module, true); var il = dynamic.GetILGenerator(); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Newobj, ctorInfo); il.Emit(OpCodes.Ret); return (Func)dynamic.CreateDelegate(typeof(Func)); } else { var arg0 = Expression.Parameter(typeof(A), "arg0"); var expr = Expression.New(ctorInfo, arg0); var lambda = Expression.Lambda>(expr, arg0); return lambda.Compile(); } } /// /// Emits the IL to instantiate a type of R with two arguments to /// the constructor /// public static Func Ctor() { var ctorInfo = GetConstructor() .IfNone(() => throw new ArgumentException($"Constructor not found for type {typeof(R).FullName}")); var ctorParams = ctorInfo.GetParameters(); if (ILCapability.Available) { var dynamic = new DynamicMethod("CreateInstance", ctorInfo.DeclaringType, ctorParams.Select(p => p.ParameterType).ToArray(), typeof(R).Module, true); var il = dynamic.GetILGenerator(); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldarg_1); il.Emit(OpCodes.Newobj, ctorInfo); il.Emit(OpCodes.Ret); return (Func)dynamic.CreateDelegate(typeof(Func)); } else { var arg0 = Expression.Parameter(typeof(A), "arg0"); var arg1 = Expression.Parameter(typeof(B), "arg1"); var expr = Expression.New(ctorInfo, arg0, arg1); var lambda = Expression.Lambda>(expr, arg0, arg1); return lambda.Compile(); } } /// /// Emits the IL to instantiate a type of R with three arguments to /// the constructor /// public static Func Ctor() { var ctorInfo = GetConstructor() .IfNone(() => throw new ArgumentException($"Constructor not found for type {typeof(R).FullName}")); var ctorParams = ctorInfo.GetParameters(); if (ILCapability.Available) { var dynamic = new DynamicMethod("CreateInstance", ctorInfo.DeclaringType, ctorParams.Select(p => p.ParameterType).ToArray(), typeof(R).Module, true); var il = dynamic.GetILGenerator(); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldarg_1); il.Emit(OpCodes.Ldarg_2); il.Emit(OpCodes.Newobj, ctorInfo); il.Emit(OpCodes.Ret); return (Func)dynamic.CreateDelegate(typeof(Func)); } else { var arg0 = Expression.Parameter(typeof(A), "arg0"); var arg1 = Expression.Parameter(typeof(B), "arg1"); var arg2 = Expression.Parameter(typeof(C), "arg2"); var expr = Expression.New(ctorInfo, arg0, arg1, arg2); var lambda = Expression.Lambda>(expr, arg0, arg1, arg2); return lambda.Compile(); } } /// /// Emits the IL to instantiate a type of R with four arguments to /// the constructor /// public static Func Ctor() { var ctorInfo = GetConstructor() .IfNone(() => throw new ArgumentException($"Constructor not found for type {typeof(R).FullName}")); if (ctorInfo == null) throw new ArgumentException($"Constructor not found for type {typeof(R).FullName}"); if (ILCapability.Available) { var ctorParams = ctorInfo.GetParameters(); var dynamic = new DynamicMethod("CreateInstance", ctorInfo.DeclaringType, ctorParams.Select(p => p.ParameterType).ToArray(), typeof(R).Module, true); var il = dynamic.GetILGenerator(); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldarg_1); il.Emit(OpCodes.Ldarg_2); il.Emit(OpCodes.Ldarg_3); il.Emit(OpCodes.Newobj, ctorInfo); il.Emit(OpCodes.Ret); return (Func)dynamic.CreateDelegate(typeof(Func)); } else { var arg0 = Expression.Parameter(typeof(A), "arg0"); var arg1 = Expression.Parameter(typeof(B), "arg1"); var arg2 = Expression.Parameter(typeof(C), "arg2"); var arg3 = Expression.Parameter(typeof(D), "arg3"); var expr = Expression.New(ctorInfo, arg0, arg1, arg2, arg3); var lambda = Expression.Lambda>(expr, arg0, arg1, arg2, arg3); return lambda.Compile(); } } /// /// Emits the IL to invoke a static method /// public static Option> Func1(Type arg1, Func? methodPred = null) { methodPred ??= _ => true; var methodInfo = typeof(TYPE) .GetTypeInfo() .GetAllMethods(true) .Where(x => { if (!x.IsStatic) return false; var ps = x.GetParameters(); if (ps.Length != 1) return false; if (ps[0].ParameterType != arg1) return false; return methodPred(x); }) .FirstOrDefault(); if (methodInfo == null) return None; var methodParams = methodInfo.GetParameters(); if (ILCapability.Available) { var dynamic = new DynamicMethod("CreateInstance", typeof(R), methodParams.Select(_ => typeof(object)).ToArray(), typeof(R).Module, true); var il = dynamic.GetILGenerator(); il.DeclareLocal(typeof(R)); il.Emit(OpCodes.Ldarg_0); if (arg1.GetTypeInfo().IsValueType) { il.Emit(OpCodes.Unbox_Any, arg1); } else { il.Emit(OpCodes.Castclass, arg1); } il.Emit(OpCodes.Call, methodInfo); il.Emit(OpCodes.Ret); return (Func)dynamic.CreateDelegate(typeof(Func)); } else { var larg1 = Expression.Parameter(typeof(object), "arg1"); var expr = Expression.Call(methodInfo, Expression.Convert(larg1, arg1)); var lambda = Expression.Lambda>(expr, larg1); return lambda.Compile(); } } /// /// Emits the IL to invoke a static method with one argument /// public static Option> Func1(Func? methodPred = null) { methodPred ??= _ => true; var methodInfo = typeof(TYPE) .GetTypeInfo() .GetAllMethods(true) .Where(x => { if (!x.IsStatic) return false; var ps = x.GetParameters(); if (ps.Length != 1) return false; if (ps[0].ParameterType != typeof(A)) return false; return methodPred(x); }) .FirstOrDefault(); if (methodInfo == null) return None; if (ILCapability.Available) { var methodParams = methodInfo.GetParameters(); var dynamic = new DynamicMethod("CreateInstance", typeof(R), methodParams.Select(p => p.ParameterType).ToArray(), typeof(R).Module, true); var il = dynamic.GetILGenerator(); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Call, methodInfo); il.Emit(OpCodes.Ret); return (Func)dynamic.CreateDelegate(typeof(Func)); } else { var larg0 = Expression.Parameter(typeof(A), "arg0"); var expr = Expression.Call(methodInfo, larg0); var lambda = Expression.Lambda>(expr, larg0); return lambda.Compile(); } } /// /// Emits the IL to invoke a static method with two arguments /// public static Option> Func2(Func? methodPred = null) { methodPred ??= _ => true; var methodInfo = typeof(TYPE) .GetTypeInfo() .GetAllMethods(true) .Where(x => { if (!x.IsStatic) return false; var ps = x.GetParameters(); if (ps.Length != 2) return false; if (ps[0].ParameterType != typeof(A)) return false; if (ps[1].ParameterType != typeof(B)) return false; return methodPred(x); }) .FirstOrDefault(); if (methodInfo == null) return None; if (ILCapability.Available) { var methodParams = methodInfo.GetParameters(); var dynamic = new DynamicMethod("CreateInstance", typeof(R), methodParams.Select(p => p.ParameterType).ToArray(), typeof(R).Module, true); var il = dynamic.GetILGenerator(); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldarg_1); il.Emit(OpCodes.Call, methodInfo); il.Emit(OpCodes.Ret); return (Func)dynamic.CreateDelegate(typeof(Func)); } else { var larg0 = Expression.Parameter(typeof(A), "arg0"); var larg1 = Expression.Parameter(typeof(B), "arg1"); var expr = Expression.Call(methodInfo, larg0, larg1); var lambda = Expression.Lambda>(expr, larg0, larg1); return lambda.Compile(); } } /// /// Emits the IL to invoke a static method with three arguments /// public static Option> Func3(Func? methodPred = null) { methodPred ??= _ => true; var methodInfo = typeof(TYPE) .GetTypeInfo() .GetAllMethods(true) .Where(x => { if (!x.IsStatic) return false; var ps = x.GetParameters(); if (ps.Length != 3) return false; if (ps[0].ParameterType != typeof(A)) return false; if (ps[1].ParameterType != typeof(B)) return false; if (ps[2].ParameterType != typeof(C)) return false; return methodPred(x); }) .FirstOrDefault(); if (methodInfo == null) return None; if(ILCapability.Available) { var methodParams = methodInfo.GetParameters(); var dynamic = new DynamicMethod("CreateInstance", typeof(R), methodParams.Select(p => p.ParameterType).ToArray(), typeof(R).Module, true); var il = dynamic.GetILGenerator(); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldarg_1); il.Emit(OpCodes.Ldarg_2); il.Emit(OpCodes.Call, methodInfo); il.Emit(OpCodes.Ret); return (Func)dynamic.CreateDelegate(typeof(Func)); } else { var larg0 = Expression.Parameter(typeof(A), "arg0"); var larg1 = Expression.Parameter(typeof(B), "arg1"); var larg2 = Expression.Parameter(typeof(C), "arg2"); var expr = Expression.Call(methodInfo, larg0, larg1, larg2); var lambda = Expression.Lambda>(expr, larg0, larg1, larg2); return lambda.Compile(); } } /// /// Emits the IL to invoke a static method with four arguments /// public static Option> Func4(Func? methodPred = null) { methodPred ??= _ => true; var methodInfo = typeof(TYPE) .GetTypeInfo() .GetAllMethods(true) .Where(x => { if (!x.IsStatic) return false; var ps = x.GetParameters(); if (ps.Length != 4) return false; if (ps[0].ParameterType != typeof(A)) return false; if (ps[1].ParameterType != typeof(B)) return false; if (ps[2].ParameterType != typeof(C)) return false; if (ps[3].ParameterType != typeof(D)) return false; return methodPred(x); }) .FirstOrDefault(); if (methodInfo == null) return None; if (ILCapability.Available) { var methodParams = methodInfo.GetParameters(); var dynamic = new DynamicMethod("CreateInstance", typeof(R), methodParams.Select(p => p.ParameterType).ToArray(), typeof(R).Module, true); var il = dynamic.GetILGenerator(); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldarg_1); il.Emit(OpCodes.Ldarg_2); il.Emit(OpCodes.Ldarg_3); il.Emit(OpCodes.Call, methodInfo); il.Emit(OpCodes.Ret); return (Func)dynamic.CreateDelegate(typeof(Func)); } else { var larg0 = Expression.Parameter(typeof(A), "arg0"); var larg1 = Expression.Parameter(typeof(B), "arg1"); var larg2 = Expression.Parameter(typeof(C), "arg2"); var larg3 = Expression.Parameter(typeof(D), "arg3"); var expr = Expression.Call(methodInfo, larg0, larg1, larg2, larg3); var lambda = Expression.Lambda>(expr, larg0, larg1, larg2, larg3); return lambda.Compile(); } } /// /// Builds a function to provide a hash-code for a record type. the hash-code is built from /// the hash-codes of all the *fields* that make up the type. /// /// You should cache the result of this method to reduce the work of building the IL /// each time. Better still use the `RecordType〈A〉` type to provide a cached version of these /// results. /// public static Func GetHashCode(bool includeBase) { var hashF = (HashableResolve.Exists, EqResolve.Exists, OrdResolve.Exists) switch { (true, _, _) => HashableResolve.GetHashCodeFunc, (_, true, _) => EqResolve.GetHashCodeFunc, (_, _, true) => OrdResolve.GetHashCodeFunc, _ => null }; if (hashF is not null) return hashF; var fields = GetPublicInstanceFields( includeBase, typeof(NonHashAttribute), typeof(NonStructuralAttribute), typeof(NonRecordAttribute)); var self = Expression.Parameter(typeof(A)); // Use 32-bit FNV hash parameters as signed values since .net GetHashCode returns a signed 32-bit integer. var fnvOffsetBasis = Expression.Constant(-2128831035); var fnvPrime = Expression.Constant(16777619); var Null = Expression.Constant(null, typeof(A)); var refEq = Expression.ReferenceEqual(self, Null); IEnumerable Fields() { foreach (var field in fields) { var hashMethod = Resolver.GetHashCodeMethodAlways(field.FieldType); yield return Expression.Call( null, hashMethod, Expression.PropertyOrField(self, field.Name) ); } } // Implement FNV 1a hashing algorithm - [Fowler–Noll–Vo hash function](https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function#FNV-1a_hash) var expr = Fields() .AsIterable() .Fold(fnvOffsetBasis as Expression, (state, field) => Expression.Multiply( fnvPrime, Expression.ExclusiveOr( state, field))); var lambda = Expression.Lambda>( typeof(A).GetTypeInfo().IsValueType ? expr : Expression.Condition(refEq, Expression.Constant(0), expr) , self); return lambda.Compile(); } /// /// Provides a function that compares two record type arguments (one of type A and one of /// object) for structural equality, this first makes sure that the `Object` argument is of /// type A and then compares the *fields* from each argument for equality and returns true if /// all are equal. /// /// You should cache the result of this method to reduce the work of building the IL /// each time. Better still use the `RecordType〈A〉` type to provide a cached version of these /// results. /// public static Func Equals(bool includeBase) { var eqF = (EqResolve.Exists, OrdResolve.Exists) switch { (true, _) => EqResolve.EqualsFunc, (_, true) => OrdResolve.EqualsFunc, _ => null }; if (eqF is not null) { return (lhs, rhs) => rhs is A r && eqF(lhs, r); } var fields = GetPublicInstanceFields( includeBase, typeof(NonEqAttribute), typeof(NonStructuralAttribute), typeof(NonRecordAttribute)); var self = Expression.Parameter(typeof(A), "self"); var other = Expression.Parameter(typeof(object), "other"); var otherCast = Expression.Convert(other, typeof(A)); var True = Expression.Constant(true); var NullA = Expression.Constant(null, typeof(A)); var NullObj = Expression.Constant(null, typeof(object)); var refEq = Expression.ReferenceEqual(self, other); var notNullX = Expression.ReferenceNotEqual(self, NullA); var notNullY = Expression.ReferenceNotEqual(other, NullObj); var typeA = Expression.TypeEqual(self, typeof(A)); var typeB = Expression.TypeEqual(other, typeof(A)); var typesEqual = Expression.Equal(typeA, typeB); var expr = Expression.AndAlso( typesEqual, fields .AsIterable() .Fold(True as Expression, (state, field) => Expression.AndAlso( state, Expression.Call( null, Resolver.GetEqualsMethodAlways(field.FieldType), Expression.PropertyOrField(self, field.Name), Expression.PropertyOrField(otherCast, field.Name))))); var orExpr = Expression.OrElse(refEq, Expression.AndAlso(notNullX, Expression.AndAlso(notNullY, expr))); var lambda = Expression.Lambda>( typeof(A).GetTypeInfo().IsValueType ? expr : orExpr, self, other); return lambda.Compile(); } /// /// Provides a function that compares two record type arguments for structural equality, this /// first compares the *fields* from each argument for equality and returns true if all are /// equal. /// /// You should cache the result of this method to reduce the work of building the IL /// each time. Better still use the `RecordType〈A〉` type to provide a cached version of these /// results. /// public static Func EqualsTyped(bool includeBase) { var eqF = (EqResolve.Exists, OrdResolve.Exists) switch { (true, _) => EqResolve.EqualsFunc, (_, true) => OrdResolve.EqualsFunc, _ => null }; if (eqF is not null) { return eqF; } var fields = GetPublicInstanceFields( includeBase, typeof(NonEqAttribute), typeof(NonStructuralAttribute), typeof(NonRecordAttribute) ); var self = Expression.Parameter(typeof(A), "self"); var other = Expression.Parameter(typeof(A), "other"); var True = Expression.Constant(true); var Null = Expression.Constant(null, typeof(A)); var refEq = Expression.ReferenceEqual(self, other); var notNullX = Expression.ReferenceNotEqual(self, Null); var notNullY = Expression.ReferenceNotEqual(other, Null); var typeA = Expression.TypeEqual(self, typeof(A)); var typeB = Expression.TypeEqual(other, typeof(A)); var typesEqual = Expression.Equal(typeA, typeB); var expr = Expression.AndAlso( typesEqual, fields .AsIterable() .Fold(True as Expression, (state, field) => Expression.AndAlso( state, Expression.Call( null, Resolver.GetEqualsMethodAlways(field.FieldType), Expression.PropertyOrField(self, field.Name), Expression.PropertyOrField(other, field.Name))))); var orExpr = Expression.OrElse(refEq, Expression.AndAlso(notNullX, Expression.AndAlso(notNullY, expr))); var lambda = Expression.Lambda>( typeof(A).GetTypeInfo().IsValueType ? expr : orExpr, self, other); return lambda.Compile(); } /// /// Provides a function that compares two record type arguments for structural equality, this /// compares the *fields* from each argument for equality and returns 0 if all are equal, -1 /// if X is less than Y, and 1 if X is greater than Y. /// /// You should cache the result of this method to reduce the work of building the IL /// each time. Better still use the `RecordType〈A〉` type to provide a cached version of these /// results. /// public static Func Compare(bool includeBase) { if (OrdResolve.Exists) { return OrdResolve.CompareFunc; } var fields = GetPublicInstanceFields( includeBase, typeof(NonOrdAttribute), typeof(NonStructuralAttribute), typeof(NonRecordAttribute) ); var self = Expression.Parameter(typeof(A), "self"); var other = Expression.Parameter(typeof(A), "other"); var Zero = Expression.Constant(0); var Minus1 = Expression.Constant(-1); var Plus1 = Expression.Constant(1); var Null = Expression.Constant(null, typeof(A)); var refEq = Expression.ReferenceEqual(self, other); var xIsNull = Expression.ReferenceEqual(self, Null); var yIsNull = Expression.ReferenceEqual(other, Null); var typeA = Expression.TypeEqual(self, typeof(A)); var typeB = Expression.TypeEqual(other, typeof(A)); var typesNotEqual = Expression.NotEqual(typeA, typeB); var returnTarget = Expression.Label(typeof(int)); var ord = Expression.Variable(typeof(int), "ord"); IEnumerable Fields() { foreach (var f in fields) { var m = Resolver.GetCompareMethodAlways(f.FieldType); var comparer = Expression.Assign( ord, Expression.Call( null, m, Expression.PropertyOrField(self, f.Name), Expression.PropertyOrField(other, f.Name))); if (f.FieldType.IsValueType) { yield return [comparer]; } else { var fnull = Expression.Constant(null, f.FieldType); yield return [ Expression.IfThen( Expression.And( Expression.ReferenceEqual(Expression.PropertyOrField(self, f.Name), fnull), Expression.IsFalse(Expression.ReferenceEqual(Expression.PropertyOrField(other, f.Name), fnull))), Expression.Return(returnTarget, Minus1)), Expression.IfThen( Expression.And( Expression.ReferenceEqual(Expression.PropertyOrField(other, f.Name), fnull), Expression.IsFalse(Expression.ReferenceEqual(Expression.PropertyOrField(self, f.Name), fnull))), Expression.Return(returnTarget, Plus1)), Expression.IfThenElse( Expression.ReferenceEqual( Expression.PropertyOrField(self, f.Name), Expression.PropertyOrField(other, f.Name)), Expression.Assign(ord, Zero), comparer) ]; } yield return [ // Fields are not equal Expression.IfThen( Expression.NotEqual(ord, Zero), Expression.Return(returnTarget, ord, typeof(int))) ]; } } var block = Expression.Block( new [] { ord }, new[] { Expression.IfThen(refEq, Expression.Return(returnTarget, Zero)), Expression.IfThen(xIsNull, Expression.Return(returnTarget, Minus1)), Expression.IfThen(yIsNull, Expression.Return(returnTarget, Plus1)), Expression.IfThen(typesNotEqual, Expression.Return(returnTarget, Minus1)) } .Concat( Fields().Bind(identity)) .Concat( new [] { Expression.Label(returnTarget, Zero) as Expression })); var lambda = Expression.Lambda>(block, self, other); return lambda.Compile(); } static Func ToStringExpr(bool includeBase) { var fields = GetPublicInstanceFields( includeBase, typeof(NonShowAttribute), typeof(NonRecordAttribute) ).ToArray(); var stringBuilder = GetConstructor().IfNone(() => throw new ArgumentException($"Constructor not found for StringBuilder")); var appendChar = GetPublicInstanceMethod("Append", true).IfNone(() => throw new ArgumentException($"Append method found for StringBuilder")); var appendString = GetPublicInstanceMethod("Append", true).IfNone(() => throw new ArgumentException($"Append method found for StringBuilder")); var toString = GetPublicInstanceMethod("ToString", false).IfNone(() => throw new ArgumentException($"ToString method found for StringBuilder")); var name = typeof(A).Name; var self = Expression.Parameter(typeof(A), "self"); var nullA = Expression.Constant(null, typeof(A)); var nullStr = Expression.Constant(null, typeof(string)); var sb = Expression.Variable(typeof(StringBuilder), "sb"); var tmpStr = Expression.Variable(typeof(string), "tmpStr"); var result = Expression.Variable(typeof(string), "result"); var returnTarget = Expression.Label(typeof(string)); if (name.IndexOf('`') != -1) name = name.Split('`').AsIterable().Head.Value!; Expression fieldExpr(FieldInfo field) { var convertToString = (GetPublicStaticMethod(typeof(Convert), "ToString", field.FieldType) || GetPublicStaticMethod(typeof(Convert), "ToString", typeof(object))) .IfNone(() => throw new Exception()); return Expression.Block( Expression.Assign(tmpStr, convertToString.GetParameters()[0].ParameterType == typeof(object) ? Expression.Call(convertToString, Expression.Convert(Expression.Field(self, field), typeof(object))) : Expression.Call(convertToString, Expression.Field(self, field))), Expression.IfThenElse( Expression.ReferenceEqual(tmpStr, nullStr), Expression.Call(sb, appendString, Expression.Constant("null")), Expression.Call(sb, appendString, tmpStr))); } var inner = Expression.Block( fields.Select(fieldExpr).Intersperse(Expression.Call(sb, appendString, Expression.Constant(", ")))); var outer = Expression.Block( Expression.Assign(sb, Expression.New(stringBuilder)), Expression.Call(sb, appendString, Expression.Constant(name)), Expression.Call(sb, appendChar, Expression.Constant('(')), inner, Expression.Call(sb, appendChar, Expression.Constant(')')), Expression.Assign(result, Expression.Call(sb, toString))); var expr = Expression.IfThenElse( Expression.ReferenceEqual(self, nullA), Expression.Assign(result, Expression.Constant("(null)")), outer); var block = Expression.Block( new [] { tmpStr, sb, result }, expr, Expression.Return(returnTarget, result), Expression.Label(returnTarget, result)); var lambda = Expression.Lambda>(block, self); return lambda.Compile(); } public static Func ToString(bool includeBase) { if (!ILCapability.Available) { return ToStringExpr(includeBase); } var isValueType = typeof(A).GetTypeInfo().IsValueType; var dynamic = new DynamicMethod("FieldsToString", typeof(string), [typeof(A)], typeof(A).Module, true); var fields = GetPublicInstanceFields( includeBase, typeof(NonShowAttribute), typeof(NonRecordAttribute) ).ToArray(); var stringBuilder = GetConstructor().IfNone(() => throw new ArgumentException($"Constructor not found for StringBuilder")); var appendChar = GetPublicInstanceMethod("Append", true).IfNone(() => throw new ArgumentException($"Append method found for StringBuilder")); var appendString = GetPublicInstanceMethod("Append", true).IfNone(() => throw new ArgumentException($"Append method found for StringBuilder")); var toString = GetPublicInstanceMethod("ToString", true).IfNone(() => throw new ArgumentException($"ToString method found for Object")); var name = typeof(A).Name; if (name.IndexOf('`') != -1) name = name.Split('`').AsIterable().Head.Value!; var il = dynamic.GetILGenerator(); il.DeclareLocal(typeof(StringBuilder)); var notNull = il.DefineLabel(); if (!isValueType) { // Check reference == null il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Brtrue_S, notNull); // Is null so return "(null)" il.Emit(OpCodes.Ldstr, "(null)"); il.Emit(OpCodes.Ret); il.MarkLabel(notNull); } // var sb = new StringBuilder() il.Emit(OpCodes.Newobj, stringBuilder); il.Emit(OpCodes.Stloc_0); // sb.Append('(') il.Emit(OpCodes.Ldloc_0); if (fields.Length == 0) { il.Emit(OpCodes.Ldstr, $"{name}"); } else { il.Emit(OpCodes.Ldstr, $"{name}("); } il.Emit(OpCodes.Callvirt, appendString); il.Emit(OpCodes.Pop); bool first = true; foreach (var field in fields) { var skipAppend = il.DefineLabel(); if (!first) { // sb.Append(", ") il.Emit(OpCodes.Ldloc_0); il.Emit(OpCodes.Ldstr, ", "); il.Emit(OpCodes.Callvirt, appendString); il.Emit(OpCodes.Pop); } if (!field.FieldType.GetTypeInfo().IsValueType) { var fieldNotNull = il.DefineLabel(); // If(this.field == null) il.Emit(OpCodes.Ldarg_0); il.Emit(isValueType ? OpCodes.Ldflda : OpCodes.Ldfld, field); il.Emit(OpCodes.Brtrue_S, fieldNotNull); // sb.Append("null") il.Emit(OpCodes.Ldloc_0); il.Emit(OpCodes.Ldstr, "null"); il.Emit(OpCodes.Callvirt, appendString); il.Emit(OpCodes.Pop); // continue il.Emit(OpCodes.Br_S, skipAppend); il.MarkLabel(fieldNotNull); } il.Emit(OpCodes.Ldloc_0); // sb il.Emit(OpCodes.Ldarg_0); // this il.Emit(isValueType ? OpCodes.Ldflda : OpCodes.Ldfld, field); var convertToString = (GetPublicStaticMethod(typeof(Convert), "ToString", field.FieldType) || GetPublicStaticMethod(typeof(Convert), "ToString", typeof(object))) .IfNone(() => throw new Exception()); if (field.FieldType.GetTypeInfo().IsValueType && convertToString.GetParameters().AsIterable().Head.Value!.ParameterType == typeof(object)) { il.Emit(OpCodes.Box, field.FieldType); } il.Emit(convertToString.IsVirtual ? OpCodes.Callvirt : OpCodes.Call, convertToString); il.Emit(OpCodes.Callvirt, appendString); il.Emit(OpCodes.Pop); il.MarkLabel(skipAppend); first = false; } if (fields.Length > 0) { // Append(')') il.Emit(OpCodes.Ldloc_0); il.Emit(OpCodes.Ldc_I4_S, ')'); il.Emit(OpCodes.Callvirt, appendChar); il.Emit(OpCodes.Pop); } // return sb.ToString() il.Emit(OpCodes.Ldloc_0); il.Emit(OpCodes.Callvirt, toString); il.Emit(OpCodes.Ret); return (Func)dynamic.CreateDelegate(typeof(Func)); } static Action GetObjectDataExpr(bool includeBase) { var fields = GetPublicInstanceFields( includeBase, typeof(NonSerializedAttribute), typeof(NonRecordAttribute) ); var argNullExcept = GetConstructor().IfNone(() => throw new Exception()); var self = Expression.Parameter(typeof(A), "self"); var info = Expression.Parameter(typeof(SerializationInfo), "info"); var returnTarget = Expression.Label(); Expression WriteField(FieldInfo field) { var name = PrettyFieldName(field); var addValue = (GetPublicInstanceMethod("AddValue", typeof(string), field.FieldType, true) || GetPublicInstanceMethod("AddValue", typeof(string), typeof(object), true)) .IfNone(() => throw new Exception()); return addValue.GetParameters()[1].ParameterType == typeof(object) ? Expression.Call(info, addValue, Expression.Constant(name), Expression.Convert(Expression.Field(self, field), typeof(object))) : Expression.Call(info, addValue, Expression.Constant(name), Expression.Field(self, field)); } var block = Expression.Block( Expression.IfThen( Expression.ReferenceEqual(info, Expression.Constant(null, typeof(SerializationInfo))), Expression.Throw(Expression.New(argNullExcept, Expression.Constant("info")))), Expression.Block(fields.Select(WriteField)), Expression.Return(returnTarget), Expression.Label(returnTarget)); var lambda = Expression.Lambda>(block, self, info); return lambda.Compile(); } public static Action GetObjectData(bool includeBase) { if (!ILCapability.Available) { return GetObjectDataExpr(includeBase); } var isValueType = typeof(A).GetTypeInfo().IsValueType; var dynamic = new DynamicMethod( "GetObjectData", null, new[] { typeof(A), typeof(SerializationInfo) }, typeof(A).Module, true); var fields = GetPublicInstanceFields( includeBase, typeof(NonSerializedAttribute), typeof(NonRecordAttribute) ); var argNullExcept = GetConstructor().IfNone(() => throw new Exception()); var il = dynamic.GetILGenerator(); var infoIsNotNull = il.DefineLabel(); // if(info == null) il.Emit(OpCodes.Ldarg_1); il.Emit(OpCodes.Brtrue_S, infoIsNotNull); /// throw new ArgumentNullException("info"); il.Emit(OpCodes.Ldstr, "info"); il.Emit(OpCodes.Newobj, argNullExcept); il.Emit(OpCodes.Throw); il.MarkLabel(infoIsNotNull); foreach (var field in fields) { var name = PrettyFieldName(field); il.Emit(OpCodes.Ldarg_1); il.Emit(OpCodes.Ldstr, name); il.Emit(OpCodes.Ldarg_0); il.Emit(isValueType ? OpCodes.Ldflda : OpCodes.Ldfld, field); var addValue = (GetPublicInstanceMethod("AddValue", typeof(string), field.FieldType, true) || GetPublicInstanceMethod("AddValue", typeof(string), typeof(object), true)) .IfNone(() => throw new Exception()); if (field.FieldType.GetTypeInfo().IsValueType && addValue.GetParameters()[1].ParameterType == typeof(object)) { il.Emit(OpCodes.Box, field.FieldType); } il.Emit(OpCodes.Callvirt, addValue); } il.Emit(OpCodes.Ret); return (Action)dynamic.CreateDelegate(typeof(Action)); } static Action SetObjectDataExpr(bool includeBase) { // Expression doesn't support setting of fields that are readonly or init only. // So we fall back to reflection for this. Not ideal. var fields = GetPublicInstanceFields( includeBase, typeof(NonSerializedAttribute), typeof(NonRecordAttribute)); return (self, info) => { foreach (var field in fields) { var name = PrettyFieldName(field); field.SetValue(self, info.GetValue(name, field.FieldType)); } }; } public static Action SetObjectData(bool includeBase) { if (!ILCapability.Available) { return SetObjectDataExpr(includeBase); } var dynamic = new DynamicMethod("SetObjectData", null, [typeof(A), typeof(SerializationInfo)], typeof(A).Module, true); var fields = GetPublicInstanceFields( includeBase, typeof(NonSerializedAttribute), typeof(NonRecordAttribute)); var getTypeFromHandle = GetPublicStaticMethod("GetTypeFromHandle").IfNone(() => throw new Exception()); var getValue = GetPublicInstanceMethod("GetValue", true).IfNone(() => throw new Exception()); var argNullExcept = GetConstructor().IfNone(() => throw new Exception()); var il = dynamic.GetILGenerator(); var infoIsNotNull = il.DefineLabel(); // if(info == null) il.Emit(OpCodes.Ldarg_1); il.Emit(OpCodes.Brtrue_S, infoIsNotNull); /// throw new ArgumentNullException("info"); il.Emit(OpCodes.Ldstr, "info"); il.Emit(OpCodes.Newobj, argNullExcept); il.Emit(OpCodes.Throw); il.MarkLabel(infoIsNotNull); foreach (var field in fields) { var name = PrettyFieldName(field); il.Emit(OpCodes.Ldarg_0); // this il.Emit(OpCodes.Ldarg_1); // info il.Emit(OpCodes.Ldstr, name); // field-name il.Emit(OpCodes.Ldtoken, field.FieldType); // typeof(FieldType) il.Emit(OpCodes.Call, getTypeFromHandle); // Type.GetTypeFromHandle(typeof(FieldType)) il.Emit(OpCodes.Callvirt, getValue); // info.GetValue("field-name", FieldType) if (field.FieldType.GetTypeInfo().IsValueType) { il.Emit(OpCodes.Unbox_Any, field.FieldType); } else { il.Emit(OpCodes.Castclass, field.FieldType); } il.Emit(OpCodes.Stfld, field); } il.Emit(OpCodes.Ret); return (Action)dynamic.CreateDelegate(typeof(Action)); } public static Func? GetPropertyOrField(string name) => GetProperty(name) ?? GetField(name); public static Func? GetProperty(string name) { var m = typeof(A).GetMethod($"get_{name}"); if (m == null) return null; if (m.ReturnType != typeof(B)) return null; if (ILCapability.Available) { var arg = typeof(A); var dynamic = new DynamicMethod( $"{typeof(A).Name}_{name}", typeof(B), [arg], typeof(A).Module, true); var il = dynamic.GetILGenerator(); il.DeclareLocal(typeof(B)); if (arg.IsValueType) { il.Emit(OpCodes.Ldarga_S, 0); } else { il.Emit(OpCodes.Ldarg_0); } if (m.IsVirtual) { il.Emit(OpCodes.Callvirt, m); } else { il.Emit(OpCodes.Call, m); } il.Emit(OpCodes.Stloc_0); il.Emit(OpCodes.Ldloc_0); il.Emit(OpCodes.Ret); return (Func)dynamic.CreateDelegate(typeof(Func)); } else { var larg0 = Expression.Parameter(typeof(A), "arg0"); var expr = Expression.Property(larg0, m); var lambda = Expression.Lambda>(expr, larg0); return lambda.Compile(); } } public static Func? GetField(string name) { var fld = typeof(A).GetField(name); if (fld == null) return null; if (fld.FieldType != typeof(B)) return null; if (ILCapability.Available) { var arg = typeof(A); var dynamic = new DynamicMethod( $"{typeof(A).Name}_{name}", typeof(B), new[] {arg}, typeof(A).Module, true); var il = dynamic.GetILGenerator(); il.DeclareLocal(typeof(B)); if (arg.IsValueType) { il.Emit(OpCodes.Ldarga_S, 0); } else { il.Emit(OpCodes.Ldarg_0); } il.Emit(OpCodes.Ldfld, fld); il.Emit(OpCodes.Stloc_0); il.Emit(OpCodes.Ldloc_0); il.Emit(OpCodes.Ret); return (Func)dynamic.CreateDelegate(typeof(Func)); } else { var larg0 = Expression.Parameter(typeof(A), "arg0"); var expr = Expression.Field(larg0, fld); var lambda = Expression.Lambda>(expr, larg0); return lambda.Compile(); } } static string PrettyFieldName(FieldInfo field) => field.Name.Split('<', '>').Match( () => "", x => x, (_, xs) => xs.Head.Value!); } public static class ILCapability { public static readonly bool Available; static ILCapability() => Available = GetAvailability(); static bool GetAvailability() { try { TestSystemExceptionCtor(); return true; } catch (PlatformNotSupportedException) { return false; } } static Func TestSystemExceptionCtor() { var type = typeof(SystemException); var ctor = type.GetConstructor(Type.EmptyTypes); var dynamic = new DynamicMethod("CreateInstance", type, Type.EmptyTypes, true); var il = dynamic.GetILGenerator(); il.Emit(OpCodes.Newobj, ctor!); il.Emit(OpCodes.Ret); return (Func)dynamic.CreateDelegate(typeof(Func)); } } ================================================ FILE: LanguageExt.Core/Utility/Inter.cs ================================================ using System.Runtime.CompilerServices; using System.Threading; namespace LanguageExt { internal static class Inter { [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int And(ref int loc, int value) { int current = loc; while (true) { int newValue = current & value; int oldValue = Interlocked.CompareExchange(ref loc, newValue, current); if (oldValue == current) { return oldValue; } current = oldValue; } } } } ================================================ FILE: LanguageExt.Core/Utility/Pool.cs ================================================ using System; using System.Collections.Concurrent; namespace LanguageExt { /// /// Type class for newing an object /// internal interface New { A New(); } /// /// Type class for newing an object with one constructor argument /// Also provides a Set for setting the value when being popped off a /// pool stack (see `Pool` below). /// internal interface New { A New(B value); void Set(A item, B value); } /// /// Thread-safe pooling /// Manages a concurrent stack of values that will grow as needed /// When spent new objects are allocated used the `New〈A〉` trait /// internal static class Pool where NewA : struct, New { static ConcurrentStack stack = new ConcurrentStack(); public static A Pop() => stack.TryPop(out var var) ? var : default(NewA).New(); public static void Push(A value) => stack.Push(value); } /// /// Thread-safe pooling /// Manages a concurrent stack of values that will grow as needed /// When spent new objects are allocated used the `New〈A〉` trait /// internal static class Pool where NewA : struct, New { static ConcurrentStack stack = new ConcurrentStack(); public static A Pop(B value) { if(stack.TryPop(out var var)) { default(NewA).Set(var, value); return var; } else { return default(NewA).New(value); } } public static void Push(A value) => stack.Push(value); } } ================================================ FILE: LanguageExt.Core/Utility/ReferenceEqualityComparer.cs ================================================ using System.Collections.Generic; namespace LanguageExt { internal class ReferenceEqualityComparer : IEqualityComparer { public bool Equals(A? x, A? y) => ReferenceEquals(x, y); public int GetHashCode(A? obj) => obj?.GetHashCode() ?? 0; } } ================================================ FILE: LanguageExt.Core/Utility/Reflect.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using static LanguageExt.Prelude; namespace LanguageExt { class Reflect { static bool Intersects(A[] ma, A[] mb) { foreach (var a in ma) { foreach (var b in mb) { if (a?.Equals(b) ?? false) return true; } } return false; } public static IEnumerable GetPublicInstanceFields(bool includeBase, params Type[] excludeAttrs) { var excludeAttrsSet = excludeAttrs.AsIterable().Map(a => a.Name).ToArray(); var publicFields = typeof(A) .GetTypeInfo() .GetAllFields(includeBase) #if !COREFX13 .OrderBy(f => f.MetadataToken) #endif .Where(f => { if (!f.IsPublic || f.IsStatic) return false; if (Intersects(f.CustomAttributes.AsIterable().Map(a => a.AttributeType.Name).ToArray(), excludeAttrsSet)) return false; return true; }); var publicPropNames = typeof(A) .GetTypeInfo() .GetAllProperties(includeBase) #if !COREFX13 .OrderBy(p => p.MetadataToken) #endif .Where(p => p.CanRead && (p.GetMethod?.IsPublic ?? false) && !IsStatic(p)) .Where(p => !Intersects(p.CustomAttributes.AsIterable().Map(a => a.AttributeType.Name).ToArray(), excludeAttrsSet)) .ToArray(); var backingFields = typeof(A) .GetTypeInfo() .GetAllFields(includeBase) #if !COREFX13 .OrderBy(p => p.MetadataToken) #endif .Where(f => f.IsPrivate && publicPropNames.AsIterable().Exists(p => f.Name.StartsWith($"<{p.Name}>"))) .ToArray(); return EnumerableOptimal.ConcatFast(publicFields, backingFields); } /// /// Returns true if the property is static by inspecting /// the static property of the accessors. Note that if /// there are no accessors then the property is assumed /// to be **non static**. Not sure that this case is /// even possible in CLR. /// /// /// public static bool IsStatic (PropertyInfo p) => p.GetAccessors( true ).AsIterable().Head.Map( x => x.IsStatic ).IfNone( false ); public static Option GetPublicStaticMethod(Type type, string name, Type argA) => type.GetTypeInfo() .DeclaredMethods .Where(x => { if (!x.IsStatic) return false; if (x.Name != name) return false; var ps = x.GetParameters(); if (ps.Length != 1) return false; if (ps[0].ParameterType != argA) return false; return true; }) .FirstOrDefault(); public static Option GetPublicStaticMethod(string name, Type argA) => typeof(TYPE) .GetTypeInfo() .DeclaredMethods .Where(x => { if (!x.IsStatic) return false; if (x.Name != name) return false; var ps = x.GetParameters(); if (ps.Length != 1) return false; if (ps[0].ParameterType != argA) return false; return true; }) .FirstOrDefault(); public static Option GetPublicStaticMethod(string name) => typeof(TYPE) .GetTypeInfo() .DeclaredMethods .Where(x => { if (!x.IsStatic) return false; if (x.Name != name) return false; var ps = x.GetParameters(); if (ps.Length != 1) return false; if (ps[0].ParameterType != typeof(A)) return false; return true; }) .FirstOrDefault(); public static Option GetPublicStaticMethod(string name) => typeof(TYPE) .GetTypeInfo() .DeclaredMethods .Where(x => { if (!x.IsStatic) return false; if (x.Name != name) return false; var ps = x.GetParameters(); if (ps.Length != 2) return false; if (ps[0].ParameterType != typeof(A)) return false; if (ps[1].ParameterType != typeof(B)) return false; return true; }) .FirstOrDefault(); public static Option GetPublicStaticMethod(string name, Type argA, Type argB) => typeof(TYPE) .GetTypeInfo() .DeclaredMethods .Where(x => { if (!x.IsStatic) return false; if (x.Name != name) return false; var ps = x.GetParameters(); if (ps.Length != 2) return false; if (ps[0].ParameterType != argA) return false; if (ps[1].ParameterType != argB) return false; return true; }) .FirstOrDefault(); public static Option GetPublicInstanceMethod(string name, bool includeBase) => typeof(TYPE) .GetTypeInfo() .GetAllMethods(includeBase) .Where(x => { if (x.IsStatic) return false; if (x.Name != name) return false; if (x.GetParameters().Length != 0) return false; return true; }) .FirstOrDefault(); public static Option GetPublicInstanceMethod(string name, bool includeBase) => typeof(TYPE) .GetTypeInfo() .GetAllMethods(includeBase) .Where(x => { if (x.IsStatic) return false; if (x.Name != name) return false; var ps = x.GetParameters(); if (ps.Length != 1) return false; if (ps[0].ParameterType != typeof(A)) return false; return true; }) .FirstOrDefault(); public static Option GetPublicInstanceMethod(string name, bool includeBase) => typeof(TYPE) .GetTypeInfo() .GetAllMethods(includeBase) .Where(x => { if (x.IsStatic) return false; if (x.Name != name) return false; var ps = x.GetParameters(); if (ps.Length != 2) return false; if (ps[0].ParameterType != typeof(A)) return false; if (ps[1].ParameterType != typeof(B)) return false; return true; }) .FirstOrDefault(); public static Option GetPublicInstanceMethod(Type type, string name, bool includeBase) => type.GetTypeInfo() .GetAllMethods(includeBase) .Where(x => { if (x.IsStatic) return false; if (x.Name != name) return false; if (x.GetParameters().Length != 0) return false; return true; }) .FirstOrDefault(); public static Option GetPublicInstanceMethod(string name, Type arg1, Type arg2, bool includeBase) => typeof(TYPE) .GetTypeInfo() .GetAllMethods(includeBase) .Where(x => { if (x.IsStatic) return false; if (x.Name != name) return false; var ps = x.GetParameters(); if (ps.Length != 2) return false; if (ps[0].ParameterType != arg1) return false; if (ps[1].ParameterType != arg2) return false; return true; }) .FirstOrDefault(); public static Option GetConstructor() => typeof(TYPE) .GetTypeInfo() .DeclaredConstructors .Where(x => { if (x.IsStatic) return false; if (x.GetParameters().Length != 0) return false; return true; }) .FirstOrDefault(); public static Option GetConstructor() => typeof(TYPE) .GetTypeInfo() .DeclaredConstructors .Where(x => { if (x.IsStatic) return false; var ps = x.GetParameters(); if (ps.Length != 1) return false; if (ps[0].ParameterType != typeof(A)) return false; return true; }) .FirstOrDefault(); public static Option GetConstructor() => typeof(TYPE) .GetTypeInfo() .DeclaredConstructors .Where(x => { if (x.IsStatic) return false; var ps = x.GetParameters(); if (ps.Length != 2) return false; if (ps[0].ParameterType != typeof(A)) return false; if (ps[1].ParameterType != typeof(B)) return false; return true; }) .FirstOrDefault(); public static Option GetConstructor() => typeof(TYPE) .GetTypeInfo() .DeclaredConstructors .Where(x => { if (x.IsStatic) return false; var ps = x.GetParameters(); if (ps.Length != 3) return false; if (ps[0].ParameterType != typeof(A)) return false; if (ps[1].ParameterType != typeof(B)) return false; if (ps[2].ParameterType != typeof(C)) return false; return true; }) .FirstOrDefault(); public static Option GetConstructor() => typeof(TYPE) .GetTypeInfo() .DeclaredConstructors .Where(x => { if (x.IsStatic) return false; var ps = x.GetParameters(); if (ps.Length != 4) return false; if (ps[0].ParameterType != typeof(A)) return false; if (ps[1].ParameterType != typeof(B)) return false; if (ps[2].ParameterType != typeof(C)) return false; if (ps[3].ParameterType != typeof(D)) return false; return true; }) .FirstOrDefault(); public static bool IsFunc(Type? type) => type != null && typeof(MulticastDelegate).IsAssignableFrom(type); public static bool IsAnonymous(Type? type) => type != null && Attribute.IsDefined(type, typeof(CompilerGeneratedAttribute), false) && type.IsGenericType && type.Name.Contains("AnonymousType") && (type.Name.StartsWith("<>") || type.Name.StartsWith("VB$")) && type.Attributes.HasFlag(TypeAttributes.NotPublic); } } ================================================ FILE: LanguageExt.Core/Utility/SysInfo.cs ================================================ using System; using System.Runtime.CompilerServices; namespace LanguageExt { /// /// System information helper /// public static class SysInfo { static int processorCount; static int defaultAsyncSequenceParallelism; /// /// Cached number of processors in the machine /// public static int ProcessorCount { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => processorCount == 0 ? (processorCount = Environment.ProcessorCount) : processorCount; } /// /// When working with an IEnumerable of Tasks or Seq of Tasks, this setting is used as /// the default number of task items streamed at any one time. This reduces pressure /// on the system when working with large lazy streams of tasks. /// /// Each method that uses it has an override that allows for per-usage settings. /// /// The default value is max(1, Environment.ProcessorCount / 2) /// public static int DefaultAsyncSequenceParallelism { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => defaultAsyncSequenceParallelism == 0 ? (defaultAsyncSequenceParallelism = Math.Max(4, ProcessorCount / 2)) : defaultAsyncSequenceParallelism; [MethodImpl(MethodImplOptions.AggressiveInlining)] set => defaultAsyncSequenceParallelism = Math.Max(4, value); } } } ================================================ FILE: LanguageExt.Core/Utility/TaskExt.cs ================================================ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT License. // See the LICENSE file in the project root for more information. using System.Collections.Generic; using System.Diagnostics; using System.Runtime.CompilerServices; namespace System.Threading.Tasks { internal static class TaskExt { public static readonly Task Never = new TaskCompletionSource().Task; public static WhenAnyValueTask WhenAny(ValueTask[] tasks) { var whenAny = new WhenAnyValueTask(tasks); whenAny.Start(); return whenAny; } // REVIEW: Evaluate options to reduce locking and test performance. Right now, there's one lock // protecting the queue and the completion delegate field. Care has been taken to limit // the time under the lock, and the (sequential single) reader path has limited locking. // Contention due to concurrent completion of tasks could be a concern. internal sealed class WhenAnyValueTask { /// /// The tasks to await. Entries in this array may be replaced using . /// private readonly ValueTask[] _tasks; /// /// Array of cached delegates passed to awaiters on tasks. These delegates have a closure containing the task index. /// private readonly Action[] _onReady; /// /// Queue of indexes of ready tasks. Awaiting the object will consume this queue in order. /// /// /// A lock on this field is taken when updating the queue or . /// private readonly Queue _ready; /// /// Callback of the current awaiter, if any. /// /// /// Protected for reads and writes by a lock on . /// private Action? _onCompleted; /// /// Creates a when any task around the specified tasks. /// /// Initial set of tasks to await. public WhenAnyValueTask(ValueTask[] tasks) { _tasks = tasks; var n = tasks.Length; _ready = new Queue(n); // NB: Should never exceed this length, so we won't see dynamic realloc. _onReady = new Action[n]; for (var i = 0; i < n; i++) { // // Cache these delegates, for they have closures (over `this` and `index`), and we need them // for each replacement of a task, to hook up the continuation. // int index = i; _onReady[index] = () => OnReady(index); } } /// /// Start awaiting the tasks. This is done separately from the constructor to avoid complexity dealing /// with handling concurrent callbacks to the current instance while the constructor is running. /// public void Start() { for (var i = 0; i < _tasks.Length; i++) { // // Register a callback with the task, which will enqueue the index of the completed task // for consumption by awaiters. // _tasks[i].ConfigureAwait(false).GetAwaiter().OnCompleted(_onReady[i]); } } /// /// Gets an awaiter to await completion of any of the awaited tasks, returning the index of the completed /// task. When sequentially awaiting the current instance, task indices are yielded in the order that of /// completion. If all tasks have completed and been observed by awaiting the current instance, the awaiter /// never returns on a subsequent attempt to await the completion of any task. The caller is responsible /// for bookkeeping that avoids awaiting this instance more often than the number of pending tasks. /// /// Awaiter to await completion of any of the awaited task. /// This class only supports a single active awaiter at any point in time. public Awaiter GetAwaiter() => new Awaiter(this); /// /// Replaces the (completed) task at the specified and starts awaiting it. /// /// The index of the parameter to replace. /// The new task to store and await at the specified index. public void Replace(int index, in ValueTask task) { Debug.Assert(_tasks[index].IsCompleted, "A task shouldn't be replaced before it has completed."); _tasks[index] = task; task.ConfigureAwait(false).GetAwaiter().OnCompleted(_onReady[index]); } /// /// Called when any task has completed (thus may run concurrently). /// /// The index of the completed task in . private void OnReady(int index) { Action? onCompleted = null; lock (_ready) { // // Store the index of the task that has completed. This will be picked up from GetResult. // _ready.Enqueue(index); // // If there's a current awaiter, we'll steal its continuation action and invoke it. By setting // the continuation action to null, we avoid waking up the same awaiter more than once. Any // task completions that occur while no awaiter is active will end up being enqueued in _ready. // if (_onCompleted != null) { onCompleted = _onCompleted; _onCompleted = null; } } onCompleted?.Invoke(); } /// /// Invoked by awaiters to check if any task has completed, in order to short-circuit the await operation. /// /// true if any task has completed; otherwise, false. private bool IsCompleted() { // REVIEW: Evaluate options to reduce locking, so the single consuming awaiter has limited contention // with the multiple concurrent completing enumerator tasks, e.g. using ConcurrentQueue. lock (_ready) { return _ready.Count > 0; } } /// /// Gets the index of the earliest task that has completed, used by the awaiter. After stealing an index from /// the ready queue (by means of awaiting the current instance), the user may chose to replace the task at the /// returned index by a new task, using the method. /// /// Index of the earliest task that has completed. private int GetResult() { lock (_ready) { return _ready.Dequeue(); } } /// /// Register a continuation passed by an awaiter. /// /// The continuation action delegate to call when any task is ready. private void OnCompleted(Action action) { bool shouldInvoke = false; lock (_ready) { // // Check if we have anything ready (which could happen in the short window between checking // for IsCompleted and calling OnCompleted). If so, we should invoke the action directly. Not // doing so would be a correctness issue where a task has completed, its index was enqueued, // but the continuation was never called (unless another task completes and calls the action // delegate, whose subsequent call to GetResult would pick up the lost index). // if (_ready.Count > 0) { shouldInvoke = true; } else { Debug.Assert(_onCompleted == null, "Only a single awaiter is allowed."); _onCompleted = action; } } // // NB: We assume this case is rare enough (IsCompleted and OnCompleted happen right after one // another, and an enqueue should have happened right in between to go from an empty to a // non-empty queue), so we don't run the risk of triggering a stack overflow due to // synchronous completion of the await operation (which may be in a loop that awaits the // current instance again). // if (shouldInvoke) { action(); } } /// /// Awaiter type used to await completion of any task. /// public struct Awaiter : INotifyCompletion { private readonly WhenAnyValueTask _parent; public Awaiter(WhenAnyValueTask parent) => _parent = parent; public bool IsCompleted => _parent.IsCompleted(); public int GetResult() => _parent.GetResult(); public void OnCompleted(Action action) => _parent.OnCompleted(action); } } } } ================================================ FILE: LanguageExt.Core/Utility/WaitAsync.cs ================================================ using System; using System.Threading; using System.Threading.Tasks; namespace LanguageExt; public static class WaitAsync { public static async ValueTask<(A A, B B)> WaitAll(ValueTask va, ValueTask vb) { var ta = va.AsTask(); var tb = vb.AsTask(); await Task.WhenAll(ta, tb).ConfigureAwait(false); return (ta.Result, tb.Result); } public static async ValueTask<(A A, B B, C C)> WaitAll(ValueTask va, ValueTask vb, ValueTask vc) { var ta = va.AsTask(); var tb = vb.AsTask(); var tc = vc.AsTask(); await Task.WhenAll(ta, tb, tc).ConfigureAwait(false); return (ta.Result, tb.Result, tc.Result); } public static async ValueTask<(A A, B B, C C, D D)> WaitAll(ValueTask va, ValueTask vb, ValueTask vc, ValueTask vd) { var ta = va.AsTask(); var tb = vb.AsTask(); var tc = vc.AsTask(); var td = vd.AsTask(); await Task.WhenAll(ta, tb, tc, td).ConfigureAwait(false); return (ta.Result, tb.Result, tc.Result, td.Result); } public static async ValueTask<(A A, B B, C C, D D, E E)> WaitAll(ValueTask va, ValueTask vb, ValueTask vc, ValueTask vd, ValueTask ve) { var ta = va.AsTask(); var tb = vb.AsTask(); var tc = vc.AsTask(); var td = vd.AsTask(); var te = ve.AsTask(); await Task.WhenAll(ta, tb, tc, td, te).ConfigureAwait(false); return (ta.Result, tb.Result, tc.Result, td.Result, te.Result); } public static async ValueTask<(A A, B B, C C, D D, E E, F F)> WaitAll(ValueTask va, ValueTask vb, ValueTask vc, ValueTask vd, ValueTask ve, ValueTask vf) { var ta = va.AsTask(); var tb = vb.AsTask(); var tc = vc.AsTask(); var td = vd.AsTask(); var te = ve.AsTask(); var tf = vf.AsTask(); await Task.WhenAll(ta, tb, tc, td, te, tf).ConfigureAwait(false); return (ta.Result, tb.Result, tc.Result, td.Result, te.Result, tf.Result); } public static async Task WaitOneAsync(this WaitHandle handle, int millisecondsTimeout, CancellationToken cancellationToken) { RegisteredWaitHandle? registeredHandle = null; var tokenRegistration = default(CancellationTokenRegistration); try { var tcs = new TaskCompletionSource(); registeredHandle = ThreadPool.RegisterWaitForSingleObject( handle, static (state, timedOut) => ((TaskCompletionSource?)state)?.TrySetResult(!timedOut), tcs, millisecondsTimeout, true); tokenRegistration = cancellationToken.Register( static state => ((TaskCompletionSource?)state)?.TrySetCanceled(), tcs); return await tcs.Task.ConfigureAwait(false); } finally { registeredHandle?.Unregister(null); tokenRegistration.Dispose(); } } public static async Task WaitOneAsync(this WaitHandle handle, int millisecondsTimeout) { RegisteredWaitHandle? registeredHandle = null; try { var tcs = new TaskCompletionSource(); registeredHandle = ThreadPool.RegisterWaitForSingleObject( handle, static (state, timedOut) => ((TaskCompletionSource)state!).TrySetResult(!timedOut), tcs, millisecondsTimeout, true); return await tcs.Task.ConfigureAwait(false); } finally { registeredHandle?.Unregister(null); } } public static Task WaitOneAsync(this WaitHandle handle, TimeSpan timeout, CancellationToken cancellationToken) => handle.WaitOneAsync((int)timeout.TotalMilliseconds, cancellationToken); public static Task WaitOneAsync(this WaitHandle handle, CancellationToken cancellationToken) => handle.WaitOneAsync(Timeout.Infinite, cancellationToken); public static Task WaitOneAsync(this WaitHandle handle) => handle.WaitOneAsync(Timeout.Infinite); } ================================================ FILE: LanguageExt.Core/Void.cs ================================================ using System.Diagnostics.Contracts; using LanguageExt.Common; namespace LanguageExt; /// /// Meant to represent `void`, but we can't construct a `System.Void`. /// /// A `Void` is the initial object in a category, equivalent to an empty set, and because there are no values in an /// empty set there's no way to construct a type of `Void`. /// /// /// Usages: /// * Used in the pipes system to represent a 'closed' path. /// * Used in `Decidable` contravariant functors to 'lose' information. /// public record Void { /// /// Voids can't be constructed, as they're the 'uninhabited type', i.e. an empty set, with no values. /// Void() => throw new BottomException(); [Pure] public override string ToString() => "void"; } ================================================ FILE: LanguageExt.FSharp/LanguageExt.FSharp.csproj ================================================ TRACE;DEBUG CONTRACTS_FULL 1701;1702;1705;IDE1006;CS1591;CS1573;CS1712;CS1711;CS1572;CS1587 enable net10.0 5.0.0-beta-77 LanguageExt.FSharp LanguageExt.FSharp Paul Louth Functional language extensions for C# Copyright (c) Paul Louth. All rights reserved. README.nuget.md Helper library for the LanguageExt functional framework that facilitates interop between F# native types like Option, Map, List, etc to .NET and Language-Ext types. C#, Functional, Language Extension, Monad, Option, Either, Reader, Writer, State, List, Set, Map, Queue, Memo, Memoization, Immutable, Lambda, Pattern Matching, Tuple lang-ext-small.png https://github.com/louthy/language-ext MIT false bin\$(Configuration)\$(TargetFramework)\$(AssemblyName).xml library 5.0.0.0 5.0.0.0 default ================================================ FILE: LanguageExt.FSharp/Prelude.cs ================================================ using System; using Microsoft.FSharp.Core; using Microsoft.FSharp.Collections; using static LanguageExt.Prelude; namespace LanguageExt; public static class FSharp { /// /// Convert an F# Option into a LanguageExt Option /// public static Option fs(FSharpOption fsOption) => FSharpOption.get_IsSome(fsOption) ? Some(fsOption.Value) : None; /// /// Convert a LanguageExt Option into an F# Option /// public static FSharpOption fs(Option option) => option.Map(FSharpOption.Some) .IfNone(FSharpOption.None)!; /// /// Convert an F# List into an IEnumerable T /// public static Lst fs(FSharpList fsList) => List.createRange(fsList); /// /// Convert a LanguageExt List (Lst T) into an F# List /// public static FSharpList fs(Lst list) => ListModule.OfSeq(list); /// /// Convert an F# Map into a LanguageExt Map (Map K V) /// public static Map fs(FSharpMap fsMap) => Map.addRange( Map(), List.map(fsMap, identity) ); /// /// Convert a LanguageExt Map (Map K V) into an F# Map /// public static FSharpMap fs(Map map) => MapModule.OfSeq(map.AsIterable().Map(item => Tuple.Create(item.Key,item.Value))); /// /// Convert LanguageExt Unit to F# unit /// /// () public static void fs(Unit unit) { } /// /// Convert an F# Result into a LanguageExt Either /// public static Either fs(FSharpResult result) => result.IsOk ? Either.Right(result.ResultValue) : Either.Left(result.ErrorValue); /// /// Convert a LanguageExt Either into an F# Result /// public static FSharpResult fs(Either either) => either.Match(FSharpResult.NewError, FSharpResult.NewOk); /// /// Convert a LanguageExt Option into an F# Option /// public static FSharpOption ToFSharp(this Option option) => option.IsNone ? FSharpOption.None : match(option, Some: FSharpOption.Some, None: () => failwith>("returns null, so can't use the None branch")); /// /// Convert a LanguageExt Map (Map K V) into an F# Map /// public static FSharpMap ToFSharp(this Map map) => MapModule.OfSeq(map.AsIterable().Map(item => Tuple.Create(item.Key, item.Value))); /// /// Convert a LanguageExt List (Lst A) into an F# List /// public static FSharpList ToFSharp(this Lst list) => ListModule.OfSeq(list); /// /// Convert a LanguageExt Either into an F# Result /// public static FSharpResult ToFSharp(this Either either) => either.Match(FSharpResult.NewError, FSharpResult.NewOk); } ================================================ FILE: LanguageExt.FSharp/README.nuget.md ================================================ # LanguageExt.FSharp `LanguageExt.FSharp` is provides interop between F# and C# for the [language-ext functional programming framework](https://github.com/louthy/language-ext). The framework uses and abuses the features of C# to provide a pure functional-programming 'Base Class Library' that, if you squint, can look like extensions to the language itself. The desire here is to make programming in C# much more robust by helping the engineer's inertia flow in the direction of declarative and pure functional code rather than imperative. Using these techniques for large code-bases can bring tangible benefits to long-term maintenance by removing hidden complexity and by easing the engineer's cognitive load. ## Features ### [Functional effects and IO](https://louthy.github.io/language-ext/LanguageExt.Core/Effects/index.html) | Location | Feature | Description | |----------|--------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `Core` | `IO` | [A synchronous and asynchronous side-effect: an IO monad](https://louthy.github.io/language-ext/LanguageExt.Core/Effects/IO/index.html) | | `Core` | `Eff` | [A synchronous and asynchronous side-effect with error handling](https://louthy.github.io/language-ext/LanguageExt.Core/Effects/Eff/Eff%20no%20runtime/index.html) | | `Core` | `Eff` | [Same as `Eff` but with an injectable runtime for dependency-injection: a unit testable IO monad](https://louthy.github.io/language-ext/LanguageExt.Core/Effects/Eff/Eff%20with%20runtime/index.html) | | `Core` | Pipes | [A clean and powerful stream processing system that lets you build and connect reusable streaming components](https://louthy.github.io/language-ext/LanguageExt.Core/Effects/Pipes/index.html) | | `Core` | StreamT | [less powerful (than Pipes), but easier to use streaming effects transformer](https://louthy.github.io/language-ext/LanguageExt.Core/Effects/StreamT/index.html) | ### [Atomic concurrency and collections](https://louthy.github.io/language-ext/LanguageExt.Core/Concurrency/index.html) | Location | Feature | Description | |----------|------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------| | `Core` | `Atom` | [A lock-free atomically mutable reference for working with shared state](https://louthy.github.io/language-ext/LanguageExt.Core/Concurrency/Atom) | | `Core` | `Ref` | [An atomic reference to be used in the transactional memory system](https://louthy.github.io/language-ext/LanguageExt.Core/Concurrency/STM) | | `Core` | `AtomHashMap` | [An immutable `HashMap` with a lock-free atomically mutable reference](https://louthy.github.io/language-ext/LanguageExt.Core/Concurrency/AtomHashMap) | | `Core` | `AtomSeq` | [An immutable `Seq` with a lock-free atomically mutable reference](https://louthy.github.io/language-ext/LanguageExt.Core/Concurrency/AtomSeq) | | `Core` | `VectorClock` | [Understand distributed causality](https://louthy.github.io/language-ext/LanguageExt.Core/Concurrency/VectorClock) | | `Core` | `VersionVector` | [A vector clock with some versioned data](https://louthy.github.io/language-ext/LanguageExt.Core/Concurrency/VersionVector) | | `Core` | `VersionHashMap ` | [Distrubuted atomic versioning of keys in a hash-map](https://louthy.github.io/language-ext/LanguageExt.Core/Concurrency/VersionHashMap) | ### [Immutable collections](https://louthy.github.io/language-ext/LanguageExt.Core/Immutable%20Collections/index.html) | Location | Feature | Description | |----------|----------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `Core` | `Arr` | [Immutable array](https://louthy.github.io/language-ext/LanguageExt.Core/Immutable%20Collections/Arr/index.html) | | `Core` | `Seq` | [Lazy immutable list, evaluate at-most-once](https://louthy.github.io/language-ext/LanguageExt.Core/Immutable%20Collections/Seq/index.html) - very, very fast! | | `Core` | `Iterable` | [Wrapper around `IEnumerable` with support for traits](https://louthy.github.io/language-ext/LanguageExt.Core/Immutable%20Collections/Iterable/index.html) - enables the higher-kinded traits to work with enumerables. | | `Core` | `Lst` | [Immutable list](https://louthy.github.io/language-ext/LanguageExt.Core/Immutable%20Collections/List/index.html) - use `Seq` over `Lst` unless you need `InsertAt` | | `Core` | `Map` | [Immutable map](https://louthy.github.io/language-ext/LanguageExt.Core/Immutable%20Collections/Map/index.html) | | `Core` | `Map` | [Immutable map with Ord constraint on `K`](https://louthy.github.io/language-ext/LanguageExt.Core/Immutable%20Collections/Map/index.html) | | `Core` | `HashMap` | [Immutable hash-map](https://louthy.github.io/language-ext/LanguageExt.Core/Immutable%20Collections/HashMap/index.html) | | `Core` | `HashMap` | [Immutable hash-map with Eq constraint on `K`](https://louthy.github.io/language-ext/LanguageExt.Core/Immutable%20Collections/HashMap/index.html) | | `Core` | `Set` | [Immutable set](https://louthy.github.io/language-ext/LanguageExt.Core/Immutable%20Collections/Set/index.html) | | `Core` | `Set` | [Immutable set with Ord constraint on `A`](https://louthy.github.io/language-ext/LanguageExt.Core/Immutable%20Collections/Set/index.html) | | `Core` | `HashSet` | [Immutable hash-set](https://louthy.github.io/language-ext/LanguageExt.Core/Immutable%20Collections/HashSet/index.html) | | `Core` | `HashSet` | [Immutable hash-set with Eq constraint on `A`](https://louthy.github.io/language-ext/LanguageExt.Core/Immutable%20Collections/HashSet/index.html) | | `Core` | `Que` | [Immutable queue](https://louthy.github.io/language-ext/LanguageExt.Core/Immutable%20Collections/Queue/index.html) | | `Core` | `Stck` | [Immutable stack](https://louthy.github.io/language-ext/LanguageExt.Core/Immutable%20Collections/Stack/index.html) | ### [Optional and alternative value monads](https://louthy.github.io/language-ext/LanguageExt.Core/Monads/Alternative%20Monads/index.html) | Location | Feature | Description | |----------|---------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `Core` | `Option` | [Option monad](https://louthy.github.io/language-ext/LanguageExt.Core/Monads/Alternative%20Monads/Option/index.html) | | `Core` | `OptionT` | [Option monad-transformer](https://louthy.github.io/language-ext/LanguageExt.Core/Monads/Alternative%20Monads/OptionT/index.html) | | `Core` | `Either` | [Right/Left choice monad](https://louthy.github.io/language-ext/LanguageExt.Core/Monads/Alternative%20Monads/Either/index.html) | | `Core` | `EitherT` | [Right/Left choice monad-transformer](https://louthy.github.io/language-ext/LanguageExt.Core/Monads/Alternative%20Monads/EitherT/index.html) | | `Core` | `Fin` | [`Error` handling monad, like `Either`](https://louthy.github.io/language-ext/LanguageExt.Core/Monads/Alternative%20Monads/Fin/index.html) | | `Core` | `FinT` | [`Error` handling monad-transformer](https://louthy.github.io/language-ext/LanguageExt.Core/Monads/Alternative%20Monads/FinT/index.html) | | `Core` | `Try` | [Exception handling monad](https://louthy.github.io/language-ext/LanguageExt.Core/Monads/Alternative%20Monads/Try/index.html) | | `Core` | `TryT` | [Exception handling monad-transformer](https://louthy.github.io/language-ext/LanguageExt.Core/Monads/Alternative%20Monads/TryT/index.html) | | `Core` | `Validation` | [Validation applicative and monad](https://louthy.github.io/language-ext/LanguageExt.Core/Monads/Alternative%20Monads/Validation/index.html) for collecting multiple errors before aborting an operation | | `Core` | `ValidationT` | [Validation applicative and monad-transformer](https://louthy.github.io/language-ext/LanguageExt.Core/Monads/Alternative%20Monads/ValidationT/index.html) | ### [State managing monads](https://louthy.github.io/language-ext/LanguageExt.Core/Monads/State%20and%20Environment%20Monads/index.html) | Location | Feature | Description | |----------|--------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `Core` | `Reader` | [Reader monad](https://louthy.github.io/language-ext/LanguageExt.Core/Monads/State%20and%20Environment%20Monads/Reader/Reader/index.html) | | `Core` | `ReaderT` | [Reader monad-transformer](https://louthy.github.io/language-ext/LanguageExt.Core/Monads/State%20and%20Environment%20Monads/Reader/ReaderT/index.html) | | `Core` | `Writer` | [Writer monad that logs to a `W` constrained to be a Monoid](https://louthy.github.io/language-ext/LanguageExt.Core/Monads/State%20and%20Environment%20Monads/Writer/Writer/index.html) | | `Core` | `WriterT` | [Writer monad-transformer](https://louthy.github.io/language-ext/LanguageExt.Core/Monads/State%20and%20Environment%20Monads/Writer/WriterT/index.html) | | `Core` | `State` | [State monad](https://louthy.github.io/language-ext/LanguageExt.Core/Monads/State%20and%20Environment%20Monads/State/State/index.html) | | `Core` | `StateT` | [State monad-transformer](https://louthy.github.io/language-ext/LanguageExt.Core/Monads/State%20and%20Environment%20Monads/State/StateT/index.html) | ### [Parser combinators](https://louthy.github.io/language-ext/LanguageExt.Parsec/index.html) | Location | Feature | Description | |----------|----------------|--------------------------------------------------------------------------------------------------------------------------------| | `Parsec` | `Parser` | [String parser monad and full parser combinators library](https://louthy.github.io/language-ext/LanguageExt.Parsec/index.html) | | `Parsec` | `Parser` | [Parser monad that can work with any input stream type](https://louthy.github.io/language-ext/LanguageExt.Parsec/index.html) | ### [Pretty](https://louthy.github.io/language-ext/LanguageExt.Core/Pretty/index.html) | Location | Feature | Description | |----------|----------|--------------------------------------------------| | `Core` | `Doc` | Produce nicely formatted text with smart layouts | ### [Differencing](https://louthy.github.io/language-ext/LanguageExt.Core/DataTypes/Patch/index.html) | Location | Feature | Description | |----------|-----------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `Core` | `Patch` | Uses patch-theory to efficiently calculate the difference (`Patch.diff(list1, list2)`) between two collections of `A` and build a patch which can be applied (`Patch.apply(patch, list)`) to one to make the other (think git diff). | ### [Traits](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/index.html) The traits are major feature of `v5`+ language-ext that makes generic programming with higher-kinds a reality. Check out Paul's [series on Higher Kinds](https://paullouth.com/higher-kinds-in-c-with-language-ext/) to get a deeper insight. | Location | Feature | Description | |----------|----------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `Core` | `MonoidK` | [A monoid on applicative functors](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Alternative/index.html) | | `Core` | `Applicative` | [Applicative functor](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Applicative/index.html) | | `Core` | `Eq` | [Ad-hoc equality trait](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Eq/index.html) | | `Core` | `Fallible` | [Trait that describes types that can fail](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Fallible/index.html) | | `Core` | `Foldable` | [Aggregation over a structure](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Foldable/index.html) | | `Core` | `Functor` | [Functor `Map`](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Functor/index.html) | | `Core` | `Has` | [Used in runtimes to enable DI-like capabilities](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Has/index.html) | | `Core` | `Hashable` | [Ad-hoc has-a-hash-code trait](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Hashable/index.html) | | `Core` | `Local` | [Creates a local environment to run a computation ](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Local/index.html) | | `Core` | `Monad` | [Monad trait](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Monads/Monad/index.html) | | `Core` | `MonadT` | [Monad transformer trait](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Monads/MonadT/index.html) | | `Core` | `Monoid` | [A monoid is a type with an identity `Empty` and an associative binary operation `+`](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Monoid/index.html) | | `Core` | `MonoidK` | [Equivalent of monoids for working on higher-kinded types](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/MonoidK/index.html) | | `Core` | `Mutates` | [Used in runtimes to enable stateful operations](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Mutates/index.html) | | `Core` | `Ord` | [Ad-hoc ordering / comparisons](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Ord/index.html) | | `Core` | `Range` | [Abstraction of a range of values](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Range/index.html) | | `Core` | `Readable` | [Generalised Reader monad abstraction](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Readable/index.html) | | `Core` | `SemigroupK` | [A semigroup on functors](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/SemigroupK/index.html) | | `Core` | `Semigroup` | [Provides an associative binary operation `+`](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Semigroup/index.html) | | `Core` | `SemigroupK` | [Equivalent of semigroups for working with higher-kinded types](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/SemigroupK/index.html) | | `Core` | `Stateful` | [Generalised State monad abstraction](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Stateful/index.html) | | `Core` | `Traversable` | [Traversable structures support element-wise sequencing of Applicative effects](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Traversable/index.html) | | `Core` | `Writable` | [Generalised Writer monad abstraction](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Writable/index.html) | ### [Value traits](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Domain/index.html) These work a little like `NewType` but they impart semantic meaning and some common operators for the underlying value. | Location | Feature | Description | |----------|--------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `Core` | `DomainType` | Provides a mapping from `SELF` to an underlying representation: `REPR` | | `Core` | `Identifier ` | Identifiers (like IDs in databases: `PersonId` for example), they are equivalent to `DomaintType` with equality. | | `Core` | `VectorSpace` | Scalable values; can add and subtract self, but can only multiply and divide by a scalar. Can also negate. | | `Core` | `Amount ` | Quantities, such as the amount of money in USD on a bank account or a file size in bytes. Derives `VectorSpace`, `IdentifierLike`, `DomainType`, and is orderable (comparable). | | `Core` | `LocusLike ` | Works with space-like structures. Spaces have absolute and relative distances. Has an origin/zero point and derives `DomainType`, `IdentifierLike`, `AmountLike` and `VectorSpace`. `DISTANCE` must also be an `AmountLike`. | ================================================ FILE: LanguageExt.Megaparsec/Delegates.cs ================================================ using LanguageExt.Traits; namespace LanguageExt.Megaparsec; public delegate K ConsumedOK(A value, State state, Hints hints); public delegate K ConsumedErr(ParseError error, State state); public delegate K EmptyOK(A value, State state, Hints hints); public delegate K EmptyErr(ParseError error, State state); ================================================ FILE: LanguageExt.Megaparsec/ErrorFancy/ErrorFancy.Module.cs ================================================ namespace LanguageExt.Megaparsec; public static class ErrorFancy { public static ErrorFancy Fail(string value) => new ErrorFancy.Fail(value); public static ErrorFancy Indentation(int ordering, int reference, int actual) => new ErrorFancy.Indentation(ordering, reference, actual); public static ErrorFancy Custom(E value) => new ErrorFancy.Custom(value); } ================================================ FILE: LanguageExt.Megaparsec/ErrorFancy/ErrorFancy.cs ================================================ namespace LanguageExt.Megaparsec; public abstract record ErrorFancy : IComparable> { public record Fail(string Value) : ErrorFancy { public override int CompareTo(ErrorFancy? other) => other is Fail(var rhs) ? string.Compare(Value, rhs, StringComparison.Ordinal) : 1; } public record Indentation(int Ordering, int Reference, int Actual) : ErrorFancy { public override int CompareTo(ErrorFancy? other) => other switch { Fail => 0, Indentation(var rhsOrdering, var rhsReference, var rhsActual) => Ordering.CompareTo(rhsOrdering) switch { 0 => Reference.CompareTo(rhsReference) switch { 0 => Actual.CompareTo(rhsActual), var n => n }, var n => n }, Custom => 1, _ => throw new NotSupportedException() }; } public record Custom(E Value) : ErrorFancy { public override int CompareTo(ErrorFancy? other) => other is Custom(var rhs) ? Comparer.Default.Compare(Value, rhs) : 0; } public abstract int CompareTo(ErrorFancy? other); } ================================================ FILE: LanguageExt.Megaparsec/ErrorItem/ErrorItem.Extensions.cs ================================================ using LanguageExt.Traits; namespace LanguageExt.Megaparsec; public static class ErrorItemExtensions { public static ErrorItem As(this K ea) => (ErrorItem)ea; } ================================================ FILE: LanguageExt.Megaparsec/ErrorItem/ErrorItem.Module.cs ================================================ using LanguageExt.Traits; namespace LanguageExt.Megaparsec; public class ErrorItem : Functor { public static ErrorItem Token(T token) => new ErrorItem.Tokens([token]); public static ErrorItem Tokens(in Seq tokens) => new ErrorItem.Tokens(tokens); public static ErrorItem Tokens(in ReadOnlySpan tokens) => new ErrorItem.Tokens([..tokens]); public static ErrorItem Tokens(in S tokens) where S : TokenStream => Tokens(S.ChunkToTokens(tokens)); public static ErrorItem Label(string label) => new ErrorItem.Label(label); public static ErrorItem EndOfInput() => new ErrorItem.EndfOfInput(); public static K Map(Func f, K ma) => ma switch { ErrorItem.Tokens (var tokens) => Tokens(tokens.Map(f)), ErrorItem.Label (var label) => Label(label), ErrorItem.EndfOfInput => EndOfInput(), _ => throw new NotSupportedException() }; } ================================================ FILE: LanguageExt.Megaparsec/ErrorItem/ErrorItem.cs ================================================ using LanguageExt.Traits; namespace LanguageExt.Megaparsec; /// /// A data type that is used to represent “unexpected or expected” items in /// 'ParseError'. It is parametrised over the token type `T`. /// /// Token type public abstract record ErrorItem : K, IComparable> { public ErrorItem Map(Func f) => this.Kind().Map(f).As(); public ErrorItem Select(Func f) => this.Kind().Map(f).As(); /// /// Non-empty stream of tokens /// /// Tokens /// Token type public record Tokens(in Seq Items) : ErrorItem { public override int CompareTo(ErrorItem? other) => throw new NotImplementedException(); } /// /// Label (should not be empty) /// /// Label value /// Token type public record Label(string Value) : ErrorItem { public override int CompareTo(ErrorItem? other) => throw new NotImplementedException(); } /// /// End of input /// /// Token type public record EndfOfInput : ErrorItem { public override int CompareTo(ErrorItem? other) => throw new NotImplementedException(); } public abstract int CompareTo(ErrorItem? other); public static bool operator >(ErrorItem l, ErrorItem r) => l.CompareTo(r) > 0; public static bool operator >=(ErrorItem l, ErrorItem r) => l.CompareTo(r) >= 0; public static bool operator <(ErrorItem l, ErrorItem r) => l.CompareTo(r) < 0; public static bool operator <=(ErrorItem l, ErrorItem r) => l.CompareTo(r) <= 0; } ================================================ FILE: LanguageExt.Megaparsec/ErrorItem/ExpectedErrors.cs ================================================ namespace LanguageExt.Megaparsec; /// /// Precomputed expected errors /// /// /// By precomputing expected errors, we can avoid creating a lot of additional memory allocations for common /// parser combinators. /// public static class ExpectedErrors { public static readonly Set> newline = Set.singleton(ErrorItem.Label("newline")); public static readonly Set> tab = Set.singleton(ErrorItem.Label("tab")); public static readonly Option whiteSpace = "white space"; public static readonly Set> whiteSpaceChar = Set.singleton(ErrorItem.Label("white space")); public static readonly Set> upperChar = Set.singleton(ErrorItem.Label("uppercase letter")); public static readonly Set> lowerChar = Set.singleton(ErrorItem.Label("lowercase letter")); public static readonly Set> letterChar = Set.singleton(ErrorItem.Label("letter")); public static readonly Set> alphaNumChar = Set.singleton(ErrorItem.Label("alphanumeric character")); public static readonly Set> digitChar = Set.singleton(ErrorItem.Label("digit")); public static readonly Set> binaryDigitChar = Set.singleton(ErrorItem.Label("binary digit")); public static readonly Set> hexDigitChar = Set.singleton(ErrorItem.Label("hexadecimal digit")); public static readonly Set> numberChar = Set.singleton(ErrorItem.Label("numeric character")); public static readonly Set> symbolChar = Set.singleton(ErrorItem.Label("symbol")); public static readonly Set> punctuationChar = Set.singleton(ErrorItem.Label("punctuation")); public static readonly Set> control = Set.singleton(ErrorItem.Label("control character")); public static readonly Set> separator = Set.singleton(ErrorItem.Label("separator")); } ================================================ FILE: LanguageExt.Megaparsec/Hints/Hints.Module.cs ================================================ namespace LanguageExt.Megaparsec; public static class Hints { /// /// No hints /// public static Hints empty() => Hints.Empty; /// /// No hints /// public static Hints singleton(ErrorItem value) => new ([value]); /// /// Convert a `ParseError` record into 'Hints'. /// /// /// /// Token type /// Error type /// Hints public static Hints fromOffset(int streamPos, ParseError error) => error switch { ParseError.Trivial(var errOffset, _, var ps) => streamPos == errOffset ? ps.IsEmpty ? Hints.Empty : new Hints(ps) : Hints.Empty, _ => Hints.Empty }; } ================================================ FILE: LanguageExt.Megaparsec/Hints/Hints.cs ================================================ using LanguageExt.Traits; using LanguageExt.UnsafeValueAccess; namespace LanguageExt.Megaparsec; /// /// `Hints` represent a collection of `ErrorItem` values to be included into /// q `ParseError` (when it's a `TrivialError`) as 'expected' message items /// when a parser fails without consuming input right after a successful parser /// that produced the hints. /// /// Errors /// Token type public record Hints(Set> Errors) : Monoid> { public Hints Combine(Hints rhs) => new (Errors + rhs.Errors); public static Hints Empty { get; } = new (Set>.Empty); /// /// Replace the hints with the given `ErrorItem` (or delete it if 'Nothing' is given). /// This is used in the `label` primitive. /// /// Error item /// Refreshed hints public Hints Refresh(Option> errorItem) => errorItem.IsSome ? Errors.IsEmpty ? Empty : new Hints(Set.singleton(errorItem.ValueUnsafe()!)) : Empty; } ================================================ FILE: LanguageExt.Megaparsec/LanguageExt.Megaparsec.csproj ================================================  net10.0 enable enable ================================================ FILE: LanguageExt.Megaparsec/LineText.cs ================================================ namespace LanguageExt.Megaparsec; /// /// Lazy text structure /// /// Lazy text function public class LineText(Func lazyText) { int isAvailable; string text = ""; /// /// Construct a new LineText /// /// Lazy text function /// LineText public static LineText Lift(Func lazyText) => new (lazyText); /// /// Read text /// public string Text => GetText(); string GetText() { if(isAvailable == 2) return text; SpinWait sw = default; while (true) { if(isAvailable == 2) return text; if (Interlocked.CompareExchange(ref isAvailable, 1, 0) == 0) { try { text = lazyText(); isAvailable = 2; return text; } catch { isAvailable = 0; throw; } } else { sw.SpinOnce(); } } } } ================================================ FILE: LanguageExt.Megaparsec/ModuleT/Expr.cs ================================================ using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt.Megaparsec; public static partial class ModuleT where MP : MonadParsecT where S : TokenStream where M : Monad { } ================================================ FILE: LanguageExt.Megaparsec/ModuleT/Failure.cs ================================================ using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt.Megaparsec; public static partial class ModuleT where MP : MonadParsecT where S : TokenStream where M : Monad { /// /// Stop parsing and report the `ParseError`. /// /// /// This is the only way to control the position of the error without /// manipulating the parser state manually. /// /// Value type to parse /// Error /// Parser public static K error(ParseError error) => MP.Error(error); /// /// Stop parsing and report a trivial `ParseError`. /// /// Optional unexpected tokens /// Expected tokens /// Value type (never yielded because this is designed to error) /// Parser public static K failure(Option> unexpected, Set> expected) => getOffset >> (o => error(ParseError.Trivial(o, unexpected, expected))); /// /// Stop parsing and report a fancy 'ParseError'. To report a single custom parse error /// /// Optional unexpected tokens /// Value type (never yielded because this is designed to error) /// Parser public static K failure(Set> errors) => getOffset >> (o => error(ParseError.Fancy(o, errors))); /// /// Stop parsing and report a fancy 'ParseError'. To report a single custom parse error /// /// Custom error /// Value type (never yielded because this is designed to error) /// Parser public static K failure(E error) => Pure(error) >> ErrorFancy.Custom >> Set.singleton >> (failure) >> lower; /// /// The parser `unexpected(item)` fails with an error message telling /// about an unexpected `item` without consuming any input. /// /// The unexpected item /// Value type (never yielded because this is designed to error) /// Parser public static K unexpected(ErrorItem item) => failure(Some(item), default); /// /// `observing(p)` allows us to 'observe' failure of the `p` parser, /// should it happen, without actually ending parsing but instead getting /// the `ParseError` in `Left`. On success, the parsed value is returned in /// `Right`, as usual. Note, this primitive just allows you to observe /// parse errors as they happen, it does not backtrack or change how the /// `p` parser works in any way. /// /// Parser value type /// Parser /// Parser public static K, A>> observing(K p) => MP.Observing(p); /// /// Specify how to process a `ParseError` that happens inside the specified /// region. This applies to both normal and delayed `ParseError` values. /// /// As a side effect of the implementation, the inner computation will start /// with an empty collection of delayed errors; they will be updated and /// “restored” on the way out of 'region'. /// /// Error mapping for any raised error in the region /// Region to process /// Parser value type /// Parser public static K region(Func, ParseError> mapError, K region) => from de in MP.Gets(s => s.ParseErrors) >> MP.Modify(s => s with { ParseErrors = [] }) from r1 in MP.Observing(region) >> MP.Modify(s => s with { ParseErrors = s.ParseErrors.Map(mapError) + de }) from r2 in r1 switch { Either, A>.Left (var err) => MP.Error(mapError(err)), Either, A>.Right (var x) => MP.Pure(x), _ => throw new NotSupportedException() } select r2; /// /// `withRecovery(f, p)` allows us to continue parsing even if the parser /// `p` fails. In this case `f` is called with the `ParseError` as its /// argument. Typical usage is to return a value signifying failure to /// parse this particular object and to consume some part of the input up /// to the point where the next object starts. /// /// Note that if `f` fails, the original error message is reported as if /// without `withRecovery`. In no way can the recovering parser `f` influence /// error messages. /// /// Delegate to invoke on error /// Parser to run /// Value type to parse /// Parser public static K withRecovery(Func, K> onError, K p) => MP.WithRecovery(onError, p); } ================================================ FILE: LanguageExt.Megaparsec/ModuleT/Lexer.cs ================================================ using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt.Megaparsec; public static partial class ModuleT where MP : MonadParsecT where S : TokenStream where M : Monad { // -- | @'space' sc lineComment blockComment@ produces a parser that can parse // -- white space in general. It's expected that you create such a parser once // -- and pass it to other functions in this module as needed (when you see // -- @spaceConsumer@ in documentation, usually it means that something like // -- 'space' is expected there). // -- // -- @sc@ is used to parse blocks of space characters. You can use // -- 'Text.Megaparsec.Char.space1' from "Text.Megaparsec.Char" for this // -- purpose as well as your own parser (if you don't want to automatically // -- consume newlines, for example). Make sure that the parser does not // -- succeed on the empty input though. In an earlier version of the library // -- 'Text.Megaparsec.Char.spaceChar' was recommended, but now parsers based // -- on 'takeWhile1P' are preferred because of their speed. // -- // -- @lineComment@ is used to parse line comments. You can use // -- @skipLineComment@ if you don't need anything special. // -- // -- @blockComment@ is used to parse block (multi-line) comments. You can use // -- @skipBlockComment@ or @skipBlockCommentNested@ if you don't need anything // -- special. // -- // -- If you don't want to allow a kind of comment, simply pass 'empty' which // -- will fail instantly when parsing of that sort of comment is attempted and // -- 'space' will just move on or finish depending on whether there is more // -- white space for it to consume. // space :: // (MonadParsec e s m) => // -- | A parser for space characters which does not accept empty // -- input (e.g. 'Text.Megaparsec.Char.space1') // m () -> // -- | A parser for a line comment (e.g. 'skipLineComment') // m () -> // -- | A parser for a block comment (e.g. 'skipBlockComment') // m () -> // m () // space sp line block = // skipMany $ // choice // [hidden sp, hidden line, hidden block] } ================================================ FILE: LanguageExt.Megaparsec/ModuleT/Prim.cs ================================================ using System.Diagnostics.Contracts; using LanguageExt.ClassInstances; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt.Megaparsec; public static partial class ModuleT where MP : MonadParsecT where S : TokenStream where M : Monad { /// /// Match a single token /// /// /// semicolon = single(';') /// /// Token to match /// Parser [Pure] public static K single(T token) => single>(token); /// /// Match a single token /// /// /// semicolon = single(';') /// /// Equality trait /// Token to match /// Parser [Pure] public static K single(T token) where EqT : Eq => token(x => EqT.Equals(x, token) ? Some(x) : None, Set.singleton(ErrorItem.Token(token))); /// /// The parser `satisfy(f)` succeeds for any token for which the supplied /// function `f` returns `True`. /// /// /// **Performance note**: when you need to parse a single token, it is often /// a good idea to use `satisfy` with the right predicate function instead of /// creating a complex parser using the combinators. /// /// /// See also: `anySingle`, `anySingleBut`, `oneOf`, `noneOf`. /// /// Predicate function /// Parser [Pure] public static K satisfy(Func f) => token(x => f(x) ? Some(x) : None, default); /// /// Parse and return a single token. It's a good idea to attach a 'label' /// to this parser. /// public static readonly K anySingle = satisfy(_ => true); /// /// Match any token but the given one. It's a good idea to attach a `label` /// to this parser. /// /// Token to avoid /// Parser [Pure] public static K anySingleBut(T token) => anySingleBut>(token); /// /// Match any token but the given one. It's a good idea to attach a `label` /// to this parser. /// /// Equality trait /// Token to avoid /// Parser [Pure] public static K anySingleBut(T token) where EqT : Eq => satisfy(x => !EqT.Equals(x, token)); /// /// `chunk(chk)` only matches the chunk `chk`. /// /// Parser [Pure] public static K chunk(S chk) => tokens(static (x, y) => x.Equals(y), chk); /// /// The parser `label(name, p)` behaves as parser `p`, but whenever the /// parser `p` fails _without consuming any input_, it replaces names of /// 'expected' tokens with the name `name`. /// /// Value type to parse /// Label name /// Parser to label /// Parser [Pure] public static K label(string name, K p) => MP.Label(name, p); /// /// `hidden(p)` behaves just like parser `p`, but it doesn't show any /// 'expected' tokens in the error-message when `p` fails. /// /// Value type to parse /// Parser to hide /// Parser [Pure] public static K hidden(K p) => MP.Hidden(p); /// /// The parser `Try(p)` behaves like the parser `p`, except that it /// backtracks the parser state when `p` fails (either consuming input or /// not). /// /// /// This combinator is used whenever arbitrary look-ahead is needed. Since /// it pretends that it hasn't consumed any input when `p` fails, the /// (`|`) combinator will try its second alternative even if the first /// parser failed while consuming input. /// /// /// For example, here is a parser that is supposed to parse the word “let” /// or the word “lexical”: /// /// parseTest((string("let") | string("lexical")), "lexical") /// /// unexpected "lex" /// expecting "let" /// /// What happens here? The first parser consumes “le” and fails (because it /// doesn't see a “t”). The second parser, however, isn't tried, since the /// first parser has already consumed some input! `Try` fixes this behavior /// and allows backtracking to work: /// /// parseTest((try (string("let")) | string("lexical")), "lexical") /// "lexical" /// /// `Try` also improves error messages in case of overlapping alternatives, /// because Megaparsec's hint system can be used: /// /// parseTest((try (string("let")) | string("lexical")), "le") /// /// unexpected "le" /// expecting "let" or "lexical" /// /// **Note** that the combinator: `string` backtracks automatically (see `tokens`), so it /// does not need `Try`. However, the examples above demonstrate the idea behind 'Try' so well /// that it was decided to keep them. You still need to use 'Try' when your /// alternatives are complex, composite parsers. /// Value type to parse /// Parser to try /// Parser [Pure] public static K @try(K p) => MP.Try(p); /// /// If `p` in `lookAhead(p)` succeeds (either by consuming input or not), /// the whole parser behaves like `p` succeeded without consuming anything /// (parser state is also not updated). If `p` fails, `lookAhead` has no /// effect, i.e. it will fail consuming input if `p` fails consuming input. /// Combine with `try` if this is undesirable /// /// Value type to parse /// Parser to look ahead with /// Parser [Pure] public static K lookAhead(K p) => MP.LookAhead(p); /// /// `notFollowedBy(p)` only succeeds when the parser `p` fails. This parser /// /never consumes/ any input and /never modifies/ parser state. It can be /// used to implement the “longest match” rule. /// /// Value type to parse /// Parser to test /// Parser [Pure] public static K notFollowedBy(K p) => MP.NotFollowedBy(p); /// /// This parser only succeeds at the end of input /// /// Parser public static readonly K eof = MP.EOF; /// /// An escape hatch for defining custom 'MonadParsec' primitives /// /// Parser value type /// Parsing function to lift /// Parser [Pure] public static K lift(Func, Reply> f) => MP.Lift(f); /// /// `oneOf(cases)` succeeds if the current token is in the collection of token /// `cases`. Returns the parsed token. Note, this parser cannot automatically /// generate the “expected” component of the error-message, so usually you should /// label it manually with `label` or `|`. /// /// Foldable trait /// Token cases to test /// Parser [Pure] public static K oneOf(K cases) where F : Foldable => oneOf>(cases); /// /// `oneOf(cases)` succeeds if the current token is in the collection of token /// `cases`. Returns the parsed token. Note, this parser cannot automatically /// generate the “expected” component of the error-message, so usually you should /// label it manually with `label` or `|`. /// /// Foldable trait /// Equality trait /// Token cases to test /// Parser [Pure] public static K oneOf(K cases) where F : Foldable where EqT : Eq => satisfy(x => Foldable.contains(x, cases)); /// /// `oneOf(cases)` succeeds if the current token is in the collection of token /// `cases`. Returns the parsed token. Note, this parser cannot automatically /// generate the “expected” component of the error-message, so usually you should /// label it manually with `label` or `|`. /// /// Token cases to test /// Parser [Pure] public static K oneOf(ReadOnlySpan cases) => MP.OneOf>(S.TokensToChunk(cases)); /// /// `oneOf(cases)` succeeds if the current token is in the collection of token /// `cases`. Returns the parsed token. Note, this parser cannot automatically /// generate the “expected” component of the error-message, so usually you should /// label it manually with `label` or `|`. /// /// Equality trait /// Token cases to test /// Parser [Pure] public static K oneOf(ReadOnlySpan cases) where EqT : Eq => MP.OneOf(S.TokensToChunk(cases)); /// /// `oneOf(cases)` succeeds if the current token is in the collection of token /// `cases`. Returns the parsed token. Note, this parser cannot automatically /// generate the “expected” component of the error-message, so usually you should /// label it manually with `label` or `|`. /// /// Token cases to test /// Parser [Pure] public static K oneOf(S cases) => MP.OneOf>(cases); /// /// `oneOf(cases)` succeeds if the current token is in the collection of token /// `cases`. Returns the parsed token. Note, this parser cannot automatically /// generate the “expected” component of the error-message, so usually you should /// label it manually with `label` or `|`. /// /// Equality trait /// Token cases to test /// Parser [Pure] public static K oneOf(S cases) where EqT : Eq => MP.OneOf(cases); /// /// As the dual of `oneOf`, `noneOf` succeeds if the current token is not in the /// supplied list of token `cases`. Returns the parsed character. Note that this /// parser cannot automatically generate the “expected” component of the /// error-message, so usually you should label it manually with. /// `label` or `|` /// /// Token cases to test /// Foldable trait /// Parser [Pure] public static K noneOf(K cases) where F : Foldable => satisfy(x => !Foldable.contains(x, cases)); /// /// As the dual of `oneOf`, `noneOf` succeeds if the current token is not in the /// supplied list of token `cases`. Returns the parsed character. Note that this /// parser cannot automatically generate the “expected” component of the /// error-message, so usually you should label it manually with. /// `label` or `|` /// /// Token cases to test /// Equality trait /// Foldable trait /// Parser [Pure] public static K noneOf(K cases) where F : Foldable where EqT : Eq => satisfy(x => !Foldable.contains(x, cases)); /// /// As the dual of `oneOf`, `noneOf` succeeds if the current token is not in the /// supplied list of token `cases`. Returns the parsed character. Note that this /// parser cannot automatically generate the “expected” component of the /// error-message, so usually you should label it manually with. /// `label` or `|` /// /// Token cases to test /// Parser [Pure] public static K noneOf(ReadOnlySpan cases) => MP.NoneOf>(S.TokensToChunk(cases)); /// /// As the dual of `oneOf`, `noneOf` succeeds if the current token is not in the /// supplied list of token `cases`. Returns the parsed character. Note that this /// parser cannot automatically generate the “expected” component of the /// error-message, so usually you should label it manually with. /// `label` or `|` /// /// Token cases to test /// Equality trait /// Parser [Pure] public static K noneOf(ReadOnlySpan cases) where EqT : Eq => MP.NoneOf(S.TokensToChunk(cases)); /// /// As the dual of `oneOf`, `noneOf` succeeds if the current token is not in the /// supplied list of token `cases`. Returns the parsed character. Note that this /// parser cannot automatically generate the “expected” component of the /// error-message, so usually you should label it manually with. /// `label` or `|` /// /// Token cases to test /// Parser [Pure] public static K noneOf(S cases) => MP.NoneOf>(cases); /// /// As the dual of `oneOf`, `noneOf` succeeds if the current token is not in the /// supplied list of token `cases`. Returns the parsed character. Note that this /// parser cannot automatically generate the “expected” component of the /// error-message, so usually you should label it manually with. /// `label` or `|` /// /// Token cases to test /// Equality trait /// Parser [Pure] public static K noneOf(S cases) where EqT : Eq => MP.NoneOf(cases); /// /// The `choice(ps)` parser tries to apply the parsers in the list `ps` in order, /// until one of them succeeds. Returns the value of the succeeding parser. /// /// Parsers to try /// Type of value to parse /// Succeeding parser or MP.Empty on fail - use the `|` operator to capture failure [Pure] public static K choice(params ReadOnlySpan> ps) => Alternative.choice(ps); /// /// One or more... /// /// /// Run the applicative functor repeatedly, collecting the results, until failure. /// /// Will succeed if at least one item has been yielded. /// /// Parser /// One or more values [Pure] public static K> some(K p) => Alternative.some(p); /// /// Zero or more... /// /// /// Run the applicative parser repeatedly, collecting the results, until failure. /// Will always succeed. /// /// Parser /// Zero or more values [Pure] public static K> many(K p) => Alternative.many(p); /// /// Skip zero or more... /// /// /// Run the parser repeatedly until failure. /// Will always succeed. /// /// Parser /// Unit [Pure] public static K skipMany(K p) => Alternative.skipMany(p); /// /// `endBy(p, sep)` parses zero-or-more occurrences of `p`, separated and ended by /// `sep`. Returns a list of values returned by `p`. /// /// Value parser /// Separator parser /// Value type /// Separator type /// [Pure] public static K> endBy(K p, K sep) => Alternative.endBy(p, sep); /// /// `endBy1(p, sep)` parses one-or-more occurrences of `p`, separated and ended by /// `sep`. Returns a list of values returned by `p`. /// /// Value parser /// Separator parser /// Value type /// Separator type /// [Pure] public static K> endBy1(K p, K sep) => Alternative.endBy1(p, sep); /// /// Combine two alternatives /// /// Left parser /// Right parser /// Left value type /// Right value type /// Parser structure with an `Either` lifted into it [Pure] public static K> either(K ma, K mb) => Alternative.either(ma, mb); /// /// `manyUntil(p, end)` applies `p` _zero_ or more times until `end` succeeds. /// Returns the list of values returned by`p`. `end` result is consumed and /// lost. Use `manyUntil2` if you wish to keep it. /// /// Parser to consume /// Terminating parser /// Value type /// End value type /// Parser [Pure] public static K> manyUntil(K p, K end) => Alternative.manyUntil(p, end); /// /// `manyUntil2(p, end)` applies `p` _zero_ or more times until `end` succeeds. /// Returns the list of values returned by `p` plus the `end` result. /// /// Use `manyUntil` if you don't wish to keep the `end` result. /// /// Parser to consume /// Terminating structure /// Value type /// End value type /// Parser [Pure] public static K Items, END End)> manyUntil2(K p, K end) => Alternative.manyUntil2(p, end); /// /// `someUntil(p, end)` applies `p` _one_ or more times until `end` succeeds. /// Returns the list of values returned by `p`. `end` result is consumed and /// lost. Use `someUntil2` if you wish to keep it. /// /// Structure to consume /// Terminating structure /// Value type /// End value type /// Parser [Pure] public static K> someUntil(K p, K end) => Alternative.someUntil(p, end); /// /// `someUntil2(p, end)` applies `p` _one_ or more times until `end` succeeds. /// Returns the list of values returned by `p` plus the `end` result. /// /// Use `someUntil` if you don't wish to keep the `end` result. /// /// Structure to consume /// Terminating structure /// Value type /// End value type /// Parser [Pure] public static K Items, END End)> someUntil2(K p, K end) => Alternative.someUntil2(p, end); /// /// `option(x, p)` tries to apply `p`. If `p` fails without 'consuming' anything, it /// returns `value`, otherwise the value returned by `p`. /// /// Default value to use if `o` fails without 'consuming' anything /// Parser /// /// Parser [Pure] public static K option(A value, K p) => Alternative.option(value, p); /// /// `sepBy(p, sep) processes _zero_ or more occurrences of `p`, separated by `sep`. /// /// Structure to yield return values /// Separator structure /// Value type /// Separator type /// Parser [Pure] public static K> sepBy(K p, K sep) => Alternative.sepBy(p, sep); /// /// `sepBy(p, sep) processes _one_ or more occurrences of `p`, separated by `sep`. /// /// Structure to yield return values /// Separator structure /// Value type /// Separator type /// Parser [Pure] public static K> sepBy1(K p, K sep) => Alternative.sepBy1(p, sep); /// /// `sepEndBy(p, sep) processes _zero_ or more occurrences of `p`, separated /// and optionally ended by `sep`. Returns a list of values returned by `p`. /// /// Structure to yield return values /// Separator structure /// Value type /// Separator type /// Parser [Pure] public static K> sepByEnd(K p, K sep) => Alternative.sepByEnd(p, sep); /// /// `sepEndBy1(p, sep) processes _one_ or more occurrences of `p`, separated /// and optionally ended by `sep`. Returns a list of values returned by `p`. /// /// Structure to yield return values /// Separator structure /// Value type /// Separator type /// Parser [Pure] public static K> sepByEnd1(K p, K sep) => Alternative.sepByEnd1(p, sep); /// /// Process `p` _one_ or more times and drop all yielded values. /// /// /// Run the applicative functor repeatedly until failure. At least one item must be yielded for overall success. /// /// Parser /// Parser [Pure] public static K skipSome(K p) => Alternative.skipSome(p); /// /// `skip(n, p)` processes `n` occurrences of `p`, skipping its result. /// If `n` is not positive, the process equates to `Pure(unit)`. /// /// Number of occurrences of `fa` to skip /// Parser /// Value type /// Parser [Pure] public static K skip(int n, K p) => Alternative.skip(n, p); /// /// `skipManyUntil(p, end)` applies the process `p` _zero_ or more times /// skipping results until process `end` succeeds. The resulting value from /// `end` is then returned. /// /// Value type /// End value type /// Parser [Pure] public static K skipManyUntil(K p, K end) => Alternative.skipManyUntil(p, end); /// /// `skipManyUntil(p, end)` applies the process `p` _one_ or more times /// skipping results until process `end` succeeds. The resulting value from /// `end` is then returned. /// /// Value type /// End value type /// [Pure] public static K skipSomeUntil(K p, K end) => Alternative.skipSomeUntil(p, end); } ================================================ FILE: LanguageExt.Megaparsec/ModuleT/State.cs ================================================ using LanguageExt.Traits; namespace LanguageExt.Megaparsec; public static partial class ModuleT where MP : MonadParsecT where S : TokenStream where M : Monad { //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // State parser combinators // /// /// Return the full parser state as a `State` record /// /// Parser public static readonly K> getParserState = MP.Ask; /// /// Write the full parser state /// /// Parser public static K setParserState(State s) => MP.Put(s); /// /// Return the full parser state and then map it to a new value using the supplied function /// /// Parser public static K mapParserState(Func, A> f) => MP.Asks(f); /// /// Update the parser state using the supplied function /// /// Update function /// Parser public static K modifyParserState(Func, State> f) => MP.Modify(f); /// /// Return the current input /// public static readonly K getInput = MP.Asks(s => s.Input); /// /// `setInput(input)` continues parsing with `input`. /// /// Input to continue with /// Parser public static K setInput(S input) => MP.Modify(current => current with { Input = input }); /// /// Get the number of tokens processed so far. /// /// Parser public static readonly K getOffset = MP.Asks(x => x.Offset); /// /// Set the number of tokens processed so far /// /// Token offset /// Parser public static K setOffset(int offset) => MP.Modify(s => s with { Offset = offset }); /// /// Return the current source position. This function /is not cheap/, do /// not call it e.g. on matching of every token, that's a bad idea. Still you /// can use it to get 'SourcePos' to attach to things that you parse. /// /// The function works under the assumption that we move in the input stream /// only forwards and never backwards, which is always true unless the user /// abuses the library. /// /// /// This isn't a high-performance function, use infrequently and only when you /// need to get the position of the current token. /// /// Parser public static readonly K getSourcePos = from st in MP.Get let pst = Reach.offsetNoLine(st.Offset, st.PosState) from _ in MP.Put(st with { PosState = pst }) select pst.SourcePos; } ================================================ FILE: LanguageExt.Megaparsec/ModuleT/Text.cs ================================================ using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt.Megaparsec; public static partial class ModuleT where MP : MonadParsecT where S : TokenStream where M : Monad { /// /// Parse a string /// public static K @string(string xs) => ModuleT.chunk(S.TokensToChunk(xs.AsSpan())); /// /// Parse the specified character /// /// Character to parse /// Parser public static K ch(char c) => MP.Token(ch => ch == c ? Some(c) : None, Set.singleton(ErrorItem.Label($"{c}"))); /// /// Parse a newline character. /// public static readonly K newline = MP.Token(static ch => ch == '\n' ? Some('\n') : None, ExpectedErrors.newline); /// /// Parse a newline character. /// public static readonly K tab = MP.Token(static ch => ch == '\t' ? Some('\t') : None, ExpectedErrors.tab); /// /// Parse a newline character. /// public static readonly K crlf = ModuleT.chunk(S.TokensToChunk(['\r', '\n'])); /// /// End of line parser. /// /// /// Tries a newline parser first, then tries a carriage-return followed by a newline parser. /// public static readonly K eol = charToStream * newline | crlf | Prelude.label("end of line"); /// /// Parse an uppercase letter /// public static readonly K upperChar = MP.Token(static ch => char.IsUpper(ch) ? Some(ch) : None, ExpectedErrors.upperChar); /// /// Parse a lowercase letter /// public static readonly K lowerChar = MP.Token(static ch => char.IsLower(ch) ? Some(ch) : None, ExpectedErrors.lowerChar); /// /// Parse a letter /// public static readonly K letterChar = MP.Token(static ch => char.IsLetter(ch) ? Some(ch) : None, ExpectedErrors.letterChar); /// /// Parse an alphanumeric character (letter or digit) /// public static readonly K aplhaNumChar = MP.Token(static ch => char.IsLetterOrDigit(ch) ? Some(ch) : None, ExpectedErrors.alphaNumChar); /// /// Parse a digit /// public static readonly K digitChar = MP.Token(static ch => char.IsDigit(ch) ? Some(ch) : None, ExpectedErrors.digitChar); /// /// Parse a binary digit /// public static readonly K binaryDigitChar = MP.Token(static ch => ch is '0' or '1' ? Some(ch) : None, ExpectedErrors.binaryDigitChar); /// /// Parse a hexadecimal digit /// public static readonly K hexDigitChar = MP.Token(static ch => char.IsAsciiHexDigit(ch) ? Some(ch) : None, ExpectedErrors.hexDigitChar); /// /// Parse a number character /// public static readonly K numberChar = MP.Token(static ch => char.IsNumber(ch) ? Some(ch) : None, ExpectedErrors.numberChar); /// /// Parse a symbol character /// public static readonly K symbolChar = MP.Token(static ch => char.IsSymbol(ch) ? Some(ch) : None, ExpectedErrors.symbolChar); /// /// Parse a punctuation character /// public static readonly K punctuationChar = MP.Token(static ch => char.IsPunctuation(ch) ? Some(ch) : None, ExpectedErrors.punctuationChar); /// /// Parse a white-space character. /// public static readonly K spaceChar = MP.Token(static ch => char.IsWhiteSpace(ch) ? Some(ch) : None, ExpectedErrors.whiteSpaceChar); /// /// Parse zero or more white-space characters. /// public static readonly K space = (static _ => unit) * MP.TakeWhile(char.IsWhiteSpace, ExpectedErrors.whiteSpace); /// /// Parse one or more white-space characters. /// public static readonly K space1 = (static _ => unit) * MP.TakeWhile1(char.IsWhiteSpace, ExpectedErrors.whiteSpace); /// /// Parse zero or more white-space characters (ignoring newlines and carriage-returns). /// public static readonly K hspace = (static _ => unit) * MP.TakeWhile(static ch => ch != '\n' && ch != '\r' && char.IsWhiteSpace(ch), ExpectedErrors.whiteSpace); /// /// Parse zero or more white-space characters (ignoring newlines and carriage-returns). /// public static readonly K hspace1 = (static _ => unit) * MP.TakeWhile1(static ch => ch != '\n' && ch != '\r' && char.IsWhiteSpace(ch), ExpectedErrors.whiteSpace); /// /// Parse a control character /// public static readonly K controlChar = MP.Token(static ch => char.IsControl(ch) ? Some(ch) : None, ExpectedErrors.control); /// /// Parse a separator character /// public static readonly K separatorChar = MP.Token(static ch => char.IsSeparator(ch) ? Some(ch) : None, ExpectedErrors.separator); /// /// Make a chunk `S` from a single token `char` /// public static S charToStream(char ch) => S.TokenToChunk(ch); static K Test => ModuleT.oneOf(['1', '2', '3']) | label("hello"); } ================================================ FILE: LanguageExt.Megaparsec/ModuleT/Token.cs ================================================ using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt.Megaparsec; public static partial class ModuleT where MP : MonadParsecT where S : TokenStream where M : Monad { /// /// The parser `token(test, expected)` accepts tokens for which the /// matching function `test` returns `Some` result. If `None` is /// returned, the `expected` set is used to report the items that were /// expected. /// /// Value type to parse /// Token predicate test function /// Expected items /// Token parser public static K token(Func> test, in Set> expected) => MP.Token(test, expected); /// /// The parser `tokens(test, chunk)` parses a chunk of input and returns it. /// The supplied predicate `test` is used to check equality of given and parsed /// chunks after a candidate chunk of correct length is fetched from the stream. /// /// This can be used, for example, to write 'chunk': /// /// chunk = tokens (==) /// /// Note that this is an auto-backtracking primitive, which means that if it /// fails, it never consumes any input. This is done to make its consumption /// model match how error messages for this primitive are reported (which /// becomes an important thing as user gets more control with primitives like /// 'withRecovery'): /// /// parseTest((string("abc")), "abd") /// /// unexpected "abd" /// expecting "abc" /// /// This means that it's not necessary to use `@try` with `tokens`-based parsers, /// such as `string` and. /// /// Predicate test function. The first argument is the chunk to /// test, the second argument is the reference chunk. /// Reference chunk /// Parsed chunk public static K tokens(Func test, S chunk) => MP.Tokens(test, chunk); /// /// Parse zero or more tokens for which the supplied predicate holds. /// Try to use this as much as possible because, for many streams, this /// combinator is much faster than parsers built with `many` and `satisfy`. /// /// takeWhile((Some "foo"), f) = many (satisfy(f) | "foo") /// takeWhile(None, f) = many (satisfy(f)) /// /// The combinator never fails, although it may parse the empty chunk. /// /// Predicate to use to test tokens /// Name for a single token in the row /// A chunk of matching tokens public static K takeWhile(Func test, Option name = default) => MP.TakeWhile(test, name); /// /// Parse one or more tokens for which the supplied predicate holds. /// Try to use this as much as possible because, for many streams, this /// combinator is much faster than parsers built with `many` and `satisfy`. /// /// takeWhile((Some "foo"), f) = many (satisfy(f) | "foo") /// takeWhile(None, f) = many (satisfy(f)) /// /// The combinator never fails, although it may parse the empty chunk. /// /// Predicate to use to test tokens /// Name for a single token in the row /// A chunk of matching tokens public static K takeWhile1(Func test, Option name = default) => MP.TakeWhile1(test, name); /// /// Extract the specified number of tokens from the input stream and /// return them packed as a chunk of stream. If there are not enough tokens /// in the stream, a parse error will be signalled. It's guaranteed that if /// the parser succeeds, the requested number of tokens will be returned. /// /// The parser is roughly equivalent to: /// /// take((Just "foo"), n) = count(n, (anySingle | "foo")) /// take(Nothing, n) = count(n, anySingle) /// /// Note that if the combinator fails due to an insufficient number of tokens /// in the input stream, it backtracks automatically. No `@try` is necessary /// with `take`. /// /// How many tokens to extract /// Name for a single token in the row /// A chunk of matching tokens public static K take(int n, Option name = default) => MP.Take(n, name); } ================================================ FILE: LanguageExt.Megaparsec/MonadParsecT/MonadParsecT.Module.cs ================================================ using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt.Megaparsec; /// /// MonadParsec module /// public static partial class Module where MP : MonadParsecT where S : TokenStream where M : Monad { /// /// Stop parsing and report the `ParseError`. This is the only way to /// control the position of the error without manipulating the parser state /// manually. /// /// Error /// Value type to parse /// Parser public static K error(ParseError error) => MP.Error(error); /// /// The parser `Label(name, p)` behaves as parser `p`, but whenever the /// parser `p` fails /without consuming any input/, it replaces names of /// “expected” tokens with the name `name`. /// /// Label name /// Parser to label /// Value type to parse /// Parser public static K label(string name, K p) => MP.Label(name, p); /// /// `hidden(p)` behaves just like parser `p`, but it doesn't show any /// “expected” tokens in the error-message when `p` fails. /// /// Parser to hide /// Value type to parse /// Parser public static K hidden(K p) => MP.Hidden(p); /// /// The parser `Try(p)` behaves like the parser `p`, except that it /// backtracks the parser state when `p` fails (either consuming input or /// not). /// /// /// This combinator is used whenever arbitrary look-ahead is needed. Since /// it pretends that it hasn't consumed any input when `p` fails, the /// (`|`) combinator will try its second alternative even if the first /// parser failed while consuming input. /// /// /// For example, here is a parser that is supposed to parse the word “let” /// or the word “lexical”: /// /// parseTest((string("let") | string("lexical")), "lexical") /// /// unexpected "lex" /// expecting "let" /// /// What happens here? The first parser consumes “le” and fails (because it /// doesn't see a “t”). The second parser, however, isn't tried, since the /// first parser has already consumed some input! `Try` fixes this behavior /// and allows backtracking to work: /// /// parseTest((try (string("let")) | string("lexical")), "lexical") /// "lexical" /// /// `Try` also improves error messages in case of overlapping alternatives, /// because Megaparsec's hint system can be used: /// /// parseTest((try (string("let")) | string("lexical")), "le") /// /// unexpected "le" /// expecting "let" or "lexical" /// /// **Note** that the combinator: `string` backtracks automatically (see `tokens`), so it /// does not need `Try`. However, the examples above demonstrate the idea behind 'Try' so well /// that it was decided to keep them. You still need to use 'Try' when your /// alternatives are complex, composite parsers. /// Parser to try /// Value type to parse /// Parser public static K @try(K p) => MP.Try(p); /// /// If `p` in `lookAhead(p)` succeeds (either by consuming input or not), /// the whole parser behaves like `p` succeeded without consuming anything /// (parser state is also not updated). If `p` fails, `lookAhead` has no /// effect, i.e. it will fail consuming input if `p` fails consuming input. /// Combine with `try` if this is undesirable /// /// Parser to look ahead with /// Value type to parse /// Parser public static K lookAhead(K p) => MP.LookAhead(p); /// /// `notFollowedBy(p)` only succeeds when the parser `p` fails. This parser /// /never consumes/ any input and /never modifies/ parser state. It can be /// used to implement the “longest match” rule. /// /// Parser to test /// Value type to parse /// Parser public static K notFollowedBy(K p) => MP.NotFollowedBy(p); /// /// `withRecovery(f, p)` allows us to continue parsing even if the parser /// `p` fails. In this case `f` is called with the `ParseError` as its /// argument. Typical usage is to return a value signifying failure to /// parse this particular object and to consume some part of the input up /// to the point where the next object starts. /// /// Note that if `f` fails, the original error message is reported as if /// without `withRecovery`. In no way can the recovering parser `f` influence /// error messages. /// /// Delegate to invoke on error /// Parser to run /// Value type to parse /// Parser public static K withRecovery(Func, K> onError, K p) => MP.WithRecovery(onError, p); /// /// This parser only succeeds at the end of input /// /// Parser public static readonly K eof = MP.EOF; /// /// `observing(p)` allows us to “observe” failure of the `p` parser, /// should it happen, without actually ending parsing but instead getting /// the `ParseError` in `Left`. On success, the parsed value is returned in /// `Right` as usual. Note, this primitive just allows you to observe /// parse errors as they happen, it does not backtrack or change how the /// `p` parser works in any way. /// /// Parser value type /// Parser /// Parser public static K, A>> observing(K p) => MP.Observing(p); /// /// The parser `token(test, expected)` accepts tokens for which the /// matching function `test` returns `Some` result. If `None` is /// returned, the `expected` set is used to report the items that were /// expected. /// /// Token predicate test function /// Expected items /// Value type to parse /// Token parser public static K token(Func> test, in Set> expected) => MP.Token(test, expected); /// /// The parser `tokens(test, chunk)` parses a chunk of input and returns it. /// The supplied predicate `test` is used to check equality of given and parsed /// chunks after a candidate chunk of correct length is fetched from the stream. /// /// This can be used, for example, to write 'chunk': /// /// chunk = tokens (==) /// /// Note that this is an auto-backtracking primitive, which means that if it /// fails, it never consumes any input. This is done to make its consumption /// model match how error messages for this primitive are reported (which /// becomes an important thing as user gets more control with primitives like /// 'withRecovery'): /// /// parseTest((string("abc")), "abd") /// /// unexpected "abd" /// expecting "abc" /// /// This means that it's not necessary to use `@try` with `tokens`-based parsers, /// such as `string` and. /// /// Predicate test function. The first argument is the chunk to /// test, the second argument is the reference chunk. /// Reference chunk /// Parsed chunk public static K tokens(Func test, S chunk) => MP.Tokens(test, chunk); /// /// Parse zero or more tokens for which the supplied predicate holds. /// Try to use this as much as possible because, for many streams, this /// combinator is much faster than parsers built with `many` and `satisfy`. /// /// takeWhile((Some "foo"), f) = many (satisfy(f) | "foo") /// takeWhile(None, f) = many (satisfy(f)) /// /// The combinator never fails, although it may parse the empty chunk. /// /// Predicate to use to test tokens /// Name for a single token in the row /// A chunk of matching tokens public static K takeWhile(Func test, Option name = default) => MP.TakeWhile(test, name); /// /// Parse one or more tokens for which the supplied predicate holds. /// Try to use this as much as possible because, for many streams, this /// combinator is much faster than parsers built with `many` and `satisfy`. /// /// takeWhile((Some "foo"), f) = many (satisfy(f) | "foo") /// takeWhile(None, f) = many (satisfy(f)) /// /// The combinator never fails, although it may parse the empty chunk. /// /// Predicate to use to test tokens /// Name for a single token in the row /// A chunk of matching tokens public static K takeWhile1(Func test, Option name = default) => MP.TakeWhile1(test, name); /// /// Extract the specified number of tokens from the input stream and /// return them packed as a chunk of stream. If there are not enough tokens /// in the stream, a parse error will be signalled. It's guaranteed that if /// the parser succeeds, the requested number of tokens will be returned. /// /// The parser is roughly equivalent to: /// /// take((Just "foo"), n) = count(n, (anySingle | "foo")) /// take(Nothing, n) = count(n, anySingle) /// /// Note that if the combinator fails due to an insufficient number of tokens /// in the input stream, it backtracks automatically. No `@try` is necessary /// with `take`. /// /// How many tokens to extract /// Name for a single token in the row /// A chunk of matching tokens public static K take(int n, Option name = default) => MP.Take(n, name); /// /// Return the full parser state as a `State` record /// /// Parser public static readonly K> getParserState = MP.Ask; /// /// Write the full parser state /// /// Parser public static K putParserState(State s) => MP.Put(s); /// /// Return the full parser state and then map it to a new value using the supplied function /// /// Parser public static K mapParserState(Func, A> f) => MP.Asks(f); /// /// Update the parser state using the supplied function /// /// Update function /// Parser public static K modifyParserState(Func, State> f) => MP.Modify(f); /// /// An escape hatch for defining custom 'MonadParsec' primitives /// /// Parser value type /// Parsing function to lift /// Parser public static K lift(Func, Reply> f) => MP.Lift(f); } ================================================ FILE: LanguageExt.Megaparsec/MonadParsecT/MonadParsecT.cs ================================================ using LanguageExt.Traits; namespace LanguageExt.Megaparsec; /// /// Parser combinator monad transformer trait /// /// /// Type class describing monads that implement the full set of primitive parsers. /// /// /// **Note** that the following primitives are “fast” and should be taken /// advantage of as much as possible if your aim is a fast parser: `tokens`, /// `takeWhileP`, `takeWhile1P`, and `takeP` /// /// This type /// Error type /// Token-stream type /// Token type /// Lifted monad public interface MonadParsecT : MonadT, Alternative, Identifiable, Fallible, MP>, Readable>, Stateful> where MP : MonadParsecT where M : Monad where S : TokenStream { /// /// Stop parsing and report the `ParseError`. This is the only way to /// control the position of the error without manipulating the parser state /// manually. /// /// Parser value type /// Error /// public static abstract K Error(ParseError error); /// /// The parser `label(name, p)` behaves as parser `p`, but whenever the /// parser `p` fails /without consuming any input/, it replaces names of /// “expected” tokens with the name `name`. /// /// Label name /// Parser to label /// Parser value type /// Parser public static abstract K Label(string name, K p); /// /// `hidden(p)` behaves just like parser `p`, but it doesn't show any /// “expected” tokens in the error-message when `p` fails. /// /// Parser to hide /// Parser value type /// Parser public static virtual K Hidden(K p) => MP.Label("", p); /// /// The parser `@try(p)` behaves like the parser `p`, except that it /// backtracks the parser state when `p` fails (either consuming input or /// not). /// /// /// This combinator is used whenever arbitrary look-ahead is needed. Since /// it pretends that it hasn't consumed any input when `p` fails, the /// (`|`) combinator will try its second alternative even if the first /// parser failed while consuming input. /// /// /// For example, here is a parser that is supposed to parse the word “let” /// or the word “lexical”: /// /// parseTest((string("let") | string("lexical")), "lexical") /// /// unexpected "lex" /// expecting "let" /// /// What happens here? The first parser consumes “le” and fails (because it /// doesn't see a “t”). The second parser, however, isn't tried, since the /// first parser has already consumed some input! `Try` fixes this behavior /// and allows backtracking to work: /// /// parseTest((@try (string("let")) | string("lexical")), "lexical") /// "lexical" /// /// `Try` also improves error messages in case of overlapping alternatives, /// because Megaparsec's hint system can be used: /// /// parseTest((@try (string("let")) | string("lexical")), "le") /// /// unexpected "le" /// expecting "let" or "lexical" /// /// /// **Note** that the combinator: `string` backtracks automatically (see `tokens`), so it /// does not need `@try`. However, the examples above demonstrate the idea behind `@try` so well /// that it was decided to keep them. You still need to use `@try` when your /// alternatives are complex, composite parsers. /// /// Parser value type /// Parser public static abstract K Try(K p); /// /// If `p` in `lookAhead(p)` succeeds (either by consuming input or not), /// the whole parser behaves like `p` succeeded without consuming anything /// (parser state is also not updated). If `p` fails, `lookAhead` has no /// effect, i.e. it will fail consuming input if `p` fails consuming input. /// Combine with `try` if this is undesirable /// /// Parser to look ahead with /// Parser value type /// Parser public static abstract K LookAhead(K p); /// /// `notFollowedBy(p)` only succeeds when the parser `p` fails. This parser /// /never consumes/ any input and /never modifies/ parser state. It can be /// used to implement the “longest match” rule. /// /// Parser to test /// Parser value type /// Unit parser public static abstract K NotFollowedBy(K p); /// /// `withRecovery(f, p)` allows us to continue parsing even if the parser /// `p` fails. In this case `f` is called with the `ParseError` as its /// argument. Typical usage is to return a value signifying failure to /// parse this particular object and to consume some part of the input up /// to the point where the next object starts. /// /// Note that if `f` fails, the original error message is reported as if /// without `withRecovery`. In no way can the recovering parser `f` influence /// error messages. /// /// Delegate to invoke on error /// Parser to run /// Parser value type /// Parser public static abstract K WithRecovery(Func, K> onError, K p); /// /// This parser only succeeds at the end of input /// public static abstract K EOF { get; } /// /// `observing(p)` allows us to “observe” failure of the `p` parser, /// should it happen, without actually ending parsing but instead getting /// the `ParseError` in `Left`. On success, the parsed value is returned in /// `Right` as usual. Note, this primitive just allows you to observe /// parse errors as they happen, it does not backtrack or change how the /// `p` parser works in any way. /// /// Parser /// Parser value type /// Parser public static abstract K, A>> Observing(K p); /// /// The parser `token(test, expected)` accepts tokens for which the /// matching function `test` returns `Some` result. If `None` is /// returned, the `expected` set is used to report the items that were /// expected. /// /// Token predicate test function /// Expected items /// Parser value type /// Token parser public static abstract K Token(Func> test, in Set> expected); /// /// The parser `tokens(test, chunk)` parses a chunk of input and returns it. /// The supplied predicate `test` is used to check equality of given and parsed /// chunks after a candidate chunk of correct length is fetched from the stream. /// /// This can be used, for example, to write 'chunk': /// /// chunk = tokens (==) /// /// Note that this is an auto-backtracking primitive, which means that if it /// fails, it never consumes any input. This is done to make its consumption /// model match how error messages for this primitive are reported (which /// becomes an important thing as user gets more control with primitives like /// 'withRecovery'): /// /// parseTest((string("abc")), "abd") /// /// unexpected "abd" /// expecting "abc" /// /// This means that it's not necessary to use `@try` with `tokens`-based parsers, /// such as `string` and. /// /// Predicate test function. The first argument is the chunk to /// test, the second argument is the reference chunk. /// Reference chunk /// Parsed chunk public static abstract K Tokens(Func test, in S chunk); /// /// The parser `oneOf(test, expected)` accepts a set of tokens to match. /// /// Tokens to test /// Parsed stream of tokens public static abstract K OneOf(S tokens) where EqT : Eq; /// /// The parser `noneOf(test, expected)` accepts a set of tokens to not match. /// /// Tokens to test /// Parsed stream of tokens public static abstract K NoneOf(S tokens) where EqT : Eq; /// /// Parse zero or more tokens for which the supplied predicate holds. /// Try to use this as much as possible because, for many streams, this /// combinator is much faster than parsers built with `many` and `satisfy`. /// /// takeWhile((Some "foo"), f) = many (satisfy(f) | "foo") /// takeWhile(None, f) = many (satisfy(f)) /// /// The combinator never fails, although it may parse the empty chunk. /// /// Predicate to use to test tokens /// Name for a single token in the row /// A chunk of matching tokens public static abstract K TakeWhile(Func test, in Option name = default); /// /// Parse one or more tokens for which the supplied predicate holds. /// Try to use this as much as possible because, for many streams, this /// combinator is much faster than parsers built with `many` and `satisfy`. /// /// takeWhile((Some "foo"), f) = many (satisfy(f) | "foo") /// takeWhile(None, f) = many (satisfy(f)) /// /// The combinator never fails, although it may parse the empty chunk. /// /// Predicate to use to test tokens /// Name for a single token in the row /// A chunk of matching tokens public static abstract K TakeWhile1(Func test, in Option name = default); /// /// Extract the specified number of tokens from the input stream and /// return them packed as a chunk of stream. If there are not enough tokens /// in the stream, a parse error will be signalled. It's guaranteed that if /// the parser succeeds, the requested number of tokens will be returned. /// /// The parser is roughly equivalent to: /// /// take((Just "foo"), n) = count(n, (anySingle | "foo")) /// take(Nothing, n) = count(n, anySingle) /// /// Note that if the combinator fails due to an insufficient number of tokens /// in the input stream, it backtracks automatically. No `@try` is necessary /// with `take`. /// /// How many tokens to extract /// Name for a single token in the row /// A chunk of matching tokens public static abstract K Take(int n, in Option name = default); /// /// An escape hatch for defining custom 'MonadParsec' primitives /// /// Parsing function to lift /// Parser value type /// Parser public static abstract K Lift(Func, Reply> f); } ================================================ FILE: LanguageExt.Megaparsec/Operator.cs ================================================ using LanguageExt.Traits; namespace LanguageExt.Megaparsec; /// /// This data type specifies operators that work on values of type `A`. An operator /// is either binary infix or unary prefix or postfix. A binary operator has also /// an associated associativity. /// /// Monad trait type /// Value type public abstract record Operator { /// /// Non-associative infix operator /// /// Lifted binary operation public record InfixN(K> Operation) : Operator; /// /// Left-associative infix operator /// /// Lifted binary operation public record InfixL(K> Operation) : Operator; /// /// Right-associative infix operator /// /// Lifted binary operation public record InfixR(K> Operation) : Operator; /// /// Prefix operator /// /// Lifted unary operation public record Prefix(K> Operation) : Operator; /// /// Prefix operator /// /// Lifted unary operation public record Postfix(K> Operation) : Operator; /// /// Right-associative ternary. /// /// /// Right-associative means that... /// /// a /// ? b /// : d /// ? e /// : f /// /// ... is parsed as... /// /// /// a /// ? b /// : (d /// ? e /// : f) /// /// ... and not as... /// /// (a /// ? b /// : d) /// ? e /// : f /// /// /// /// The outer monadic action parses the first separator (e.g. `?`) and /// returns an action (of type: `m (a -> a -> a -> a)`) that parses the /// second separator (e.g. `:`). /// /// public record TernR(K>> Operation) : Operator; } ================================================ FILE: LanguageExt.Megaparsec/PString.cs ================================================ using System.Runtime.CompilerServices; using LanguageExt.Traits; namespace LanguageExt.Megaparsec; /// /// Type that wraps up a string and its start and length. This allows us to implement /// the low-level streaming trait and get a high-performance parser. /// /// Text /// Start of text /// Length of text [CollectionBuilder(typeof(PString), nameof(PString.From))] public readonly struct PString(string value, int start, int length) : TokenStream, IEquatable, IComparable { /// /// Backing string /// string Value => value; /// /// Start position in the backing string /// int Start => start; /// /// Number of characters in the backing string /// int Length => length; /// /// Empty parser string /// public static readonly PString Empty = new("", 0, 0); public PString(string Value) : this(Value, 0, Value.Length) { } public static PString From(string value) => new (value); public char this[int ix] => ix < 0 || ix >= Length ? throw new IndexOutOfRangeException() : Value[Start + ix]; public char this[Index ix] => ix.Value < 0 || ix.Value >= Length ? throw new IndexOutOfRangeException() : ix.IsFromEnd ? Value[Start + Length - ix.Value] : Value[Start + ix.Value]; public PString Splice(int offset, int amount) { if (amount < 0) return Empty; return amount - offset < Length ? new PString(Value, Start + offset, amount) : this; } public PString Splice(int amount) { if (amount < 0) return Empty; return amount < Length ? new PString(Value, Start, amount) : this; } public override string ToString() => new (Value.AsSpan(Start, Length)); public PStringEnum GetEnumerator() => new (this); static bool TokenStream.IsTab(char token) => token == '\t'; static bool TokenStream.IsNewline(char token) => token == '\n'; static ReadOnlySpan TokenStream.TokenToString(char token) => new ([token]); static PString TokenStream.TokenToChunk(in char token) => new (token.ToString(), 0, 1); static PString TokenStream.TokensToChunk(in ReadOnlySpan token) => new (new string(token), 0, token.Length); static ReadOnlySpan TokenStream.ChunkToTokens(in PString tokens) => tokens.Value.AsSpan(tokens.Start, tokens.Length); static int TokenStream.ChunkLength(in PString tokens) => tokens.Length; static bool TokenStream.Take1(in PString stream, out char head, out PString tail) { if (stream.Length > 0) { var start = stream.Start; var value = stream.Value; head = value[start]; tail = new PString(value, start + 1, stream.Length - 1); return true; } else { head = '?'; tail = stream; return false; } } static bool TokenStream.Take(int amount, in PString stream, out PString head, out PString tail) { // If the requested length `amount` is 0 (or less), `false` should // not be returned, instead `true` and `(out Empty, out stream)` should be returned. if (amount <= 0) { head = Empty; tail = stream; return true; } // If the requested length is greater than 0 and the stream is // empty, `false` should be returned indicating end-of-input. if (stream.Length <= 0) { head = Empty; tail = stream; return false; } // In other cases, take chunk of length `amount` (or shorter if the // stream is not long enough) from the input stream and return the // chunk along with the rest of the stream. amount = Math.Min(amount, stream.Length); var start = stream.Start; var value = stream.Value; head = new PString(value, start, amount); tail = new PString(value, start + amount, stream.Length - amount); return true; } static void TokenStream.TakeWhile( Func predicate, in PString stream, out PString head, out PString tail) { var value = stream.Value; var start = stream.Start; var end = start; var length = stream.Length; var count = 0; while (count < length && !predicate(value[end])) { end++; count++; } head = new PString(value, start, count); tail = new PString(value, end, length - count); } public bool Equals(PString other) { if(Length != other.Length) return false; var spanA = Value.AsSpan(Start, Length); var spanB = other.Value.AsSpan(other.Start, other.Length); return spanA.Equals(spanB, StringComparison.Ordinal); } public int CompareTo(PString other) { var spanA = Value.AsSpan(Start, Length); var spanB = other.Value.AsSpan(other.Start, other.Length); return spanA.CompareTo(spanB, StringComparison.Ordinal); } public struct PStringEnum { readonly int start; readonly string target; readonly int end; int current; internal PStringEnum(PString ps) { target = ps.Value; start = current = ps.Start - 1; end = ps.Start + ps.Length; } public bool MoveNext() { current++; return current >= end; } public void Reset() => current = start; public char Current => target[current]; } } ================================================ FILE: LanguageExt.Megaparsec/ParseError/ParseError.Module.cs ================================================ using static LanguageExt.Prelude; namespace LanguageExt.Megaparsec; public static class ParseError { public static ParseError Trivial( int offset, Option> unexpected, Set> expected) => new ParseError.Trivial(offset, unexpected, expected); public static ParseError Trivial( int offset, Option> unexpected, ErrorItem expected) => new ParseError.Trivial(offset, unexpected, Set.singleton(expected)); public static ParseError Trivial( int offset, Option> unexpected, Option> expected) => new ParseError.Trivial(offset, unexpected, expected.IsSome ? Set.singleton((ErrorItem)expected) : default); public static ParseError Trivial( int offset, Option> unexpected) => new ParseError.Trivial(offset, unexpected, default); public static ParseError Fancy(int offset, Set> errors) => new ParseError.Fancy(offset, errors); public static ParseError Fancy(int offset, ErrorFancy errors) => new ParseError.Fancy(offset, Set.singleton(errors)); public static ParseError mergeError(ParseError e1, ParseError e2) { return (e1.Offset.CompareTo(e2.Offset)) switch { < 0 => e2, 0 => (e1, e2) switch { (ParseError.Trivial (var s1, var u1, var p1), ParseError.Trivial (_, var u2, var p2)) => Trivial(s1, n(u1, u2), p1 + p2), (ParseError.Fancy, ParseError.Trivial) => e1, (ParseError.Trivial, ParseError.Fancy) => e2, (ParseError.Fancy (var s1, var x1), ParseError.Fancy(_, var x2) ) => Fancy(s1, x1 + x2), _ => e1 }, > 0 => e1 }; // NOTE The logic behind this merging is that since we only combine // parse errors that happen at exactly the same position, all the // unexpected items will be prefixes of input stream at that position or // labels referring to the same thing. Our aim here is to choose the // longest prefix (merging with labels and end of input is somewhat // arbitrary, but is necessary because otherwise we can't make // ParseError lawful Monoid and have nice parse errors at the same // time). static Option> n(Option> mx, Option> my) => (mx, my) switch { ({ IsNone: true }, { IsNone: true }) => None, ({ IsSome: true }, { IsNone: true }) => mx, ({ IsNone: true }, { IsSome: true }) => my, (_, _) => Some((ErrorItem)mx > (ErrorItem)my ? (ErrorItem)mx : (ErrorItem)my) }; } } ================================================ FILE: LanguageExt.Megaparsec/ParseError/ParseError.cs ================================================ using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt.Megaparsec; /// /// `ParseError` represents a parse error parametrised over the /// stream type `S` and the custom data `E`. /// /// `Semigroup` and `Monoid` instances of the data type allow us to merge /// parse errors from different branches of parsing. When merging two /// 'ParseError's, the longest match is preferred; if positions are the same, /// custom data sets and collections of message items are combined. Note that /// fancy errors take precedence over trivial errors in merging. /// /// Token type /// Error type public abstract record ParseError(int Offset) : Semigroup> { /// /// Trivial errors, generated by the Megaparsec's machinery. The data /// constructor includes the offset of error, unexpected token (if any), /// and expected tokens. /// public record Trivial(int Offset, Option> Unexpected, Set> Expected) : ParseError(Offset) { public override Func, K> WithHints( Hints hs, Func, State, K> f) => curry(f)(ParseError.Trivial(Offset, Unexpected, Expected + hs.Errors)); } /// /// Fancy, custom errors. /// public record Fancy(int Offset, Set> Errors) : ParseError(Offset) { public override Func, K> WithHints( Hints hs, Func, State, K> f) => curry(f)(this); } public abstract Func, K> WithHints( Hints hs, Func, State, K> f); public ParseError Combine(ParseError rhs) => ParseError.mergeError(this, rhs); } ================================================ FILE: LanguageExt.Megaparsec/ParsecT/DSL/Apply.cs ================================================ using LanguageExt.Traits; namespace LanguageExt.Megaparsec.DSL; record ParsecTApply(ParsecT> FF, ParsecT FA) : ParsecT where M : Monad where S : TokenStream { public K Run( State s, ConsumedOK cok, ConsumedErr cerr, EmptyOK eok, EmptyErr eerr) { return FF.Run(s, mcok, cerr, meok, eerr); K mcok(Func f, State s1, Hints hs) => FA.Run(s1, (x2, s2, hs2) => cok(f(x2), s2, hs2), cerr, (x2, s2, hs2) => cok(f(x2), s2, hs + hs2), (e, s2) => e.WithHints(hs, (ex, sx) => cerr(ex, sx))(s2)); K meok(Func f, State s1, Hints hs) => FA.Run(s1, (x2, s2, hs2) => cok(f(x2), s2, hs2), cerr, (x2, s2, hs2) => eok(f(x2), s2, hs + hs2), (e, s2) => e.WithHints(hs, (ex, sx) => eerr(ex, sx))(s2)); } } record ParsecTApplyLazy(ParsecT> FF, Memo, A> FA) : ParsecT where M : Monad where S : TokenStream { public K Run( State s, ConsumedOK cok, ConsumedErr cerr, EmptyOK eok, EmptyErr eerr) { return FF.Run(s, mcok, cerr, meok, eerr); K mcok(Func f, State s1, Hints hs) => FA.Value.As().Run(s1, (x2, s2, hs2) => cok(f(x2), s2, hs2), cerr, (x2, s2, hs2) => cok(f(x2), s2, hs + hs2), (e, s2) => e.WithHints(hs, (ex, sx) => cerr(ex, sx))(s2)); K meok(Func f, State s1, Hints hs) => FA.Value.As().Run(s1, (x2, s2, hs2) => cok(f(x2), s2, hs2), cerr, (x2, s2, hs2) => eok(f(x2), s2, hs + hs2), (e, s2) => e.WithHints(hs, (ex, sx) => eerr(ex, sx))(s2)); } } ================================================ FILE: LanguageExt.Megaparsec/ParsecT/DSL/Bind.cs ================================================ using LanguageExt.Traits; namespace LanguageExt.Megaparsec.DSL; record ParsecTBind(ParsecT FA, Func, B>> F) : ParsecT where M : Monad where S : TokenStream { public K Run( State s, ConsumedOK cok, ConsumedErr cerr, EmptyOK eok, EmptyErr eerr) { return FA.Run(s, mcok, cerr, meok, eerr); K mcok(A x, State s1, Hints hs) => F(x).As().Run(s1, cok, cerr, (x2, s2, hs2) => cok(x2, s2, hs + hs2), (e, s2) => e.WithHints(hs, (ex, sx) => cerr(ex, sx))(s2)); K meok(A x, State s1, Hints hs) => F(x).As().Run(s1, cok, cerr, (x2, s2, hs2) => eok(x2, s2, hs + hs2), (e, s2) => e.WithHints(hs, (ex, sx) => eerr(ex, sx))(s2)); } } ================================================ FILE: LanguageExt.Megaparsec/ParsecT/DSL/Catch.cs ================================================ using LanguageExt.Traits; namespace LanguageExt.Megaparsec.DSL; record ParsecTCatch( ParsecT fa, Func, bool> predicate, Func, K, A>> fail) : ParsecT where M : Monad where S : TokenStream { public K Run( State s, ConsumedOK cok, ConsumedErr cerr, EmptyOK eok, EmptyErr eerr) { return fa.Run(s, cok, cerr, eok, @catch); K @catch(ParseError err, State ms) => predicate(err) ? fail(err).As().Run(ms, cok, cerr, eok, eerr) : eerr(err, ms); } } ================================================ FILE: LanguageExt.Megaparsec/ParsecT/DSL/Choose.cs ================================================ using LanguageExt.Traits; namespace LanguageExt.Megaparsec.DSL; record ParsecTChoose(ParsecT m, ParsecT n) : ParsecT where M : Monad where S : TokenStream { public K Run( State s, ConsumedOK cok, ConsumedErr cerr, EmptyOK eok, EmptyErr eerr) { return m.Run(s, cok, cerr, eok, merr); K merr(ParseError err, State ms) { K ncerr(ParseError err1, State s1) => cerr(err1 + err, longestMatch(ms, s1)); K neok(A x, State s1, Hints hs) => eok(x, s1, Hints.fromOffset(s1.Offset, err) + hs); K neerr(ParseError err1, State s1) => eerr(err1 + err, longestMatch(ms, s1)); return n.Run(ms, cok, ncerr, neok, neerr); } State longestMatch(State s1, State s2) => s1.Offset > s2.Offset ? s1 : s2; } } record ParsecTChooseLazy(ParsecT m, Memo, A> n) : ParsecT where M : Monad where S : TokenStream { public K Run( State s, ConsumedOK cok, ConsumedErr cerr, EmptyOK eok, EmptyErr eerr) { return m.Run(s, cok, cerr, eok, merr); K merr(ParseError err, State ms) { K ncerr(ParseError err1, State s1) => cerr(err1 + err, longestMatch(ms, s1)); K neok(A x, State s1, Hints hs) => eok(x, s1, Hints.fromOffset(s1.Offset, err) + hs); K neerr(ParseError err1, State s1) => eerr(err1 + err, longestMatch(ms, s1)); return n.Value.As().Run(ms, cok, ncerr, neok, neerr); } State longestMatch(State s1, State s2) => s1.Offset > s2.Offset ? s1 : s2; } } ================================================ FILE: LanguageExt.Megaparsec/ParsecT/DSL/DSL.cs ================================================ using System.Runtime.CompilerServices; using LanguageExt.Traits; namespace LanguageExt.Megaparsec.DSL; static class DSL where M : Monad where S : TokenStream { public static ParsecT eof { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => ParsecTEOF.Default; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ParsecT pure(A value) => new ParsecTPure(value); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ParsecT empty() => ParsecTEmpty.Default; [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ParsecT error( ParseError error) => new ParsecTError(error); public static ParsecT> ask { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => ParsecTAsk.Default; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ParsecT asks(Func, A> f) => new ParsecTAsks(f); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ParsecT local( Func, State> f, ParsecT ma) => new ParsecTLocal(f, ma); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ParsecT label( string name, K, A> p) => new ParsecTLabel(name, p.As()); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ParsecT @try( K, A> p) => new ParsecTTry(p.As()); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ParsecT lookAhead( K, A> p) => new ParsecTLookAhead(p.As()); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ParsecT notFollowedBy( K, A> p) => new ParsecTNotFollowedBy(p.As()); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ParsecT take( in Option name, int n) => new ParsecTTake(name, n); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ParsecT withRecovery( Func, K, A>> recovery, K, A> p) => new ParsecTWithRecovery(recovery, p.As()); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ParsecT, A>> observing( K, A> p) => new ParsecTObserving(p.As()); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ParsecT token( Func> test, in Set> expected) => new ParsecTToken(test, expected); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ParsecT tokens( Func test, in S chunk) => new ParsecTTokens(test, chunk); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ParsecT oneOf(S tokens) where EqT : Eq => new ParsecTOneOf(tokens); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ParsecT noneOf(S tokens) where EqT : Eq => new ParsecTNoneOf(tokens); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ParsecT takeWhile( Func test, in Option name = default) => new ParsecTTakeWhile(name, test); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ParsecT takeWhile1( Func test, in Option name = default) => new ParsecTTakeWhile1(name, test); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ParsecT put( State s) => new ParsecTPutState(s); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ParsecT modify( Func, State> f) => new ParsecTModifyState(f); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ParsecT lift( Func, Reply> f) => new ParsecTLift(f); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ParsecT lift( Func, K>> f) => new ParsecTLift2(f); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ParsecT liftM(K ma) => new ParsecTMTransLift(ma); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ParsecT liftIO(IO ma) => new ParsecTMTransLiftIO(ma); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ParsecT map( ParsecT p, Func f) => new ParsecTMap(p, f); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ParsecT apply( K, Func> ff, K, A> fa) => new ParsecTApply(ff.As(), fa.As()); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ParsecT apply( K, Func> ff, Memo, A> fa) => new ParsecTApplyLazy(ff.As(), fa); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ParsecT bind( K, A> ma, Func, B>> f) => new ParsecTBind(ma.As(), f); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ParsecT choose( K, A> m, K, A> n) => new ParsecTChoose(m.As(), n.As()); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ParsecT choose( K, A> m, Memo, A> n) => new ParsecTChooseLazy(m.As(), n); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ParsecT @catch( K, A> fa, Func, bool> predicate, Func, K, A>> fail) => new ParsecTCatch(fa.As(), predicate, fail); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ParsecT fail(string message) => new ParsecTFail1(message); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ParsecT fail(E error) => new ParsecTFail2(error); } ================================================ FILE: LanguageExt.Megaparsec/ParsecT/DSL/EOF.cs ================================================ using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt.Megaparsec.DSL; record ParsecTEOF : ParsecT where M : Monad where S : TokenStream { public static readonly ParsecT Default = new ParsecTEOF(); public K Run( State s, ConsumedOK cok, ConsumedErr cerr, EmptyOK eok, EmptyErr eerr) => S.Take1(s.Input, out var x, out _) ? eerr(ParseError.Trivial(s.Offset, ErrorItem.Token(x), ErrorItem.EndOfInput()), s) : eok(unit, s, Hints.Empty); } ================================================ FILE: LanguageExt.Megaparsec/ParsecT/DSL/Empty.cs ================================================ using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt.Megaparsec.DSL; record ParsecTEmpty : ParsecT where M : Monad where S : TokenStream { public static readonly ParsecT Default = new ParsecTEmpty(); public K Run( State s, ConsumedOK cok, ConsumedErr cerr, EmptyOK eok, EmptyErr eerr) => eerr(ParseError.Trivial(s.Offset, None, []), s); } ================================================ FILE: LanguageExt.Megaparsec/ParsecT/DSL/Error.cs ================================================ using LanguageExt.Traits; namespace LanguageExt.Megaparsec.DSL; record ParsecTError(ParseError Error) : ParsecT where M : Monad where S : TokenStream { public K Run( State s, ConsumedOK cok, ConsumedErr cerr, EmptyOK eok, EmptyErr eerr) => eerr(Error, s); } ================================================ FILE: LanguageExt.Megaparsec/ParsecT/DSL/Fail.cs ================================================ using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt.Megaparsec.DSL; record ParsecTFail1(string Message) : ParsecT where M : Monad where S : TokenStream { public K Run( State s, ConsumedOK cok, ConsumedErr cerr, EmptyOK eok, EmptyErr eerr) => eerr(ParseError.Fancy(s.Offset, ErrorFancy.Fail(Message)), s); } record ParsecTFail2(E Error) : ParsecT where M : Monad where S : TokenStream { public K Run( State s, ConsumedOK cok, ConsumedErr cerr, EmptyOK eok, EmptyErr eerr) => eerr(ParseError.Fancy(s.Offset, ErrorFancy.Custom(Error)), s); } ================================================ FILE: LanguageExt.Megaparsec/ParsecT/DSL/Label.cs ================================================ using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt.Megaparsec.DSL; record ParsecTLabel(string Name, ParsecT P) : ParsecT where M : Monad where S : TokenStream { public K Run( State s, ConsumedOK cok, ConsumedErr cerr, EmptyOK eok, EmptyErr eerr) { return P.Run(s, cok1, cerr, eok1, eerr1); // Updated ConsumedOK K cok1(A x, State s1, Hints hs) => string.IsNullOrEmpty(Name) ? cok(x, s1, hs.Refresh(None)) : cok(x, s1, hs); // Updated EmptyOK K eok1(A x, State s1, Hints hs) => eok(x, s1, hs.Refresh(ErrorItem.Label(Name))); // Updated EmptyErr K eerr1(ParseError err, State s1) => err switch { ParseError.Trivial(var pos, var us, _) => eerr(ParseError.Trivial(pos, us, ErrorItem.Label(Name)), s1), _ => eerr(err, s1) }; } } ================================================ FILE: LanguageExt.Megaparsec/ParsecT/DSL/Lift.cs ================================================ using LanguageExt.Traits; namespace LanguageExt.Megaparsec.DSL; record ParsecTLift(Func, Reply> F) : ParsecT where M : Monad where S : TokenStream { public K Run( State s, ConsumedOK cok, ConsumedErr cerr, EmptyOK eok, EmptyErr eerr) => F(s) switch { (var s1, true, var result) => result switch { Result.OK(var hs, var x) => cok(x, s1, hs), Result.Error(var e) => cerr(e, s1), _ => throw new NotSupportedException() }, (var s1, false, var result) => result switch { Result.OK(var hs, var x) => eok(x, s1, hs), Result.Error(var e) => eerr(e, s1), _ => throw new NotSupportedException() } }; } record ParsecTLift2(Func, K>> F) : ParsecT where M : Monad where S : TokenStream { public K Run( State s, ConsumedOK cok, ConsumedErr cerr, EmptyOK eok, EmptyErr eerr) => F(s) >> (f => f switch { (var s1, true, var result) => result switch { Result.OK(var hs, var x) => cok(x, s1, hs), Result.Error(var e) => cerr(e, s1), _ => throw new NotSupportedException() }, (var s1, false, var result) => result switch { Result.OK(var hs, var x) => eok(x, s1, hs), Result.Error(var e) => eerr(e, s1), _ => throw new NotSupportedException() } }); } ================================================ FILE: LanguageExt.Megaparsec/ParsecT/DSL/LookAhead.cs ================================================ using LanguageExt.Traits; namespace LanguageExt.Megaparsec.DSL; record ParsecTLookAhead(ParsecT P) : ParsecT where M : Monad where S : TokenStream { public K Run( State s, ConsumedOK cok, ConsumedErr cerr, EmptyOK eok, EmptyErr eerr) { return P.Run(s, eok1, cerr, eok1, eerr); K eok1(A x, State _, Hints __) => eok(x, s, Hints.Empty) ; } } ================================================ FILE: LanguageExt.Megaparsec/ParsecT/DSL/MTransLift.cs ================================================ using LanguageExt.Traits; namespace LanguageExt.Megaparsec.DSL; record ParsecTMTransLift(K ma) : ParsecT where M : Monad where S : TokenStream { public K Run( State s, ConsumedOK cok, ConsumedErr cerr, EmptyOK eok, EmptyErr eerr) => ma >> (a => eok(a, s, Hints.Empty)); } record ParsecTMTransLiftIO(IO ma) : ParsecT where M : Monad where S : TokenStream { public K Run( State s, ConsumedOK cok, ConsumedErr cerr, EmptyOK eok, EmptyErr eerr) => M.LiftIOMaybe(ma) >> (a => eok(a, s, Hints.Empty)); } ================================================ FILE: LanguageExt.Megaparsec/ParsecT/DSL/Map.cs ================================================ using LanguageExt.Traits; namespace LanguageExt.Megaparsec.DSL; record ParsecTMap(ParsecT P, Func F) : ParsecT where M : Monad where S : TokenStream { public K Run( State s, ConsumedOK cok, ConsumedErr cerr, EmptyOK eok, EmptyErr eerr) => P.Run(s, (x, s1, hs) => cok(F(x), s1, hs), cerr, (x, s1, hs) => eok(F(x), s1, hs), eerr); } ================================================ FILE: LanguageExt.Megaparsec/ParsecT/DSL/NoneOf.cs ================================================ using LanguageExt.Traits; namespace LanguageExt.Megaparsec.DSL; record ParsecTNoneOf(S tokens) : ParsecT where S : TokenStream where M : Monad where EqT : Eq { public K Run( State s, ConsumedOK cok, ConsumedErr cerr, EmptyOK eok, EmptyErr eerr) { var ts = S.ChunkToTokens(tokens); if (S.Take1(s.Input, out var head, out var tail)) { foreach (var t in ts) { if(EqT.Equals(head, t)) { return eerr(unexpect(s.Offset, ErrorItem.Token(head), ts), s); } } return cok(head, s with { Input = tail, Offset = s.Offset + 1 }, Hints.Empty); } else { return eerr(unexpect(s.Offset, ErrorItem.EndOfInput(), ts), s); } ParseError unexpect(int pos1, ErrorItem u, ReadOnlySpan tokens) => ParseError.Trivial(pos1, u, ErrorItem.Tokens(tokens)); } } ================================================ FILE: LanguageExt.Megaparsec/ParsecT/DSL/NotFollowedBy.cs ================================================ using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt.Megaparsec.DSL; record ParsecTNotFollowedBy(ParsecT P) : ParsecT where M : Monad where S : TokenStream { public K Run( State s, ConsumedOK _, ConsumedErr __, EmptyOK eok, EmptyErr eerr) { return P.Run(s, cok1, cerr1, eok1, eerr1); // Updated ConsumedOK K cok1(A x, State s1, Hints hs) => eerr(unexpect(what()), s); // Updated EmptyErr K cerr1(ParseError err, State s1) => eok(unit, s, Hints.Empty); // Updated EmptyOK K eok1(A x, State s1, Hints hs) => eerr(unexpect(what()), s); // Updated EmptyErr K eerr1(ParseError err, State s1) => eok(unit, s, Hints.Empty); ParseError unexpect(ErrorItem u) => ParseError.Trivial(s.Offset, u); ErrorItem what() { if (TokenStream.take1(s.Input, out var t, out var _)) { return ErrorItem.Token(t); } else { return ErrorItem.EndOfInput(); } } } } ================================================ FILE: LanguageExt.Megaparsec/ParsecT/DSL/Observing.cs ================================================ using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt.Megaparsec.DSL; record ParsecTObserving(ParsecT P) : ParsecT, A>> where M : Monad where S : TokenStream { public K Run( State s, ConsumedOK, A>, B> cok, ConsumedErr _, EmptyOK, A>, B> eok, EmptyErr __) { return P.Run(s, cok1, cerr1, eok1, eerr1); K cok1(A x, State s1, Hints hs) => cok(Right(x), s1, hs); K eok1(A x, State s1, Hints hs) => eok(Right(x), s1, hs); K cerr1(ParseError err, State s1) => cok(Left(err), s1, Hints.Empty); K eerr1(ParseError err, State s1) => eok(Left(err), s1, Hints.fromOffset(s1.Offset, err)); } } ================================================ FILE: LanguageExt.Megaparsec/ParsecT/DSL/OneOf.cs ================================================ using LanguageExt.Traits; namespace LanguageExt.Megaparsec.DSL; record ParsecTOneOf(S tokens) : ParsecT where S : TokenStream where M : Monad where EqT : Eq { public K Run( State s, ConsumedOK cok, ConsumedErr cerr, EmptyOK eok, EmptyErr eerr) { var ts = S.ChunkToTokens(tokens); if (S.Take1(s.Input, out var head, out var tail)) { foreach (var t in ts) { if(EqT.Equals(head, t)) { return cok(head, s with { Input = tail, Offset = s.Offset + 1 }, Hints.Empty); } } return eerr(unexpect(s.Offset, ErrorItem.Token(head), ts), s); } else { return eerr(unexpect(s.Offset, ErrorItem.EndOfInput(), ts), s); } ParseError unexpect(int pos1, ErrorItem u, ReadOnlySpan tokens) => ParseError.Trivial(pos1, u, ErrorItem.Tokens(tokens)); } } ================================================ FILE: LanguageExt.Megaparsec/ParsecT/DSL/Pure.cs ================================================ using LanguageExt.Traits; namespace LanguageExt.Megaparsec.DSL; record ParsecTPure(A Value) : ParsecT where M : Monad where S : TokenStream { public K Run( State s, ConsumedOK cok, ConsumedErr cerr, EmptyOK eok, EmptyErr eerr) => eok(Value, s, Hints.Empty); } ================================================ FILE: LanguageExt.Megaparsec/ParsecT/DSL/Reader.cs ================================================ using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt.Megaparsec.DSL; record ParsecTAsk : ParsecT> where M : Monad where S : TokenStream { public static readonly ParsecT> Default = new ParsecTAsk(); public K Run( State s, ConsumedOK, B> cok, ConsumedErr cerr, EmptyOK, B> eok, EmptyErr eerr) => eok(s, s, Hints.Empty); } record ParsecTAsks(Func, A> F) : ParsecT where M : Monad where S : TokenStream { public K Run( State s, ConsumedOK cok, ConsumedErr cerr, EmptyOK eok, EmptyErr eerr) => eok(F(s), s, Hints.Empty); } record ParsecTLocal(Func, State> F, ParsecT P) : ParsecT where M : Monad where S : TokenStream { public K Run( State s, ConsumedOK cok, ConsumedErr cerr, EmptyOK eok, EmptyErr eerr) => P.Run(F(s), cok, cerr, eok, eerr); } ================================================ FILE: LanguageExt.Megaparsec/ParsecT/DSL/State.cs ================================================ using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt.Megaparsec.DSL; record ParsecTPutState(State State) : ParsecT where M : Monad where S : TokenStream { public K Run( State s, ConsumedOK _, ConsumedErr __, EmptyOK eok, EmptyErr ___) => eok(unit, State, Hints.Empty); } record ParsecTModifyState(Func, State> Update) : ParsecT where M : Monad where S : TokenStream { public K Run( State s, ConsumedOK _, ConsumedErr __, EmptyOK eok, EmptyErr ___) => eok(unit, Update(s), Hints.Empty); } ================================================ FILE: LanguageExt.Megaparsec/ParsecT/DSL/Take.cs ================================================ using LanguageExt.Traits; using LanguageExt.UnsafeValueAccess; namespace LanguageExt.Megaparsec.DSL; record ParsecTTake(in Option Name, int Amount) : ParsecT where M : Monad where S : TokenStream { public K Run( State s, ConsumedOK cok, ConsumedErr cerr, EmptyOK eok, EmptyErr eerr) { var n = Math.Max(0, Amount); if (S.Take(n, s.Input, out var ts, out var input1)) { var len = S.ChunkLength(ts); if (len == n) { // Happy path return cok(ts, s with { Input = input1, Offset = s.Offset + len }, Hints.Empty); } else { // Didn't read the desired number of tokens var el = (ErrorItem.Label) * Name; var ps = el.IsNone ? [] : Set.singleton(el.ValueUnsafe()!); return eerr(ParseError.Trivial(s.Offset + len, ErrorItem.EndOfInput(), ps), s); } } else { // End of the input stream var el = (ErrorItem.Label) * Name; var ps = el.IsNone ? [] : Set.singleton(el.ValueUnsafe()!); return eerr(ParseError.Trivial(s.Offset, ErrorItem.EndOfInput(), ps), s); } } } ================================================ FILE: LanguageExt.Megaparsec/ParsecT/DSL/TakeWhile.cs ================================================ using LanguageExt.Traits; namespace LanguageExt.Megaparsec.DSL; record ParsecTTakeWhile(in Option Name, Func Test) : ParsecT where M : Monad where S : TokenStream { public K Run( State s, ConsumedOK cok, ConsumedErr cerr, EmptyOK eok, EmptyErr eerr) { S.TakeWhile(Test, s.Input, out var ts, out var input1); var len = S.ChunkLength(ts); var hs = Name.IsSome ? (string)Name switch { var n when string.IsNullOrEmpty(n) => Hints.Empty, var n => Hints.singleton(ErrorItem.Label(n)) } : Hints.Empty; return len <= 0 ? eok(ts, s with { Input = input1, Offset = s.Offset + len }, hs) : cok(ts, s with { Input = input1, Offset = s.Offset + len }, hs); } } ================================================ FILE: LanguageExt.Megaparsec/ParsecT/DSL/TakeWhile1.cs ================================================ using LanguageExt.Traits; namespace LanguageExt.Megaparsec.DSL; record ParsecTTakeWhile1(in Option Name, Func Test) : ParsecT where M : Monad where S : TokenStream { public K Run( State s, ConsumedOK cok, ConsumedErr __, EmptyOK ___, EmptyErr eerr) { S.TakeWhile(Test, s.Input, out var ts, out var input1); var len = S.ChunkLength(ts); var el = Name.IsSome ? Option.Some(ErrorItem.Label((string)Name)) : Option.None; if (len <= 0) { if (S.Take1(s.Input, out var t, out _)) return eerr(ParseError.Trivial(s.Offset, ErrorItem.Token(t), el), s); else return eerr(ParseError.Trivial(s.Offset, ErrorItem.EndOfInput(), el), s); } else { var hs = Name.IsSome ? Hints.singleton((ErrorItem)el) : Hints.Empty; return cok(ts, s with { Input = input1, Offset = s.Offset + len }, hs); } } } ================================================ FILE: LanguageExt.Megaparsec/ParsecT/DSL/Token.cs ================================================ using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt.Megaparsec.DSL; record ParsecTToken(Func> Test, in Set> Expected) : ParsecT where M : Monad where S : TokenStream { public K Run( State s, ConsumedOK cok, ConsumedErr cerr, EmptyOK eok, EmptyErr eerr) { if (S.Take1(s.Input, out var c, out var cs)) { switch (Test(c)) { case { IsSome: true } o: var value = (A)o; return cok(value, s with { Offset = s.Offset + 1, Input = cs }, Hints.Empty); default: return eerr(ParseError.Trivial(s.Offset, ErrorItem.Token(c), Expected), s); } } else { return eerr(ParseError.Trivial(s.Offset, ErrorItem.EndOfInput(), Expected), s); } } } ================================================ FILE: LanguageExt.Megaparsec/ParsecT/DSL/Tokens.cs ================================================ using LanguageExt.Traits; namespace LanguageExt.Megaparsec.DSL; record ParsecTTokens(Func Test, in S Chunk) : ParsecT where M : Monad where S : TokenStream { public K Run( State s, ConsumedOK cok, ConsumedErr cerr, EmptyOK eok, EmptyErr eerr) { var len = S.ChunkLength(Chunk); if (S.Take(len, s.Input, out var tts1, out var input1)) { if (Test(Chunk, tts1)) { var st = s with { Input = input1, Offset = s.Offset + len }; return len <= 0 ? eok(tts1, st, Hints.Empty) : cok(tts1, st, Hints.Empty); } else { return eerr(unexpect(s.Offset, ErrorItem.Tokens(tts1)), s); } } else { return eerr(unexpect(s.Offset, ErrorItem.EndOfInput()), s); } ParseError unexpect(int pos1, ErrorItem u) => ParseError.Trivial(pos1, u, ErrorItem.Tokens(Chunk)); } } ================================================ FILE: LanguageExt.Megaparsec/ParsecT/DSL/Try.cs ================================================ using LanguageExt.Traits; namespace LanguageExt.Megaparsec.DSL; record ParsecTTry(ParsecT P) : ParsecT where M : Monad where S : TokenStream { public K Run( State s, ConsumedOK cok, ConsumedErr cerr, EmptyOK eok, EmptyErr eerr) { return P.Run(s, cok, eerr1, eok, eerr1); // Resets the state to before the error K eerr1(ParseError err, State _) => eerr(err, s); } } ================================================ FILE: LanguageExt.Megaparsec/ParsecT/DSL/WithRecovery.cs ================================================ using LanguageExt.Traits; namespace LanguageExt.Megaparsec.DSL; record ParsecTWithRecovery( Func, K, A>> OnError, ParsecT P) : ParsecT where M : Monad where S : TokenStream { public K Run( State s, ConsumedOK cok, ConsumedErr cerr, EmptyOK eok, EmptyErr eerr) { return P.Run(s, cok, mcerr, eok, meerr); K mcerr(ParseError err, State ms) { return OnError(err).As().Run(ms, rcok, rcerr, reok, reerr); K rcok(A x, State s1, Hints _) => cok(x, s1, Hints.Empty); K rcerr(ParseError _, State __) => cerr(err, ms); K reok(A x, State s1, Hints _) => eok(x, s1, Hints.fromOffset(s1.Offset, err)); K reerr(ParseError _, State s1) => cerr(err, ms); } K meerr(ParseError err, State ms) { return OnError(err).As().Run(ms, rcok, rcerr, reok, reerr); K rcok(A x, State s1, Hints _) => cok(x, s1, Hints.fromOffset(s1.Offset, err)); K rcerr(ParseError _, State __) => eerr(err, ms); K reok(A x, State s1, Hints _) => eok(x, s1, Hints.fromOffset(s1.Offset, err)); K reerr(ParseError _, State s1) => eerr(err, ms); } } } ================================================ FILE: LanguageExt.Megaparsec/ParsecT/Extensions/ParsecTExtensions.cs ================================================ using LanguageExt.Megaparsec.DSL; using LanguageExt.Traits; namespace LanguageExt.Megaparsec; public static class ParsecTExtensions { extension(K, A> self) where S : TokenStream where M : Monad { /// /// Downcast operator /// public ParsecT As() => (ParsecT)self; /// /// Run the parser /// /// Starting state /// Result of the parse public K> Run(State initialState) { return self.As().Run(initialState, cok, cerr, eok, eerr); K> cok(A x, State s1, Hints hs) => M.Pure(Reply.ConsumedOK(x, s1, hs)); K> cerr(ParseError e, State s1) => M.Pure(Reply.ConsumedError(e, s1)); K> eok(A x, State s1, Hints hs) => M.Pure(Reply.EmptyOK(x, s1, hs)); K> eerr(ParseError e, State s1) => M.Pure(Reply.EmptyError(e, s1)); } /// /// Invoke this parser and then feed the lifted reply to the `f` function provided. /// Then rewrap the resulting lifted reply into a new parser. /// /// Reply mapping function /// New parser result type /// Parser public ParsecT Hoist(Func>, K>> f) => DSL.lift(s => f(self.Run(s))); /// /// Downcast operator /// public static ParsecT operator +(K, A> ma) => ma.As(); /// /// Downcast operator /// public static ParsecT operator >>(K, A> lhs, Lower _) => lhs.As(); } } ================================================ FILE: LanguageExt.Megaparsec/ParsecT/ParsecT.cs ================================================ using LanguageExt.Traits; namespace LanguageExt.Megaparsec; /// /// Parser combinator transformer /// /// Error type /// Token stream type /// Token type /// Lifted monad /// Parse value type public interface ParsecT : K, A> where M : Monad where S : TokenStream { /// /// Run the parser /// /// Starting state /// Called when a value has successfully parsed /// Called when a value has partially parsed before failure /// Called when the parsing process was successful but didn't yield a value /// Called when the parsing process was unsuccessful and didn't parse anything at all /// Result of the parse being passed to one of the provided functions K Run( State initialState, ConsumedOK consumedOK, ConsumedErr consumedError, EmptyOK emptyOK, EmptyErr emptyError); } ================================================ FILE: LanguageExt.Megaparsec/ParsecT/Trait/ParsecT.TraitImpl.cs ================================================ using LanguageExt.Megaparsec.DSL; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt.Megaparsec; public class ParsecT : MonoidK>, MonadParsecT, E, S, T, M>, MonadIO> where M : Monad where S : TokenStream { static K, A> MonadParsecT, E, S, T, M>.Error( ParseError error) => DSL.error(error); static K, A> MonadParsecT, E, S, T, M>.Label( string name, K, A> p) => DSL.label(name, p); static K, A> MonadParsecT, E, S, T, M>.Try( K, A> p) => DSL.@try(p); static K, A> MonadParsecT, E, S, T, M>.LookAhead( K, A> p) => DSL.lookAhead(p); static K, Unit> MonadParsecT, E, S, T, M>.NotFollowedBy( K, A> p) => DSL.notFollowedBy(p); static K, A> MonadParsecT, E, S, T, M>.WithRecovery( Func, K, A>> onError, K, A> p) => DSL.withRecovery(onError, p); static K, Unit> MonadParsecT, E, S, T, M>.EOF => DSL.eof; static K, Either, A>> MonadParsecT, E, S, T, M>.Observing( K, A> p) => DSL.observing(p); static K, A> MonadParsecT, E, S, T, M>.Token( Func> test, in Set> expected) => DSL.token(test, expected); static K, S> MonadParsecT, E, S, T, M>.Tokens( Func test, in S chunk) => DSL.tokens(test, chunk); static K, T> MonadParsecT, E, S, T, M>.OneOf( S tokens) => DSL.oneOf(tokens); static K, T> MonadParsecT, E, S, T, M>.NoneOf( S tokens) => DSL.noneOf(tokens); static K, S> MonadParsecT, E, S, T, M>.TakeWhile( Func test, in Option name) => DSL.takeWhile(test, name); static K, S> MonadParsecT, E, S, T, M>.TakeWhile1( Func test, in Option name) => DSL.takeWhile1(test, name); static K, S> MonadParsecT, E, S, T, M>.Take( int n, in Option name) => DSL.take(name, n); static K, A> MonadParsecT, E, S, T, M>.Lift( Func, Reply> f) => DSL.lift(f); static K, A> Identifiable, string>.Identify( K, A> fa, Label label) => DSL.label(label.Value, fa); static K, B> Functor>.Map( Func f, K, A> ma) => DSL.map(ma.As(), f); static K, A> Applicative>.Pure(A value) => DSL.pure(value); static K, B> Applicative>.Apply( K, Func> mf, K, A> ma) => DSL.apply(mf.As(), ma.As()); static K, B> Applicative>.Apply( K, Func> mf, Memo, A> ma) => DSL.apply(mf.As(), ma); static K, B> Monad>.Bind( K, A> ma, Func, B>> f) => DSL.bind(ma, f); static K, B> Monad>.Recur(A value, Func, Next>> f) => Monad.unsafeRecur(value, f); static K, A> Choice>.Choose( K, A> fa, K, A> fb) => DSL.choose(fa, fb); static K, A> Choice>.Choose( K, A> fa, Memo, A> fb) => DSL.choose(fa, fb); static K, A> SemigroupK>.Combine( K, A> fa, K, A> fb) => SemigroupInstance.Instance switch { { IsSome: true, Case: SemigroupInstance semi } => ((A x, A y) => semi.Combine(x, y)) * fa * fb, _ => DSL.choose(fa, fb) }; static K, A> MonoidK>.Empty() => DSL.empty(); static K, A> Alternative>.Empty() => DSL.empty(); static K, A> Fallible, ParsecT>.Fail(ParseError error) => DSL.error(error); static K, A> Fallible, ParsecT>.Catch( K, A> fa, Func, bool> predicate, Func, K, A>> fail) => DSL.@catch(fa, predicate, fail); static K, A> MonadIO>.LiftIO(IO ma) => DSL.liftIO(ma); static K, A> MonadT, M>.Lift(K ma) => DSL.liftM(ma); static K, A> Readable, State>.Asks( Func, A> f) => DSL.asks(f); static K, A> Readable, State>.Local( Func, State> f, K, A> ma) => DSL.local(f, ma.As()); static K, Unit> Stateful, State>.Put( State value) => DSL.put(value); static K, Unit> Stateful, State>.Modify( Func, State> f) => DSL.modify(f); static K, A> Stateful, State>.Gets( Func, A> f) => DSL.asks(f); } ================================================ FILE: LanguageExt.Megaparsec/Pos.cs ================================================ using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Numerics; using LanguageExt.Traits; namespace LanguageExt.Megaparsec; /// /// `Pos` is the type for positive integers. This is used to represent line /// number, column number, and similar things like indentation level. /// /// `Semigroup` instance can be used to safely and efficiently add `Pos` values /// together. /// /// public readonly record struct Pos(int Value) : Monoid, INumber { public Pos Combine(Pos rhs) => new (Value + rhs.Value); /// /// Monoid identity: Zero position /// static Pos Monoid.Empty { get; } = new (0); /// /// Zero position /// public static Pos Zero { get; } = new (0); public static Pos One { get; } = new(1); public static int Radix { get; } = 10; public static Pos AdditiveIdentity { get; } = new(0); public static Pos MultiplicativeIdentity { get; } = new(1); /// /// Implicit conversion from int to Pos /// public static implicit operator Pos(int value) => new (value); public static Pos Abs(Pos value) => new(Math.Abs(value.Value)); public static bool IsCanonical(Pos value) => true; public static bool IsComplexNumber(Pos value) => false; public static bool IsEvenInteger(Pos value) => int.IsEvenInteger(value.Value); public static bool IsFinite(Pos value) => true; public static bool IsImaginaryNumber(Pos value) => false; public static bool IsInfinity(Pos value) => false; public static bool IsInteger(Pos value) => true; public static bool IsNaN(Pos value) => false; public static bool IsNegative(Pos value) => value.Value < 0; public static bool IsNegativeInfinity(Pos value) => false; public static bool IsNormal(Pos value) => true; public static bool IsOddInteger(Pos value) => (value.Value & 1) == 1; public static bool IsPositive(Pos value) => true; public static bool IsPositiveInfinity(Pos value) => false; public static bool IsRealNumber(Pos value) => false; public static bool IsSubnormal(Pos value) => false; public static bool IsZero(Pos value) => value.Value == 0; public static Pos MaxMagnitude(Pos x, Pos y) => x.Value > y.Value ? x : y; public static Pos MaxMagnitudeNumber(Pos x, Pos y) => x.Value > y.Value ? x : y; public static Pos MinMagnitude(Pos x, Pos y) => x.Value < y.Value ? x : y; public static Pos MinMagnitudeNumber(Pos x, Pos y) => x.Value < y.Value ? x : y; public static Pos Parse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider) => new (int.Parse(s, style, provider)); public static Pos Parse(string s, NumberStyles style, IFormatProvider? provider) => new (int.Parse(s, style, provider)); public static Pos Parse(string s, IFormatProvider? provider) => new (int.Parse(s, provider)); public static Pos Parse(ReadOnlySpan s, IFormatProvider? provider) => new (int.Parse(s, provider)); public static bool TryConvertFromChecked(TOther value, out Pos result) where TOther : INumberBase => throw new NotImplementedException(); public static bool TryConvertFromSaturating(TOther value, out Pos result) where TOther : INumberBase => throw new NotImplementedException(); public static bool TryConvertFromTruncating(TOther value, out Pos result) where TOther : INumberBase => throw new NotImplementedException(); public static bool TryConvertToChecked(Pos value, [MaybeNullWhen(false)] out TOther result) where TOther : INumberBase => throw new NotImplementedException(); public static bool TryConvertToSaturating(Pos value, [MaybeNullWhen(false)] out TOther result) where TOther : INumberBase => throw new NotImplementedException(); public static bool TryConvertToTruncating(Pos value, [MaybeNullWhen(false)] out TOther result) where TOther : INumberBase => throw new NotImplementedException(); public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, out Pos result) { if (int.TryParse(s, style, provider, out var x)) { result = new Pos(x); return true; } result = default; return false; } public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, IFormatProvider? provider, out Pos result) { if (int.TryParse(s, style, provider, out var x)) { result = new Pos(x); return true; } result = default; return false; } public static bool TryParse([NotNullWhen(true)] string? s, IFormatProvider? provider, out Pos result) { if (int.TryParse(s, provider, out var x)) { result = new Pos(x); return true; } result = default; return false; } public static bool TryParse(ReadOnlySpan s, IFormatProvider? provider, out Pos result) { if (int.TryParse(s, provider, out var x)) { result = new Pos(x); return true; } result = default; return false; } public int CompareTo(object? obj) => obj is Pos rhs ? CompareTo(rhs) : 1; public int CompareTo(Pos other) => Value.CompareTo(other.Value); public string ToString(string? format, IFormatProvider? formatProvider) => $"Pos({Value})"; public bool TryFormat(Span destination, out int charsWritten, ReadOnlySpan format, IFormatProvider? provider) => throw new NotImplementedException(); public static Pos operator +(Pos left, Pos right) => new (left.Value + right.Value); public static bool operator >(Pos left, Pos right) => left.Value > right.Value; public static bool operator >=(Pos left, Pos right) => left.Value >= right.Value; public static bool operator <(Pos left, Pos right) => left.Value < right.Value; public static bool operator <=(Pos left, Pos right) => left.Value <= right.Value; public static Pos operator --(Pos value) => new(value.Value - 1); public static Pos operator /(Pos left, Pos right) => new (left.Value / right.Value); public static Pos operator ++(Pos value) => new (value.Value + 1); public static Pos operator %(Pos left, Pos right) => new (left.Value % right.Value); public static Pos operator *(Pos left, Pos right) => new (left.Value * right.Value); public static Pos operator -(Pos left, Pos right) => new (left.Value - right.Value); public static Pos operator -(Pos value) => new (-value.Value); public static Pos operator +(Pos value) => new (+value.Value); } ================================================ FILE: LanguageExt.Megaparsec/PosState.cs ================================================ namespace LanguageExt.Megaparsec; public readonly record struct PosState( S Input, int Offset, SourcePos SourcePos, int TabWidth, string LinePrefix); ================================================ FILE: LanguageExt.Megaparsec/Reach.cs ================================================ using LanguageExt.Traits; namespace LanguageExt.Megaparsec; internal static class Reach where S : TokenStream { // Buffer size for collecting a line of text const int lineBufferSize = 256; const int lineBufferSizeMinus3 = lineBufferSize - 3; /// /// TODO /// /// Offset to reach /// Initial `PosState` to use /// public static (string Line, PosState UpdatedState) offset(int o, in PosState posState) { // Split the input stream at the current offset position // TODO: Consider if the maths here are correct // TODO: Probably TokenStream should support Split S.Take(posState.Offset, posState.Input, out _, out var input); S.Take(o, input, out var pre, out var post); //var (pre, post) = //splitAt(o - posState.Offset, posState.Input); -- OLD VERSION OF ABOVE LINE // Walk the input stream and collect the position and line-text Span ns = stackalloc char[lineBufferSize]; var spos = posState.SourcePos; var c = spos.Column.Value; var l = spos.Line.Value; var w = posState.TabWidth; var ix = 0; while(S.Take1(pre, out var tok, out pre)) { if (S.IsNewline(tok)) { // Newlines add 1 to the line number, reset the column number to 1, and reset the line text l += 1; c = 1; ix = 0; } else if (S.IsTab(tok)) { // Tabs add 0- spaces to align with the tab stop var tw = w - (c - 1) % w; c += tw; for (var i = 0; i < tw; i++) { if(ix < lineBufferSizeMinus3 ) ns[ix] = ' '; ix++; } } else { // Regular tokens should be converted to char strings and added to the line text var tchars = S.TokenToString(tok); foreach (var tch in tchars) { if (ix < lineBufferSizeMinus3) ns[ix] = tch; c++; ix++; } } // Truncate long lines if (ix == lineBufferSize) { ns[lineBufferSize - 1] = '.'; ns[lineBufferSize - 2] = '.'; ns[lineBufferSize - 3] = '.'; } } spos = spos with { Column = c, Line = l }; var sameLine = spos.Line == posState.SourcePos.Line; var line = sameLine ? posState.LinePrefix + (ix == 0 ? "" : new string(ns[..Math.Min(ix, lineBufferSize)])) : ix == 0 ? "" : new string(ns[..Math.Min(ix, lineBufferSize)]); var pstate = new PosState( post, Math.Max(o, posState.Offset), spos, posState.TabWidth, posState.LinePrefix); return (line, pstate); } /// /// TODO /// /// Offset to reach /// Initial `PosState` to use /// public static PosState offsetNoLine(int o, in PosState posState) { // Split the input stream at the current offset position // TODO: Consider if the maths here are correct // TODO: Probably TokenStream should support Split S.Take(posState.Offset, posState.Input, out _, out var input); S.Take(o, input, out var pre, out var post); //var (pre, post) = //splitAt(o - posState.Offset, posState.Input); -- OLD VERSION OF ABOVE LINE // Walk the input stream and collect the position and line-text var spos = posState.SourcePos; var c = spos.Column.Value; var l = spos.Line.Value; var w = posState.TabWidth; while(S.Take1(pre, out var tok, out pre)) { if (S.IsNewline(tok)) { // Newlines add 1 to the line number, reset the column number to 1, and reset the line text l += 1; c = 1; } else if (S.IsTab(tok)) { // Tabs add 0- spaces to align with the tab stop var tw = w - (c - 1) % w; c += tw; } else { // Regular tokens should be converted to char strings and added to the line text var tchars = S.TokenToString(tok); c += tchars.Length; } } spos = spos with { Column = c, Line = l }; var pstate = new PosState( post, Math.Max(o, posState.Offset), spos, posState.TabWidth, posState.LinePrefix); return pstate; } /// /// Replace tab characters with the given number of spaces /// /// Tab width /// String to expand /// public static string expandTab(Pos tabWidth, string str) { // Find out the new size of the string var tabSize = tabWidth.Value; var length = 0; foreach (var c in str) { if (c == '\t') { length += tabSize - length % tabSize; } else { length++; } } if (str.Length == length) { // Bail early when there are no tabs to expand return str; } Span ns = stackalloc char[length]; var index = 0; foreach (var c in str) { if (c == '\t') { var tl = tabSize - index % tabSize; for (var i = 0; i < tl; i++) { ns[index++] = ' '; } } else { ns[index++] = c; } } return new string(ns); } } ================================================ FILE: LanguageExt.Megaparsec/Reply/Extensions/Reply.Extensions.cs ================================================ using LanguageExt.Traits; namespace LanguageExt.Megaparsec; public static class ReplyExtensions { extension(K, A> self) { public Reply As() => (Reply)self; public static Reply operator +(K, A> ma) => ma.As(); } } ================================================ FILE: LanguageExt.Megaparsec/Reply/Reply.Module.cs ================================================ namespace LanguageExt.Megaparsec; /// /// Reply module /// public static class Reply { public static Reply ConsumedOK(A value, State state, Hints hints) => new (state, true, Result.OK(hints, value)); public static Reply EmptyOK(A value, State state, Hints hints) => new (state, false, Result.OK(hints, value)); public static Reply ConsumedError(ParseError error, State state) => new (state, true, Result.Error(error)); public static Reply EmptyError(ParseError error, State state) => new (state, false, Result.Error(error)); } ================================================ FILE: LanguageExt.Megaparsec/Reply/Reply.cs ================================================ using LanguageExt.Traits; namespace LanguageExt.Megaparsec; /// /// All information available after parsing. This includes consumption of input, success (with the returned value) or /// failure (with the parse error), and parser state at the end of parsing. 'Reply' can also be used to resume parsing. /// /// Updated state /// Consumption flag /// Parsed value /// Error type /// Stream type /// Token type /// Value type public readonly record struct Reply(State NewState, bool Consumed, Result Result) : K, A> { public Reply(State NewState, bool Consumed, K, A> Result) : this(NewState, Consumed, +Result) { } } ================================================ FILE: LanguageExt.Megaparsec/Reply/Trait/Reply.TraitImpl.cs ================================================ using LanguageExt.Traits; namespace LanguageExt.Megaparsec; public class Reply : Functor> { static K, B> Functor>.Map(Func f, K, A> ma) => ma switch { Reply r => new Reply(r.NewState, r.Consumed, f * r.Result), _ => throw new Exception("Impossible") }; } ================================================ FILE: LanguageExt.Megaparsec/Result/Extensions/Result.Extensions.cs ================================================ using LanguageExt.Traits; namespace LanguageExt.Megaparsec; public static class ResultExtensions { extension(K, A> self) { public Result As() => (Result)self; public static Result operator+(K, A> ma) => (Result)ma; } } ================================================ FILE: LanguageExt.Megaparsec/Result/Result.Module.cs ================================================ namespace LanguageExt.Megaparsec; public static class Result { /// /// Parser succeeded (includes hints) /// /// Hints /// Parsed value /// Token type /// Error type /// Value type /// Result public static Result OK(Hints hints, A value) => new Result.OK(hints, value); /// /// Parse failed /// /// Error value /// Token type /// Error type /// Value type /// Result public static Result Error(ParseError error) => new Result.Error(error); } ================================================ FILE: LanguageExt.Megaparsec/Result/Result.cs ================================================ using LanguageExt.Traits; namespace LanguageExt.Megaparsec; /// /// Whether the parser has failed or not. On success we include the /// resulting value, on failure we include a `ParseError`. /// /// Token type /// Error type /// Value type public abstract record Result : K, A> { /// /// Parser succeeded (includes hints) /// /// Hints /// Parsed value public record OK(Hints Hints, A Value) : Result; /// /// Parse failed /// /// Error value public record Error(ParseError Value) : Result; } ================================================ FILE: LanguageExt.Megaparsec/Result/Trait/Result.TraitImpl.cs ================================================ using LanguageExt.Traits; namespace LanguageExt.Megaparsec; /// /// Result trait implementation /// /// Token type /// Error type public class Result : Functor> { static K, B> Functor>.Map(Func f, K, A> ma) => ma switch { Result.OK (var hints, var value) => Result.OK(hints, f(value)), Result.Error (var e) => Result.Error(e), _ => throw new NotSupportedException() }; } ================================================ FILE: LanguageExt.Megaparsec/SourcePos.cs ================================================ namespace LanguageExt.Megaparsec; /// /// Represents a source position in a named location (i.e. a file) /// /// Source location name (file name) /// Line number, starting at 1 /// Column number, starting at 1 public readonly record struct SourcePos( string Name, Pos Line, Pos Column) { /// /// Create a source position from a name /// /// Name /// SourcePos public static SourcePos FromName(string name) => new (name, Pos.One, Pos.One); /// /// Convert to string /// /// String representation of the structure public override string ToString() => $"{Name}({Line},{Column})"; /// /// Move to the beginning of the next line /// /// public SourcePos NextLine => this with { Line = Line + 1, Column = Pos.One }; /// /// Move to the next token /// /// public SourcePos NextToken => this with { Column = Column + 1 }; /// /// Move to the next token /// /// public SourcePos Next(int amount) => this with { Column = Column + amount }; } ================================================ FILE: LanguageExt.Megaparsec/State.cs ================================================ namespace LanguageExt.Megaparsec; public readonly record struct State( S Input, int Offset, PosState PosState, Seq> ParseErrors); ================================================ FILE: LanguageExt.Parsec/Common.cs ================================================ using static LanguageExt.Parsec.ParserResult; namespace LanguageExt.Parsec; public static class Common { public static readonly Parser getDefPos = inp => ConsumedOK(inp.DefPos, inp); public static Parser setDefPos(Pos defpos, Parser p) => inp => p(inp.SetDefPos(defpos)); public static bool onside(Pos pos, Pos delta) => pos.Column > delta.Column || pos.Line == delta.Line; public static ParserError mergeError(ParserError? err1, ParserError? err2) => err1 is null && err2 is null ? ParserError.Unknown(Pos.Zero) : err1 is null ? err2 ?? ParserError.Unknown(Pos.Zero) : err2 is null ? err1 : string.IsNullOrEmpty(err1.Msg) ? err2 : string.IsNullOrEmpty(err2.Msg) ? err1 : Pos.Compare( err1.Pos, err2.Pos, EQ: () => err1 > err2 ? new ParserError(err1.Tag, err1.Pos, err1.Msg, List.append(err1.Expected, err2.Expected).ToLst()) : new ParserError(err2.Tag, err2.Pos, err2.Msg, List.append(err1.Expected, err2.Expected).ToLst()), GT: () => err1, LT: () => err2); public static Reply mergeErrorReply(ParserError err, Reply reply) => reply.Tag == ReplyTag.OK ? Reply.OK(reply.Result!, reply.State!, mergeError(err, reply.Error)) : Reply.Error(mergeError(err, reply.Error)); } ================================================ FILE: LanguageExt.Parsec/Exceptions.cs ================================================ using System; namespace LanguageExt.Parsec { #if !COREFX13 [Serializable] #endif public class ParserException : Exception { public ParserException() { } public ParserException(string message) : base(message) { } public ParserException(string message, Exception innerException) : base(message, innerException) { } } } ================================================ FILE: LanguageExt.Parsec/GenLanguageDef.cs ================================================ using System.Linq; using static LanguageExt.Parsec.Prim; namespace LanguageExt.Parsec; /// /// The GenLanguageDef type is a record that contains all parameteridable /// features of the "Parsec.Text.Token" module. The module "Parsec.Text.Language" /// contains some default definitions. /// public class GenLanguageDef { /// /// Describes the start of a block comment. Use the empty string if the /// language doesn't support block comments. For example "/*". /// public readonly string CommentStart; /// /// Describes the end of a block comment. Use the empty string if the /// language doesn't support block comments. For example "*\". /// public readonly string CommentEnd; /// /// Describes the start of a line comment. Use the empty string if the /// language doesn't support line comments. For example "//". /// public readonly string CommentLine; /// /// Set to 'True' if the language supports nested block comments. /// public readonly bool NestedComments; /// /// This parser should accept any start characters of identifiers. For /// example either(letter,char('_')). /// public readonly Parser IdentStart; /// /// This parser should accept any legal tail characters of identifiers. /// For example either(alphaNum, char('_')). /// public readonly Parser IdentLetter; /// /// This parser should accept any start characters of operators. For /// example oneOf(":!#$%&*+.\/\〈=〉?\@\\\\^|-~") /// public readonly Parser OpStart; /// /// This parser should accept any legal tail characters of operators. /// Note that this parser should even be defined if the language doesn't /// support user-defined operators, or otherwise the 'reservedOp' /// parser won't work correctly. /// public readonly Parser OpLetter; /// /// The list of reserved identifiers. /// public readonly Lst ReservedNames; /// /// The list of reserved operators. /// public readonly Lst ReservedOpNames; /// /// Set to 'True' if the language is case sensitive. /// public readonly bool CaseSensitive; /// /// Empty definition, use With to build /// public static readonly GenLanguageDef Empty = new ("", "", "", true, zero(), zero(), zero(), zero(), List.empty(), List.empty(), true); GenLanguageDef( string commentStart, string commentEnd, string commentLine, bool nestedComments, Parser identStart, Parser identLetter, Parser opStart, Parser opLetter, Lst reservedNames, Lst reservedOpNames, bool caseSensitive ) { CommentStart = commentStart; CommentEnd = commentEnd; CommentLine = commentLine; NestedComments = nestedComments; IdentStart = identStart; IdentLetter = identLetter; OpStart = opStart; OpLetter = opLetter; ReservedNames = reservedNames.OrderBy(x => x).AsIterable().ToLst(); ReservedOpNames = reservedOpNames.OrderBy(x=> x).AsIterable().ToLst(); CaseSensitive = caseSensitive; } public GenLanguageDef With( string? CommentStart = null, string? CommentEnd = null, string? CommentLine = null, bool? NestedComments = null, Parser? IdentStart = null, Parser? IdentLetter = null, Parser? OpStart = null, Parser? OpLetter = null, Lst? ReservedNames = null, Lst? ReservedOpNames = null, bool? CaseSensitive = null ) => new GenLanguageDef( CommentStart ?? this.CommentStart, CommentEnd ?? this.CommentEnd, CommentLine ?? this.CommentLine, NestedComments ?? this.NestedComments, IdentStart ?? this.IdentStart, IdentLetter ?? this.IdentLetter, OpStart ?? this.OpStart, OpLetter ?? this.OpLetter, ReservedNames ?? this.ReservedNames, ReservedOpNames ?? this.ReservedOpNames, CaseSensitive ?? this.CaseSensitive ); } ================================================ FILE: LanguageExt.Parsec/GenTokenParser.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using static LanguageExt.Prelude; using static LanguageExt.Parsec.ParserResult; using static LanguageExt.Parsec.Internal; using static LanguageExt.Parsec.Prim; using static LanguageExt.Parsec.Char; namespace LanguageExt.Parsec { public class GenTokenParser { /// /// This lexeme parser parses a legal identifier. Returns the identifier /// string. This parser will fail on identifiers that are reserved /// words. Legal identifier (start) characters and reserved words are /// defined in the 'LanguageDef' that is passed to /// 'makeTokenParser'. An identifier is treated as /// a single token using 'try'. /// public readonly Parser Identifier; /// /// The lexeme parser reserved(name) parses a symbol /// name, but it also checks that the name is not a prefix of a /// valid identifier. A reserved word is treated as a single token /// using 'try'. /// public readonly Func> Reserved; /// /// This lexeme parser parses a legal operator. Returns the name of the /// operator. This parser will fail on any operators that are reserved /// operators. Legal operator (start) characters and reserved operators /// are defined in the 'LanguageDef' that is passed to /// 'makeTokenParser'. An operator is treated as a /// single token using 'try'. /// public readonly Parser Operator; /// /// The lexeme parser reservedOp name parses symbol /// name, but it also checks that the name is not a prefix of a /// valid operator. A reservedOp is treated as a single token using /// 'try'. /// public readonly Func> ReservedOp; /// /// This lexeme parser parses a single literal character. Returns the /// literal character value. This parsers deals correctly with escape /// sequences. The literal character is parsed according to the grammar /// rules defined in the Haskell report (which matches most programming /// languages quite closely). /// public readonly Parser CharLiteral; /// /// This lexeme parser parses a literal string. Returns the literal /// string value. This parsers deals correctly with escape sequences and /// gaps. The literal string is parsed according to the grammar rules /// defined in the Haskell report (which matches most programming /// languages quite closely). /// public readonly Parser StringLiteral; /// /// This lexeme parser parses a natural number (a positive whole /// number). Returns the value of the number. The number can be /// specified in 'decimal', 'hexadecimal' or /// 'octal'. The number is parsed according to the grammar /// rules in the Haskell report. /// public readonly Parser Natural; /// /// This lexeme parser parses an integer (a whole number). This parser /// is like 'natural' except that it can be prefixed with /// sign (i.e. \'-\' or \'+\'). Returns the value of the number. The /// number can be specified in 'decimal', 'hexadecimal' /// or 'octal'. The number is parsed according /// to the grammar rules in the Haskell report. /// public readonly Parser Integer; /// /// This lexeme parser parses a floating point value. Returns the value /// of the number. The number is parsed according to the grammar rules /// defined in the Haskell report. /// public readonly Parser Float; /// /// This lexeme parser parses either 'natural' or a 'float'. /// Returns the value of the number. This parsers deals with /// any overlap in the grammar rules for naturals and floats. The number /// is parsed according to the grammar rules defined in the Haskell report. /// public readonly Parser> NaturalOrFloat; /// /// Parses a positive whole number in the decimal system. Returns the /// value of the number. /// public readonly Parser Decimal; /// /// Parses a positive whole number in the hexadecimal system. The number /// should be prefixed with "0x" or "0X". Returns the value of the /// number. /// public readonly Parser Hexadecimal; /// /// Parses a positive whole number in the octal system. The number /// should be prefixed with "0o" or "0O". Returns the value of the /// number. /// public readonly Parser Octal; /// /// Lexeme parser symbol(s) parses 'string' s and skips /// trailing white space. /// public readonly Func> Symbol; /// /// lexeme(p) first applies parser p and than the 'whiteSpace' /// parser, returning the value of p. Every lexical /// token (lexeme) is defined using lexeme, this way every parse /// starts at a point without white space. Parsers that use lexeme are /// called lexeme parsers in this document. /// /// The only point where the 'whiteSpace' parser should be /// called explicitly is the start of the main parser in order to skip /// any leading white space. /// public Parser Lexeme(Parser p) => from x in p from _ in WhiteSpace select x; /// /// Parses any white space. White space consists of /zero/ or more /// occurrences of a 'space', a line comment or a block (multi /// line) comment. Block comments may be nested. How comments are /// started and ended is defined in the 'LanguageDef' /// that is passed to 'makeTokenParser'. /// public readonly Parser WhiteSpace; /// /// Lexeme parser parens(p) parses p enclosed in parenthesis, /// returning the value of p. /// public Parser Parens(Parser p) => from o in Symbol("(") from x in p from c in Symbol(")") select x; /// /// Lexeme parser braces(p) parses p enclosed in braces { and /// }, returning the value of p. /// public Parser Braces(Parser p) => from o in Symbol("{") from x in p from c in Symbol("}") select x; /// /// Lexeme parser angles(p) parses p enclosed in angle brackets 〈 /// and 〉, returning the value of p. /// public Parser Angles(Parser p) => from o in Symbol("<") from x in p from c in Symbol(">") select x; /// /// Lexeme parser brackets(p) parses p enclosed in brackets [ /// and ], returning the value of p. /// public Parser Brackets(Parser p) => from o in Symbol("[") from x in p from c in Symbol("]") select x; /// /// Lexeme parser semi parses the character ; and skips any /// trailing white space. Returns the string ";". /// public readonly Parser Semi; /// /// Lexeme parser comma parses the character , and skips any /// trailing white space. Returns the string ",". /// public readonly Parser Comma; /// /// Lexeme parser colon parses the character : and skips any /// trailing white space. Returns the string ":". /// public readonly Parser Colon; /// /// Lexeme parser dot parses the character . and skips any /// trailing white space. Returns the string ".". /// public readonly Parser Dot; /// /// Lexeme parser semiSep(p) parses /zero/ or more occurrences of p /// separated by semi. Returns a list of values returned by /// p. /// public Parser> SemiSep(Parser p) => sepBy(p, Semi); /// /// Lexeme parser semiSep1(p) parses /one/ or more occurrences of p /// separated by 'semi'. Returns a list of values returned by p. /// public Parser> SemiSep1(Parser p) => sepBy1(p, Semi); /// /// Lexeme parser commaSep(p) parses /zero/ or more occurrences of /// p separated by 'comma'. Returns a list of values returned /// by p. /// public Parser> CommaSep(Parser p) => sepBy(p, Comma); /// /// Lexeme parser commaSep1(p) parses /one/ or more occurrences of /// p separated by 'comma'. Returns a list of values returned /// by p. /// public Parser> CommaSep1(Parser p) => sepBy1(p, Comma); public Parser> BracketsCommaSep1(Parser p) => Brackets(sepBy1(p, Comma)); public Parser> BracketsCommaSep(Parser p) => Brackets(sepBy(p, Comma)); public Parser> ParensCommaSep1(Parser p) => Parens(sepBy1(p, Comma)); public Parser> ParensCommaSep(Parser p) => Parens(sepBy(p, Comma)); public Parser> AnglesCommaSep1(Parser p) => Angles(sepBy1(p, Comma)); public Parser> AnglesCommaSep(Parser p) => Angles(sepBy(p, Comma)); public Parser> BracesCommaSep1(Parser p) => Braces(sepBy1(p, Comma)); public Parser> BracesCommaSep(Parser p) => Braces(sepBy(p, Comma)); public Parser> BracketsSemiSep1(Parser p) => Brackets(sepBy1(p, Semi)); public Parser> BracketsSemiSep(Parser p) => Brackets(sepBy(p, Semi)); public Parser> ParensSemiSep1(Parser p) => Parens(sepBy1(p, Semi)); public Parser> ParensSemiSep(Parser p) => Parens(sepBy(p, Semi)); public Parser> AnglesSemiSep1(Parser p) => Angles(sepBy1(p, Semi)); public Parser> AnglesSemiSep(Parser p) => Angles(sepBy(p, Semi)); public Parser> BracesSemiSep1(Parser p) => Braces(sepBy1(p, Semi)); public Parser> BracesSemiSep(Parser p) => Braces(sepBy(p, Semi)); internal GenTokenParser( Parser indentifier, Func> reserved, Parser op, Func> reservedOp, Parser charLiteral, Parser stringLiteral, Parser natural, Parser integer, Parser floating, Parser> naturalOrFloat, Parser dec, Parser hexadecimal, Parser octal, Func> symbol, Parser whiteSpace, Parser semi, Parser comma, Parser colon, Parser dot ) { Identifier = indentifier; this.Reserved = reserved; this.Operator = op; this.ReservedOp = reservedOp; this.CharLiteral = charLiteral; this.StringLiteral = stringLiteral; this.Natural = natural; this.Integer = integer; this.Float = floating; this.NaturalOrFloat = naturalOrFloat; this.Decimal = dec; Hexadecimal = hexadecimal; Octal = octal; this.Symbol = symbol; this.WhiteSpace = whiteSpace; this.Semi = semi; this.Comma = comma; this.Colon = colon; this.Dot = dot; } } } ================================================ FILE: LanguageExt.Parsec/GenTokenParser2.cs ================================================ using System; using LanguageExt.UnsafeValueAccess; using static LanguageExt.Prelude; using static LanguageExt.Parsec.Prim; namespace LanguageExt.Parsec; public class GenTokenParser2 { /// /// This lexeme parser parses a legal identifier. Returns the identifier /// string. This parser will fail on identifiers that are reserved /// words. Legal identifier (start) characters and reserved words are /// defined in the 'LanguageDef' that is passed to /// 'makeTokenParser'. An identifier is treated as /// a single token using 'try'. /// public readonly Parser<(string Value, Pos BeginPos, Pos EndPos, int BeginIndex, int EndIndex)> Identifier; /// /// The lexeme parser reserved(name) parses a symbol /// name, but it also checks that the name is not a prefix of a /// valid identifier. A reserved word is treated as a single token /// using 'try'. /// public readonly Func> Reserved; /// /// This lexeme parser parses a legal operator. Returns the name of the /// operator. This parser will fail on any operators that are reserved /// operators. Legal operator (start) characters and reserved operators /// are defined in the 'LanguageDef' that is passed to /// 'makeTokenParser'. An operator is treated as a /// single token using 'try'. /// public readonly Parser<(string Value, Pos BeginPos, Pos EndPos, int BeginIndex, int EndIndex)> Operator; /// /// The lexeme parser reservedOp name parses symbol /// name, but it also checks that the name is not a prefix of a /// valid operator. A reservedOp is treated as a single token using /// 'try'. /// public readonly Func> ReservedOp; /// /// This lexeme parser parses a single literal character. Returns the /// literal character value. This parsers deals correctly with escape /// sequences. The literal character is parsed according to the grammar /// rules defined in the Haskell report (which matches most programming /// languages quite closely). /// public readonly Parser<(char Value, Pos BeginPos, Pos EndPos, int BeginIndex, int EndIndex)> CharLiteral; /// /// This lexeme parser parses a literal string. Returns the literal /// string value. This parsers deals correctly with escape sequences and /// gaps. The literal string is parsed according to the grammar rules /// defined in the Haskell report (which matches most programming /// languages quite closely). /// public readonly Parser<(string Value, Pos BeginPos, Pos EndPos, int BeginIndex, int EndIndex)> StringLiteral; /// /// This lexeme parser parses a natural number (a positive whole /// number). Returns the value of the number. The number can be /// specified in 'decimal', 'hexadecimal' or /// 'octal'. The number is parsed according to the grammar /// rules in the Haskell report. /// public readonly Parser<(int Value, Pos BeginPos, Pos EndPos, int BeginIndex, int EndIndex)> Natural; /// /// This lexeme parser parses an integer (a whole number). This parser /// is like 'natural' except that it can be prefixed with /// sign (i.e. \'-\' or \'+\'). Returns the value of the number. The /// number can be specified in 'decimal', 'hexadecimal' /// or 'octal'. The number is parsed according /// to the grammar rules in the Haskell report. /// public readonly Parser<(int Value, Pos BeginPos, Pos EndPos, int BeginIndex, int EndIndex)> Integer; /// /// This lexeme parser parses a floating point value. Returns the value /// of the number. The number is parsed according to the grammar rules /// defined in the Haskell report. /// public readonly Parser<(double Value, Pos BeginPos, Pos EndPos, int BeginIndex, int EndIndex)> Float; /// /// This lexeme parser parses either 'natural' or a 'float'. /// Returns the value of the number. This parsers deals with /// any overlap in the grammar rules for naturals and floats. The number /// is parsed according to the grammar rules defined in the Haskell report. /// public readonly Parser<(Either Value, Pos BeginPos, Pos EndPos, int BeginIndex, int EndIndex)> NaturalOrFloat; /// /// Parses a positive whole number in the decimal system. Returns the /// value of the number. /// public readonly Parser<(int Value, Pos BeginPos, Pos EndPos, int BeginIndex, int EndIndex)> Decimal; /// /// Parses a positive whole number in the hexadecimal system. The number /// should be prefixed with "0x" or "0X". Returns the value of the /// number. /// public readonly Parser<(int Value, Pos BeginPos, Pos EndPos, int BeginIndex, int EndIndex)> Hexadecimal; /// /// Parses a positive whole number in the octal system. The number /// should be prefixed with "0o" or "0O". Returns the value of the /// number. /// public readonly Parser<(int Value, Pos BeginPos, Pos EndPos, int BeginIndex, int EndIndex)> Octal; /// /// Lexeme parser symbol(s) parses 'string' s and skips /// trailing white space. /// public readonly Func> Symbol; /// /// lexeme(p) first applies parser p and than the 'whiteSpace' /// parser, returning the value of p. Every lexical /// token (lexeme) is defined using lexeme, this way every parse /// starts at a point without white space. Parsers that use lexeme are /// called lexeme parsers in this document. /// /// The only point where the 'whiteSpace' parser should be /// called explicitly is the start of the main parser in order to skip /// any leading white space. /// public Parser<(A Value, Pos BeginPos, Pos EndPos, int BeginIndex, int EndIndex)> Lexeme(Parser p) => Token2.lexemeDef(WhiteSpace)(p); /// /// Parses any white space. White space consists of /zero/ or more /// occurrences of a 'space', a line comment or a block (multi /// line) comment. Block comments may be nested. How comments are /// started and ended is defined in the 'LanguageDef' /// that is passed to 'makeTokenParser'. /// public readonly Parser WhiteSpace; /// /// Lexeme parser parens(p) parses p enclosed in parenthesis, /// returning the value of p. /// public Parser<(A Value, Pos BeginPos, Pos EndPos, int BeginIndex, int EndIndex)> Parens(Parser<(A Value, Pos BeginPos, Pos EndPos, int BeginIndex, int EndIndex)> p) => from o in Symbol("(") from x in p from c in Symbol(")") select x; /// /// Lexeme parser braces(p) parses p enclosed in braces { and /// }, returning the value of p. /// public Parser<(A Value, Pos BeginPos, Pos EndPos, int BeginIndex, int EndIndex)> Braces(Parser<(A Value, Pos BeginPos, Pos EndPos, int BeginIndex, int EndIndex)> p) => from o in Symbol("{") from x in p from c in Symbol("}") select x; /// /// Lexeme parser angles(p) parses p enclosed in angle brackets〈 /// and 〉, returning the value of p. /// public Parser<(A Value, Pos BeginPos, Pos EndPos, int BeginIndex, int EndIndex)> Angles(Parser<(A Value, Pos BeginPos, Pos EndPos, int BeginIndex, int EndIndex)> p) => from o in Symbol("<") from x in p from c in Symbol(">") select x; /// /// Lexeme parser brackets(p) parses p enclosed in brackets [ /// and ], returning the value of p. /// public Parser<(A Value, Pos BeginPos, Pos EndPos, int BeginIndex, int EndIndex)> Brackets(Parser<(A Value, Pos BeginPos, Pos EndPos, int BeginIndex, int EndIndex)> p) => from o in Symbol("[") from x in p from c in Symbol("]") select x; /// /// Lexeme parser semi parses the character ; and skips any /// trailing white space. Returns the string ";". /// public readonly Parser<(string Value, Pos BeginPos, Pos EndPos, int BeginIndex, int EndIndex)> Semi; /// /// Lexeme parser comma parses the character , and skips any /// trailing white space. Returns the string ",". /// public readonly Parser<(string Value, Pos BeginPos, Pos EndPos, int BeginIndex, int EndIndex)> Comma; /// /// Lexeme parser colon parses the character : and skips any /// trailing white space. Returns the string ":". /// public readonly Parser<(string Value, Pos BeginPos, Pos EndPos, int BeginIndex, int EndIndex)> Colon; /// /// Lexeme parser dot parses the character . and skips any /// trailing white space. Returns the string ".". /// public readonly Parser<(string Value, Pos BeginPos, Pos EndPos, int BeginIndex, int EndIndex)> Dot; /// /// Lexeme parser semiSep(p) parses /zero/ or more occurrences of p /// separated by semi. Returns a list of values returned by /// p. /// public Parser<(Seq Value, Pos BeginPos, Pos EndPos, int BeginIndex, int EndIndex)> SepBy(Parser<(A Value, Pos BeginPos, Pos EndPos, int BeginIndex, int EndIndex)> p, Parser sep) => from bp in getPos from bi in getIndex from xs in sepBy(p, sep) select xs.IsEmpty ? (Seq(), bp, bp, bi, bi) : (xs.Map(static x => x.Value), xs.Head.ValueUnsafe()!.BeginPos, xs.Last.ValueUnsafe()!.EndPos, xs.Head.ValueUnsafe()!.BeginIndex, xs.Head.ValueUnsafe()!.EndIndex); /// /// Lexeme parser semiSep1(p) parses /one/ or more occurrences of p /// separated by 'semi'. Returns a list of values returned by p. /// public Parser<(Seq Value, Pos BeginPos, Pos EndPos, int BeginIndex, int EndIndex)> SepBy1(Parser<(A Value, Pos BeginPos, Pos EndPos, int BeginIndex, int EndIndex)> p, Parser sep) => sepBy1(p, sep) .Map(static xs => (xs.Map(static x => x.Value), xs.Head.ValueUnsafe().BeginPos, xs.Last.ValueUnsafe().EndPos, xs.Head.ValueUnsafe().BeginIndex, xs.Head.ValueUnsafe().EndIndex)); /// /// Lexeme parser semiSep(p) parses /zero/ or more occurrences of p /// separated by semi. Returns a list of values returned by /// p. /// public Parser<(Seq Value, Pos BeginPos, Pos EndPos, int BeginIndex, int EndIndex)> SemiSep(Parser<(A Value, Pos BeginPos, Pos EndPos, int BeginIndex, int EndIndex)> p) => SepBy(p, Semi); /// /// Lexeme parser semiSep1(p) parses /one/ or more occurrences of p /// separated by 'semi'. Returns a list of values returned by p. /// public Parser<(Seq Value, Pos BeginPos, Pos EndPos, int BeginIndex, int EndIndex)> SemiSep1(Parser<(A Value, Pos BeginPos, Pos EndPos, int BeginIndex, int EndIndex)> p) => SepBy1(p, Semi); /// /// Lexeme parser commaSep(p) parses /zero/ or more occurrences of /// p separated by 'comma'. Returns a list of values returned /// by p. /// public Parser<(Seq Value, Pos BeginPos, Pos EndPos, int BeginIndex, int EndIndex)> CommaSep(Parser<(A Value, Pos BeginPos, Pos EndPos, int BeginIndex, int EndIndex)> p) => SepBy(p, Comma); /// /// Lexeme parser commaSep1(p) parses /one/ or more occurrences of /// p separated by 'comma'. Returns a list of values returned /// by p. /// public Parser<(Seq Value, Pos BeginPos, Pos EndPos, int BeginIndex, int EndIndex)> CommaSep1(Parser<(A Value, Pos BeginPos, Pos EndPos, int BeginIndex, int EndIndex)> p) => SepBy1(p, Comma); public Parser<(Seq Value, Pos BeginPos, Pos EndPos, int BeginIndex, int EndIndex)> BracketsCommaSep1(Parser<(A Value, Pos BeginPos, Pos EndPos, int BeginIndex, int EndIndex)> p) => Brackets(SepBy1(p, Comma)); public Parser<(Seq Value, Pos BeginPos, Pos EndPos, int BeginIndex, int EndIndex)> BracketsCommaSep(Parser<(A Value, Pos BeginPos, Pos EndPos, int BeginIndex, int EndIndex)> p) => Brackets(SepBy(p, Comma)); public Parser<(Seq Value, Pos BeginPos, Pos EndPos, int BeginIndex, int EndIndex)> ParensCommaSep1(Parser<(A Value, Pos BeginPos, Pos EndPos, int BeginIndex, int EndIndex)> p) => Parens(SepBy1(p, Comma)); public Parser<(Seq Value, Pos BeginPos, Pos EndPos, int BeginIndex, int EndIndex)> ParensCommaSep(Parser<(A Value, Pos BeginPos, Pos EndPos, int BeginIndex, int EndIndex)> p) => Parens(SepBy(p, Comma)); public Parser<(Seq Value, Pos BeginPos, Pos EndPos, int BeginIndex, int EndIndex)> AnglesCommaSep1(Parser<(A Value, Pos BeginPos, Pos EndPos, int BeginIndex, int EndIndex)> p) => Angles(SepBy1(p, Comma)); public Parser<(Seq Value, Pos BeginPos, Pos EndPos, int BeginIndex, int EndIndex)> AnglesCommaSep(Parser<(A Value, Pos BeginPos, Pos EndPos, int BeginIndex, int EndIndex)> p) => Angles(SepBy(p, Comma)); public Parser<(Seq Value, Pos BeginPos, Pos EndPos, int BeginIndex, int EndIndex)> BracesCommaSep1(Parser<(A Value, Pos BeginPos, Pos EndPos, int BeginIndex, int EndIndex)> p) => Braces(SepBy1(p, Comma)); public Parser<(Seq Value, Pos BeginPos, Pos EndPos, int BeginIndex, int EndIndex)> BracesCommaSep(Parser<(A Value, Pos BeginPos, Pos EndPos, int BeginIndex, int EndIndex)> p) => Braces(SepBy(p, Comma)); public Parser<(Seq Value, Pos BeginPos, Pos EndPos, int BeginIndex, int EndIndex)> BracketsSemiSep1(Parser<(A Value, Pos BeginPos, Pos EndPos, int BeginIndex, int EndIndex)> p) => Brackets(SepBy1(p, Semi)); public Parser<(Seq Value, Pos BeginPos, Pos EndPos, int BeginIndex, int EndIndex)> BracketsSemiSep(Parser<(A Value, Pos BeginPos, Pos EndPos, int BeginIndex, int EndIndex)> p) => Brackets(SepBy(p, Semi)); public Parser<(Seq Value, Pos BeginPos, Pos EndPos, int BeginIndex, int EndIndex)> ParensSemiSep1(Parser<(A Value, Pos BeginPos, Pos EndPos, int BeginIndex, int EndIndex)> p) => Parens(SepBy1(p, Semi)); public Parser<(Seq Value, Pos BeginPos, Pos EndPos, int BeginIndex, int EndIndex)> ParensSemiSep(Parser<(A Value, Pos BeginPos, Pos EndPos, int BeginIndex, int EndIndex)> p) => Parens(SepBy(p, Semi)); public Parser<(Seq Value, Pos BeginPos, Pos EndPos, int BeginIndex, int EndIndex)> AnglesSemiSep1(Parser<(A Value, Pos BeginPos, Pos EndPos, int BeginIndex, int EndIndex)> p) => Angles(SepBy1(p, Semi)); public Parser<(Seq Value, Pos BeginPos, Pos EndPos, int BeginIndex, int EndIndex)> AnglesSemiSep(Parser<(A Value, Pos BeginPos, Pos EndPos, int BeginIndex, int EndIndex)> p) => Angles(SepBy(p, Semi)); public Parser<(Seq Value, Pos BeginPos, Pos EndPos, int BeginIndex, int EndIndex)> BracesSemiSep1(Parser<(A Value, Pos BeginPos, Pos EndPos, int BeginIndex, int EndIndex)> p) => Braces(SepBy1(p, Semi)); public Parser<(Seq Value, Pos BeginPos, Pos EndPos, int BeginIndex, int EndIndex)> BracesSemiSep(Parser<(A Value, Pos BeginPos, Pos EndPos, int BeginIndex, int EndIndex)> p) => Braces(SepBy(p, Semi)); internal GenTokenParser2( Parser<(string Value, Pos BeginPos, Pos EndPos, int BeginIndex, int EndIndex)> indentifier, Func> reserved, Parser<(string Value, Pos BeginPos, Pos EndPos, int BeginIndex, int EndIndex)> op, Func> reservedOp, Parser<(char Value, Pos BeginPos, Pos EndPos, int BeginIndex, int EndIndex)> charLiteral, Parser<(string Value, Pos BeginPos, Pos EndPos, int BeginIndex, int EndIndex)> stringLiteral, Parser<(int Value, Pos BeginPos, Pos EndPos, int BeginIndex, int EndIndex)> natural, Parser<(int Value, Pos BeginPos, Pos EndPos, int BeginIndex, int EndIndex)> integer, Parser<(double Value, Pos BeginPos, Pos EndPos, int BeginIndex, int EndIndex)> floating, Parser<(Either Value, Pos BeginPos, Pos EndPos, int BeginIndex, int EndIndex)> naturalOrFloat, Parser<(int Value, Pos BeginPos, Pos EndPos, int BeginIndex, int EndIndex)> dec, Parser<(int Value, Pos BeginPos, Pos EndPos, int BeginIndex, int EndIndex)> hexadecimal, Parser<(int Value, Pos BeginPos, Pos EndPos, int BeginIndex, int EndIndex)> octal, Func> symbol, Parser whiteSpace, Parser<(string Value, Pos BeginPos, Pos EndPos, int BeginIndex, int EndIndex)> semi, Parser<(string Value, Pos BeginPos, Pos EndPos, int BeginIndex, int EndIndex)> comma, Parser<(string Value, Pos BeginPos, Pos EndPos, int BeginIndex, int EndIndex)> colon, Parser<(string Value, Pos BeginPos, Pos EndPos, int BeginIndex, int EndIndex)> dot ) { Identifier = indentifier; this.Reserved = reserved; this.Operator = op; this.ReservedOp = reservedOp; this.CharLiteral = charLiteral; this.StringLiteral = stringLiteral; this.Natural = natural; this.Integer = integer; this.Float = floating; this.NaturalOrFloat = naturalOrFloat; this.Decimal = dec; Hexadecimal = hexadecimal; Octal = octal; this.Symbol = symbol; this.WhiteSpace = whiteSpace; this.Semi = semi; this.Comma = comma; this.Colon = colon; this.Dot = dot; } } ================================================ FILE: LanguageExt.Parsec/Language.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using static LanguageExt.Prelude; using static LanguageExt.Parsec.Prim; using static LanguageExt.Parsec.Char; using static LanguageExt.Parsec.Expr; using static LanguageExt.Parsec.Token; namespace LanguageExt.Parsec { public static class Language { /// /// This is a minimal token definition for Haskell style languages. It /// defines the style of comments, valid identifiers and case /// sensitivity. It does not define any reserved words or operators. /// public readonly static GenLanguageDef HaskellStyle = GenLanguageDef.Empty.With( CommentStart: "{-", CommentEnd: "-}", CommentLine: "--", NestedComments: true, IdentStart: letter, IdentLetter: either(alphaNum, oneOf("_'")), OpStart: oneOf(":!#$%&*+./<=>?@\\^|-~"), OpLetter: oneOf(":!#$%&*+./<=>?@\\^|-~"), ReservedOpNames: List(), ReservedNames: List(), CaseSensitive: true ); /// /// This is a minimal token definition for Java style languages. It /// defines the style of comments, valid identifiers and case /// sensitivity. It does not define any reserved words. /// public readonly static GenLanguageDef JavaStyle = GenLanguageDef.Empty.With( CommentStart: "/*", CommentEnd: "*/", CommentLine: "//", NestedComments: true, IdentStart: letter, IdentLetter: either(alphaNum, oneOf("_'")), OpStart: oneOf(@"!%&*+.<=>?@\^|-~"), OpLetter: oneOf(@"!%&*+.<=>?@\^|-~"), ReservedOpNames: List(), ReservedNames: List(), CaseSensitive: true ); /// /// The language definition for the language Haskell98. /// public readonly static GenLanguageDef Haskell98Def = HaskellStyle.With( ReservedOpNames: List.create("::", "..", "=", "\\", "|", "<-", "->", "@", "~", "=>"), ReservedNames: List.create( "let", "in", "case", "of", "if", "then", "else", "data", "type", "class", "default", "deriving", "do", "import", "infix", "infixl", "infixr", "instance", "module", "newtype", "where", "primitive")); } } ================================================ FILE: LanguageExt.Parsec/LanguageExt.Parsec.csproj ================================================ TRACE;DEBUG 1701;1702;1705;IDE1006;CS1591;CS1573;CS1712;CS1711;CS1572;CS1587 CONTRACTS_FULL enable net10.0 5.0.0-beta-77 LanguageExt.Parsec LanguageExt.Parsec Paul Louth Functional language extensions for C# Copyright (c) Paul Louth. All rights reserved. README.nuget.md Parser combinators library based on Haskell Parsec. This is part of the LanguageExt functional framework and requires LanguageExt.Core C#, Functional, Language Extension, Monad, Option, Either, Reader, Writer, State, List, Set, Map, Queue, Memo, Memoization, Immutable, Lambda, Pattern Matching, Tuple lang-ext-small.png https://github.com/louthy/language-ext MIT false bin\$(Configuration)\$(TargetFramework)\$(AssemblyName).xml library 5.0.0.0 5.0.0.0 default ================================================ FILE: LanguageExt.Parsec/OperatorIOs.cs ================================================ using System; namespace LanguageExt.Parsec { public static partial class Operator { public static Operator Infix(Assoc assoc, Parser> p) => new InfixOp(assoc, p); public static Operator Prefix(Parser> p) => new PrefixOp(p); public static Operator Postfix(Parser> p) => new PostfixOp(p); } public abstract class Operator { public readonly OperatorTag Tag; public Operator(OperatorTag tag) { Tag = tag; } public abstract (Seq>>, Seq>>, Seq>>, Seq>>, Seq>>) SplitOp( (Seq>>, Seq>>, Seq>>, Seq>>, Seq>>) state); } public class InfixOp : Operator { public readonly Assoc Assoc; public readonly Parser> Op; internal InfixOp(Assoc assoc, Parser> p) : base(OperatorTag.Infix) { Assoc = assoc; Op = p; } public override (Seq>>, Seq>>, Seq>>, Seq>>, Seq>>) SplitOp( (Seq>>, Seq>>, Seq>>, Seq>>, Seq>>) state) => state.Map( (rassoc, lassoc, nassoc, prefix, postfix) => Assoc == Assoc.None ? (rassoc, lassoc, Op.Cons(nassoc), prefix, postfix) : Assoc == Assoc.Left ? (rassoc, Op.Cons(lassoc), nassoc, prefix, postfix) : (Op.Cons(rassoc), lassoc, nassoc, prefix, postfix)); } public class PrefixOp : Operator { public readonly Parser> Op; internal PrefixOp(Parser> p) : base(OperatorTag.Prefix) { Op = p; } public override (Seq>>, Seq>>, Seq>>, Seq>>, Seq>>) SplitOp( (Seq>>, Seq>>, Seq>>, Seq>>, Seq>>) state) => state.Map( (rassoc, lassoc, nassoc, prefix, postfix) => (rassoc, lassoc, nassoc, Op.Cons(prefix), postfix)); } public class PostfixOp : Operator { public readonly Parser> Op; internal PostfixOp(Parser> p) : base(OperatorTag.Postfix) { Op = p; } public override (Seq>>, Seq>>, Seq>>, Seq>>, Seq>>) SplitOp( (Seq>>, Seq>>, Seq>>, Seq>>, Seq>>) state) => state.Map( (rassoc, lassoc, nassoc, prefix, postfix) => (rassoc, lassoc, nassoc, prefix, Op.Cons(postfix))); } } ================================================ FILE: LanguageExt.Parsec/Operators.cs ================================================ using System; using LanguageExt; using static LanguageExt.Prelude; namespace LanguageExt.Parsec { public enum Assoc { None, Left, Right } public enum OperatorTag { Infix, Prefix, Postfix } public static partial class Operator { public static Operator Infix(Assoc assoc, Parser> p) => new InfixOp(assoc, p); public static Operator Prefix(Parser> p) => new PrefixOp(p); public static Operator Postfix(Parser> p) => new PostfixOp(p); } public abstract class Operator { public readonly OperatorTag Tag; public Operator(OperatorTag tag) { Tag = tag; } public abstract (Seq>>, Seq>>, Seq>>, Seq>>, Seq>>) SplitOp( (Seq>>, Seq>>, Seq>>, Seq>>, Seq>>) state); } public class InfixOp : Operator { public readonly Assoc Assoc; public readonly Parser> Op; internal InfixOp(Assoc assoc, Parser> p) : base(OperatorTag.Infix) { Assoc = assoc; Op = p; } public override (Seq>>, Seq>>, Seq>>, Seq>>, Seq>>) SplitOp( (Seq>>, Seq>>, Seq>>, Seq>>, Seq>>) state) => state.Map( (rassoc, lassoc, nassoc, prefix, postfix) => Assoc == Assoc.None ? (rassoc, lassoc, Op.Cons(nassoc), prefix, postfix) : Assoc == Assoc.Left ? (rassoc, Op.Cons(lassoc), nassoc, prefix, postfix) : (Op.Cons(rassoc), lassoc, nassoc, prefix, postfix)); } public class PrefixOp : Operator { public readonly Parser> Op; internal PrefixOp(Parser> p) : base(OperatorTag.Prefix) { Op = p; } public override (Seq>>, Seq>>, Seq>>, Seq>>, Seq>>) SplitOp( (Seq>>, Seq>>, Seq>>, Seq>>, Seq>>) state) => state.Map( (rassoc, lassoc, nassoc, prefix, postfix) => (rassoc, lassoc, nassoc, Op.Cons(prefix), postfix)); } public class PostfixOp : Operator { public readonly Parser> Op; internal PostfixOp(Parser> p) : base(OperatorTag.Postfix) { Op = p; } public override (Seq>>, Seq>>, Seq>>, Seq>>, Seq>>) SplitOp( (Seq>>, Seq>>, Seq>>, Seq>>, Seq>>) state) => state.Map( (rassoc, lassoc, nassoc, prefix, postfix) => (rassoc, lassoc, nassoc, prefix, Op.Cons(postfix))); } } ================================================ FILE: LanguageExt.Parsec/PString.cs ================================================ using System; using static LanguageExt.Prelude; namespace LanguageExt.Parsec { /// /// Represents the parser source text and the parser's /// positional state. /// public class PString { public readonly string Value; public readonly int Index; public readonly int EndIndex; public readonly Pos Pos; public readonly Pos DefPos; public readonly Sidedness Side; public readonly Option UserState; public PString(string value, int index, int endIndex, Pos pos, Pos defPos, Sidedness side, Option userState) { Value = value ?? throw new ArgumentNullException(nameof(value)); Index = index; EndIndex = endIndex; Pos = pos; DefPos = defPos; Side = side; UserState = userState; } public PString SetDefPos(Pos defpos) => new PString(Value, Index, EndIndex, Pos, defpos, Side, UserState); public PString SetPos(Pos pos) => new PString(Value, Index, EndIndex, pos, DefPos, Side, UserState); public PString SetSide(Sidedness side) => new PString(Value, Index, EndIndex, Pos, DefPos, side, UserState); public PString SetValue(string value) => new PString(value, Index, value.Length, Pos, DefPos, Side, UserState); public PString SetIndex(int index) => new PString(Value, index, EndIndex, Pos, DefPos, Side, UserState); public PString SetUserState(object? state) => new PString(Value, Index, EndIndex, Pos, DefPos, Side, state); public PString SetEndIndex(int endIndex) => new PString(Value, Index, endIndex, Pos, DefPos, Side, UserState); public override string ToString() => Value.Substring(Index, EndIndex - Index); public static readonly PString Zero = new PString("", 0, 0, Pos.Zero, Pos.Zero, Sidedness.Onside, None); } } ================================================ FILE: LanguageExt.Parsec/PStringIO.cs ================================================ using System; using System.Linq; using static LanguageExt.Prelude; namespace LanguageExt.Parsec { /// /// Represents the parser source string and the parser's /// positional state. /// public class PString { public readonly T[] Value; public readonly int Index; public readonly int EndIndex; public readonly Option UserState; public readonly Func TokenPos; public PString(T[] value, int index, int endIndex, Option userState, Func tokenPos) { Value = value ?? throw new ArgumentNullException(nameof(value)); Index = index; EndIndex = endIndex; UserState = userState; TokenPos = tokenPos; } public Pos Pos => Value.Length == 0 ? Pos.Zero : Index < Value.Length ? TokenPos(Value[Index]) : TokenPos(Value[Value.Length - 1]); public PString SetValue(T[] value) => new PString(value, Index, value.Length, UserState, TokenPos); public PString SetIndex(int index) => new PString(Value, index, EndIndex, UserState, TokenPos); public PString SetUserState(object? state) => new PString(Value, Index, EndIndex, state, TokenPos); public PString SetEndIndex(int endIndex) => new PString(Value, Index, endIndex, UserState, TokenPos); public override string ToString() => $"{typeof(T).Name}({Index}, {EndIndex})"; public static PString Zero(Func tokenPos) => new PString(System.Array.Empty(), 0, 0, None, tokenPos); public PString Cast() where U : T => new PString(Value.Cast().ToArray(), Index, EndIndex, UserState, u => TokenPos((T)u)); } } ================================================ FILE: LanguageExt.Parsec/Parsec.Internal.cs ================================================ using System.Collections.Generic; using LanguageExt.UnsafeValueAccess; using static LanguageExt.Prelude; using static LanguageExt.Parsec.Common; using static LanguageExt.Parsec.Prim; using static LanguageExt.Parsec.ParserResult; namespace LanguageExt.Parsec; static class Internal { public static ParserResult newstate(PString inp) { var x = inp.Value[inp.Index]; var newpos = x == '\n' ? new Pos(inp.Pos.Line + 1, 0) : x == '\t' ? new Pos(inp.Pos.Line, ((inp.Pos.Column / 4) + 1) * 4) : new Pos(inp.Pos.Line, inp.Pos.Column + 1); return ConsumedOK(x, new PString( inp.Value, inp.Index + 1, inp.EndIndex, newpos, inp.DefPos, onside(newpos, inp.DefPos) ? Sidedness.Onside : Sidedness.Offside, inp.UserState)); } /// /// Imperative implementation of the choice parser, which in a non-stack /// overflow utopia would look similar to this: /// /// either(ps[index], choicei(ps, index + 1)) /// /// public static Parser choicei(Seq> ps) => ps.IsEmpty ? unexpected("choice parser with no choices") : inp => { List results = new List(); ParserError? error = null; foreach (var p in ps) { var t = p(inp); // cok if (t.Tag == ResultTag.Consumed && t.Reply.Tag == ReplyTag.OK) { return t; } // eok if (t.Tag == ResultTag.Empty && t.Reply.Tag == ReplyTag.OK) { return EmptyOK(t.Reply.Result, t.Reply.State, mergeError(error, t.Reply.Error)); } // cerr if (t.Tag == ResultTag.Consumed && t.Reply.Tag == ReplyTag.Error) { return ConsumedError(mergeError(error, t.Reply.Error)); } error = mergeError(error, t.Reply.Error); } return EmptyError(error); }; /// /// Imperative implementation of chain, which in a non-stack overflow utopia /// would look similar to this: /// /// from x in ps[index] /// from y in chaini(ps, index + 1) /// select x.Cons(y); /// /// public static Parser> chaini(Seq> ps) => ps.IsEmpty ? unexpected>("chain parser with 0 items") : inp => { if( ps.Count == 1) { return ps.Head.ValueUnsafe()!.Map(x => x.Cons())(inp); } var current = inp; List results = new List(); ParserError error = null; ParserResult last = null; int count = ps.Count; foreach (var p in ps) { count--; var t = p(current); if( last == null) { // cerr if (t.Tag == ResultTag.Consumed && t.Reply.Tag == ReplyTag.Error) { return ConsumedError>(t.Reply.Error); } // eerr else if (t.Tag == ResultTag.Empty && t.Reply.Tag == ReplyTag.Error) { return EmptyError>(t.Reply.Error); } // cok else if (t.Tag == ResultTag.Consumed && t.Reply.Tag == ReplyTag.OK) { results.Add(t.Reply.Result); last = t; error = t.Reply.Error; current = t.Reply.State; } // eok else //if (t.Tag == ResultTag.Empty && t.Reply.Tag == ReplyTag.OK) { results.Add(t.Reply.Result); last = t; error = t.Reply.Error; } } else { if (last.Tag == ResultTag.Consumed) { // cok, cerr if (t.Tag == ResultTag.Consumed && t.Reply.Tag == ReplyTag.Error) { return ConsumedError>(t.Reply.Error); } // cok, eerr else if (t.Tag == ResultTag.Empty && t.Reply.Tag == ReplyTag.Error) { return ConsumedError>(mergeError(error, t.Reply.Error)); } // cok, cok else if (t.Tag == ResultTag.Consumed && t.Reply.Tag == ReplyTag.OK) { if (count == 0) { results.Add(t.Reply.Result); return ConsumedOK(toSeq(results), t.Reply.State, t.Reply.Error); } else { results.Add(t.Reply.Result); last = t; error = t.Reply.Error; current = t.Reply.State; } } // cok, eok else //if (t.Tag == ResultTag.Empty && t.Reply.Tag == ReplyTag.OK) { if (count == 0) { // cok, eok -> cok (not a typo, this should be -> cok) results.Add(t.Reply.Result); return ConsumedOK(toSeq(results), t.Reply.State, mergeError(error, t.Reply.Error)); } else { results.Add(t.Reply.Result); last = t; error = mergeError(error, t.Reply.Error); } } } else if (last.Tag == ResultTag.Empty) { // eok, cerr if (t.Tag == ResultTag.Consumed && t.Reply.Tag == ReplyTag.Error) { return ConsumedError>(t.Reply.Error); } // eok, eerr else if (t.Tag == ResultTag.Empty && t.Reply.Tag == ReplyTag.Error) { return EmptyError>(mergeError(error, t.Reply.Error)); } // eok, cok else if (t.Tag == ResultTag.Consumed && t.Reply.Tag == ReplyTag.OK) { if (count == 0) { results.Add(t.Reply.Result); return ConsumedOK(toSeq(results), t.Reply.State, t.Reply.Error); } else { results.Add(t.Reply.Result); last = t; error = t.Reply.Error; current = t.Reply.State; } } // eok, eok else //if (t.Tag == ResultTag.Empty && t.Reply.Tag == ReplyTag.OK) { if (count == 0) { results.Add(t.Reply.Result); return EmptyOK(toSeq(results), t.Reply.State, mergeError(error, t.Reply.Error)); } else { results.Add(t.Reply.Result); last = t; error = mergeError(error, t.Reply.Error); } } } } } return ConsumedOK(toSeq(results), current, error); }; public static Parser> counti(int n, Parser p) => n <= 0 ? result(Seq.Empty) : from x in p from y in counti(n -1, p) select x.Cons(y); } ================================================ FILE: LanguageExt.Parsec/ParsecIO.Internal.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using LanguageExt.UnsafeValueAccess; using static LanguageExt.Prelude; using static LanguageExt.Parsec.Common; using static LanguageExt.Parsec.PrimIO; using static LanguageExt.Parsec.ParserResultIO; namespace LanguageExt.Parsec { static class InternalIO { public static ParserResult newstate(PString inp) { var x = inp.Value[inp.Index]; return ConsumedOK(x, new PString( inp.Value, inp.Index + 1, inp.EndIndex, inp.UserState, inp.TokenPos)); } /// /// Imperative implementation of the choice parser, which in a non-stack /// overflow utopia would look similar to this: /// /// either(ps[index], choicei(ps, index + 1)) /// /// public static Parser choicei(Seq> ps) => ps.IsEmpty ? unexpected("choice parser with no choices") : inp => { var results = new List(); ParserError? error = null; foreach (var p in ps) { var t = p(inp); // cok if (t is { Tag: ResultTag.Consumed, Reply.Tag: ReplyTag.OK }) { return t; } // eok if (t.Tag == ResultTag.Empty && t.Reply.Tag == ReplyTag.OK) { return EmptyOK(t.Reply.Result!, t.Reply.State, mergeError(error, t.Reply.Error)); } // cerr if (t.Tag == ResultTag.Consumed && t.Reply.Tag == ReplyTag.Error) { return ConsumedError(mergeError(error, t.Reply.Error), inp.TokenPos); } error = mergeError(error, t.Reply.Error); } return EmptyError(error!, inp.TokenPos); }; /// /// Imperative implementation of chain, which in a non-stack overflow utopia /// would look similar to this: /// /// from x in ps[index] /// from y in chaini(ps, index + 1) /// select x.Cons(y); /// /// public static Parser> chaini(Seq> ps) => ps.IsEmpty ? unexpected>("chain parser with 0 items") : inp => { if( ps.Count == 1) { return ps.Head.ValueUnsafe()!.Map(x => x.Cons())(inp); } var current = inp; var results = new List(); ParserError error = null; ParserResult last = null; int count = ps.Count; foreach (var p in ps) { count--; var t = p(current); if( last == null) { // cerr if (t.Tag == ResultTag.Consumed && t.Reply.Tag == ReplyTag.Error) { return ConsumedError>(t.Reply.Error, inp.TokenPos); } // eerr else if (t.Tag == ResultTag.Empty && t.Reply.Tag == ReplyTag.Error) { return EmptyError>(t.Reply.Error, inp.TokenPos); } // cok else if (t.Tag == ResultTag.Consumed && t.Reply.Tag == ReplyTag.OK) { results.Add(t.Reply.Result); last = t; error = t.Reply.Error; current = t.Reply.State; } // eok else //if (t.Tag == ResultTag.Empty && t.Reply.Tag == ReplyTag.OK) { results.Add(t.Reply.Result); last = t; error = t.Reply.Error; } } else { if (last.Tag == ResultTag.Consumed) { // cok, cerr if (t.Tag == ResultTag.Consumed && t.Reply.Tag == ReplyTag.Error) { return ConsumedError>(t.Reply.Error, inp.TokenPos); } // cok, eerr else if (t.Tag == ResultTag.Empty && t.Reply.Tag == ReplyTag.Error) { return ConsumedError>(mergeError(error, t.Reply.Error), inp.TokenPos); } // cok, cok else if (t.Tag == ResultTag.Consumed && t.Reply.Tag == ReplyTag.OK) { if (count == 0) { results.Add(t.Reply.Result); return ConsumedOK(toSeq(results), t.Reply.State, t.Reply.Error); } else { results.Add(t.Reply.Result); last = t; error = t.Reply.Error; current = t.Reply.State; } } // cok, eok else //if (t.Tag == ResultTag.Empty && t.Reply.Tag == ReplyTag.OK) { if (count == 0) { // cok, eok -> cok (not a typo, this should be -> cok) results.Add(t.Reply.Result); return ConsumedOK(toSeq(results), t.Reply.State, mergeError(error, t.Reply.Error)); } else { results.Add(t.Reply.Result); last = t; error = mergeError(error, t.Reply.Error); } } } else if (last.Tag == ResultTag.Empty) { // eok, cerr if (t.Tag == ResultTag.Consumed && t.Reply.Tag == ReplyTag.Error) { return ConsumedError>(t.Reply.Error, inp.TokenPos); } // eok, eerr else if (t.Tag == ResultTag.Empty && t.Reply.Tag == ReplyTag.Error) { return EmptyError>(mergeError(error, t.Reply.Error), inp.TokenPos); } // eok, cok else if (t.Tag == ResultTag.Consumed && t.Reply.Tag == ReplyTag.OK) { if (count == 0) { results.Add(t.Reply.Result); return ConsumedOK(toSeq(results), t.Reply.State, t.Reply.Error); } else { results.Add(t.Reply.Result); last = t; error = t.Reply.Error; current = t.Reply.State; } } // eok, eok else //if (t.Tag == ResultTag.Empty && t.Reply.Tag == ReplyTag.OK) { if (count == 0) { results.Add(t.Reply.Result); return EmptyOK(toSeq(results), t.Reply.State, mergeError(error, t.Reply.Error)); } else { results.Add(t.Reply.Result); last = t; error = mergeError(error, t.Reply.Error); } } } } } return ConsumedOK(toSeq(results), current, error); }; public static Parser> counti(int n, Parser p) => n <= 0 ? result>(Seq.Empty) : from x in p from y in counti(n-1, p) select x.Cons(y); } } ================================================ FILE: LanguageExt.Parsec/Parser.cs ================================================ using System; using LanguageExt; using LanguageExt.Parsec; using static LanguageExt.Prelude; using static LanguageExt.Parsec.Prim; using static LanguageExt.Parsec.Common; using static LanguageExt.Parsec.ParserResult; using static LanguageExt.Parsec.ParserResultIO; using System.Diagnostics; namespace LanguageExt.Parsec { /// /// Parser delegate type - Parses an input PString and returns a ParserResult /// /// Parsed value result type /// Input string /// Parsed value or error report public delegate ParserResult Parser(PString input); } public static class ParserExtensions { /// /// A label for the parser /// /// What was expected public static Parser label(this Parser p, string expected) => inp => { var res = p(inp); if (res.Tag == ResultTag.Consumed) { return res; } if (res.Reply.Tag == ReplyTag.Error) { return EmptyError(ParserError.Expect(inp.Pos, res.Reply.Error?.Msg ?? "", expected)); } if (res.Reply.Error is null || res.Reply.Error.Tag == ParserErrorTag.Unknown) { return res; } else { return EmptyOK(res.Reply.Result!, res.Reply.State!, ParserError.Expect(inp.Pos, res.Reply.Error.Msg, expected)); } }; public static ParserResult Parse(this Parser self, PString input) => self(input); public static ParserResult Parse(this Parser self, string input) => self(PString.Zero.SetValue(input)); public static Parser Filter(this Parser self, Func pred) => self.Where(pred); public static Parser Where(this Parser self, Func pred) => inp => self(inp).Match( EmptyOK: (x, rem, msg) => pred(x) ? EmptyOK(x, rem, msg) : EmptyError(ParserError.SysUnexpect(inp.Pos, $"\"{x}\"")), EmptyError: EmptyError, ConsumedOK: (x, rem, msg) => pred(x) ? ConsumedOK(x, rem, msg) : EmptyError(ParserError.SysUnexpect(inp.Pos, $"\"{x}\"")), ConsumedError: ConsumedError); public static Parser Map(this Parser self, Func map) => self.Select(map); public static Parser Select(this Parser self, Func map) => inp => self(inp).Select(map); public static Parser Bind(this Parser self, Func> f) => self.SelectMany(f); public static Parser SelectMany( this Parser self, Func> f) => inp => { Debug.Assert(inp != null); var t = self(inp); // cok if (t.Tag == ResultTag.Consumed && t.Reply.Tag == ReplyTag.OK) { return f(t.Reply.Result)(t.Reply.State); } // eok if (t.Tag == ResultTag.Empty && t.Reply.Tag == ReplyTag.OK) { return f(t.Reply.Result)(t.Reply.State); } // cerr if (t.Tag == ResultTag.Consumed && t.Reply.Tag == ReplyTag.Error) { return ConsumedError(t.Reply.Error); } // eerr return EmptyError(t.Reply.Error); }; public static Parser SelectMany( this Parser self, Func> bind, Func project) => inp => { Debug.Assert(inp != null); var t = self(inp); // cok if (t.Tag == ResultTag.Consumed && t.Reply.Tag == ReplyTag.OK) { var u = bind(t.Reply.Result)(t.Reply.State); if (u.Tag == ResultTag.Consumed && u.Reply.Tag == ReplyTag.OK) { // cok, cok -> cok var v = project(t.Reply.Result, u.Reply.Result); return ConsumedOK(v, u.Reply.State, u.Reply.Error); } if (u.Tag == ResultTag.Consumed && u.Reply.Tag == ReplyTag.Error) { // cok, cerr -> cerr return ConsumedError(u.Reply.Error); } if (u.Tag == ResultTag.Empty && u.Reply.Tag == ReplyTag.OK) { // cok, eok -> cok (not a typo, this should be -> cok) var v = project(t.Reply.Result, u.Reply.Result); return ConsumedOK(v, u.Reply.State, mergeError(t.Reply.Error, u.Reply.Error)); } // cok, eerr return ConsumedError(mergeError(t.Reply.Error, u.Reply.Error)); } // eok if (t.Tag == ResultTag.Empty && t.Reply.Tag == ReplyTag.OK) { var u = bind(t.Reply.Result)(t.Reply.State); if (u.Tag == ResultTag.Consumed && u.Reply.Tag == ReplyTag.OK) { // eok, cok -> cok var v = project(t.Reply.Result, u.Reply.Result); return ConsumedOK(v, u.Reply.State, u.Reply.Error); } if (u.Tag == ResultTag.Empty && u.Reply.Tag == ReplyTag.OK) { // eok, eok -> eok var v = project(t.Reply.Result, u.Reply.Result); return EmptyOK(v, u.Reply.State, mergeError(t.Reply.Error, u.Reply.Error)); } if (u.Tag == ResultTag.Consumed && u.Reply.Tag == ReplyTag.Error) { // eok, cerr -> cerr return ConsumedError(u.Reply.Error); } // eok, eerr return EmptyError(mergeError(t.Reply.Error, u.Reply.Error)); } // cerr if (t.Tag == ResultTag.Consumed && t.Reply.Tag == ReplyTag.Error) { return ConsumedError(t.Reply.Error); } // eerr return EmptyError(t.Reply.Error); }; public static Parser Flatten(this Parser> mma) => mma.Bind(identity); public static Parser Flatten(this Parser> p, Func failureText) => from value in p from returnValue in value.Match(result, compose(failureText, failure)) select returnValue; public static Parser Flatten(this Parser> p, Func failureText) => from value in p from returnValue in value.Match(compose(failureText, failure), result) select returnValue; public static Parser Flatten(this Parser> p) => Flatten(p, identity); } ================================================ FILE: LanguageExt.Parsec/ParserError.cs ================================================ using System; using System.Linq; using LanguageExt.UnsafeValueAccess; using static LanguageExt.Prelude; namespace LanguageExt.Parsec; public enum ParserErrorTag { Unknown = 0, // numbered because the SysUnexpect = 1, // order is important Unexpect = 2, Expect = 3, Message = 4 } public class ParserError : IEquatable, IComparable { public readonly ParserErrorTag Tag; public readonly Pos Pos; public readonly string Msg; public readonly Lst Expected; public readonly ParserError? Inner; public ParserError(ParserErrorTag tag, Pos pos, string message, Lst expected, ParserError? inner = null) { Tag = tag; Pos = pos; Msg = message; Expected = expected; Inner = inner; } public static ParserError Unknown(Pos pos) => new ParserError(ParserErrorTag.Unknown, pos, "", List.empty()); public static ParserError SysUnexpect(Pos pos, string message) => new ParserError(ParserErrorTag.SysUnexpect, pos, message, List.empty()); public static ParserError Unexpect(Pos pos, string message) => new ParserError(ParserErrorTag.Unexpect, pos, message, List.empty()); public static ParserError Expect(Pos pos, string message, string expected) => new ParserError(ParserErrorTag.Expect, pos, message, List.create(expected)); public static ParserError Message(Pos pos, string message) => new ParserError(ParserErrorTag.Message, pos, message, List.empty()); private static string FormatExpects(Lst expects) => expects.Count == 0 ? "" : expects.Count == 1 ? $"expecting {expects.Head.ValueUnsafe()}" : $"expecting {string.Join(", ", expects.Take(expects.Count - 1))} or {expects.Last.ValueUnsafe()}"; public override string ToString() => $"error at {Pos}: {ToStringNoPosition()}"; public string ToStringNoPosition() => (Tag == ParserErrorTag.Unexpect ? $"unexpected {Msg}" : Tag == ParserErrorTag.SysUnexpect ? $"unexpected {Msg}" : Tag == ParserErrorTag.Message ? Msg : Tag == ParserErrorTag.Expect ? $"unexpected {Msg}, {FormatExpects(Expected.Filter(x => !string.IsNullOrEmpty(x)).Distinct().AsIterable().ToLst())}" : "unknown error"); public bool Equals(ParserError? other) => other is not null && Tag == other.Tag && Msg == other.Msg; public override bool Equals(object? obj) => ((obj as ParserError)?.Equals(this)).GetValueOrDefault(); public override int GetHashCode() => (Tag,Pos,Msg).GetHashCode(); public int CompareTo(ParserError? other) => Tag.CompareTo(other?.Tag); public static bool operator ==(ParserError lhs, ParserError rhs) => isnull(lhs) && isnull(rhs) || (!isnull(lhs) && !isnull(rhs) && !lhs.Equals(rhs)); public static bool operator !=(ParserError lhs, ParserError rhs) => !(lhs == rhs); public static bool operator <(ParserError lhs, ParserError rhs) => lhs.CompareTo(rhs) < 0; public static bool operator >(ParserError lhs, ParserError rhs) => lhs.CompareTo(rhs) > 0; public static bool operator <=(ParserError lhs, ParserError rhs) => lhs.CompareTo(rhs) <= 0; public static bool operator >=(ParserError lhs, ParserError rhs) => lhs.CompareTo(rhs) >= 0; public static R Compare( ParserError lhs, ParserError rhs, Func EQ, Func GT, Func LT ) { var res = lhs.CompareTo(rhs); if (res < 0) { return LT(); } if (res > 0) { return GT(); } return EQ(); } } ================================================ FILE: LanguageExt.Parsec/ParserIO.cs ================================================ using System; using System.Linq; using System.Collections.Generic; using LanguageExt; using LanguageExt.Parsec; using static LanguageExt.Prelude; using static LanguageExt.Parsec.Prim; using static LanguageExt.Parsec.Common; using static LanguageExt.Parsec.ParserResultIO; using System.Diagnostics; namespace LanguageExt.Parsec { /// /// Parser delegate type - Parses an input PString and returns a ParserResult /// /// Input stream element type /// Parsed value result type /// Input string /// Parsed value or error report public delegate ParserResult Parser(PString input); } public static class ParserIOExtensions { /// /// A label for the parser /// /// What was expected public static Parser label(this Parser p, string expected) => inp => { var res = p(inp); if (res.Tag == ResultTag.Consumed) { return res; } if (res.Reply.Tag == ReplyTag.Error) { return EmptyError(ParserError.Expect(inp.Pos, res.Reply.Error?.Msg ?? "", expected), inp.TokenPos); } if (res.Reply.Error is null || res.Reply.Error.Tag == ParserErrorTag.Unknown) { return res; } else { return EmptyOK(res.Reply.Result!, res.Reply.State, ParserError.Expect(inp.Pos, res.Reply.Error.Msg, expected)); } }; public static ParserResult Parse(this Parser self, PString input) => self(input); public static ParserResult Parse(this Parser self, Seq input, Func tokenPos) => self(PString.Zero(tokenPos).SetValue(input.ToArray())); public static ParserResult Parse(this Parser self, IEnumerable input, Func tokenPos) => self(PString.Zero(tokenPos).SetValue(input.ToArray())); public static Parser Filter(this Parser self, Func pred) => self.Where(pred); public static Parser Where(this Parser self, Func pred) => inp => self(inp).Match( EmptyOK: (x, rem, msg) => pred(x) ? EmptyOK(x, rem, msg) : EmptyError(ParserError.SysUnexpect(inp.Pos, $"\"{x}\""), inp.TokenPos), EmptyError: msg => EmptyError(msg, inp.TokenPos), ConsumedOK: (x, rem, msg) => pred(x) ? ConsumedOK(x, rem, msg) : EmptyError(ParserError.SysUnexpect(inp.Pos, $"\"{x}\""), inp.TokenPos), ConsumedError: msg => ConsumedError(msg, inp.TokenPos)); public static Parser Map(this Parser self, Func map) => self.Select(map); public static Parser Select(this Parser self, Func map) => inp => self(inp).Select(map); public static Parser Bind(this Parser self, Func> f) => self.SelectMany(f); public static Parser SelectMany( this Parser self, Func> f) => inp => { Debug.Assert(inp != null); var t = self(inp); // cok if (t.Tag == ResultTag.Consumed && t.Reply.Tag == ReplyTag.OK) { return f(t.Reply.Result)(t.Reply.State); } // eok if (t.Tag == ResultTag.Empty && t.Reply.Tag == ReplyTag.OK) { return f(t.Reply.Result)(t.Reply.State); } // cerr if (t.Tag == ResultTag.Consumed && t.Reply.Tag == ReplyTag.Error) { return ConsumedError(t.Reply.Error, inp.TokenPos); } // eerr return EmptyError(t.Reply.Error, inp.TokenPos); }; public static Parser SelectMany( this Parser self, Func> bind, Func project) => inp => { Debug.Assert(inp != null); var t = self(inp); // cok if (t.Tag == ResultTag.Consumed && t.Reply.Tag == ReplyTag.OK) { var u = bind(t.Reply.Result)(t.Reply.State); if (u.Tag == ResultTag.Consumed && u.Reply.Tag == ReplyTag.OK) { // cok, cok -> cok var v = project(t.Reply.Result, u.Reply.Result); return ConsumedOK(v, u.Reply.State, u.Reply.Error); } if (u.Tag == ResultTag.Consumed && u.Reply.Tag == ReplyTag.Error) { // cok, cerr -> cerr return ConsumedError(u.Reply.Error, inp.TokenPos); } if (u.Tag == ResultTag.Empty && u.Reply.Tag == ReplyTag.OK) { // cok, eok -> cok (not a typo, this should be -> cok) var v = project(t.Reply.Result, u.Reply.Result); return ConsumedOK(v, u.Reply.State, mergeError(t.Reply.Error, u.Reply.Error)); } // cok, eerr return ConsumedError(mergeError(t.Reply.Error, u.Reply.Error), inp.TokenPos); } // eok if (t.Tag == ResultTag.Empty && t.Reply.Tag == ReplyTag.OK) { var u = bind(t.Reply.Result)(t.Reply.State); if (u.Tag == ResultTag.Consumed && u.Reply.Tag == ReplyTag.OK) { // eok, cok -> cok var v = project(t.Reply.Result, u.Reply.Result); return ConsumedOK(v, u.Reply.State, u.Reply.Error); } if (u.Tag == ResultTag.Empty && u.Reply.Tag == ReplyTag.OK) { // eok, eok -> eok var v = project(t.Reply.Result, u.Reply.Result); return EmptyOK(v, u.Reply.State, mergeError(t.Reply.Error, u.Reply.Error)); } if (u.Tag == ResultTag.Consumed && u.Reply.Tag == ReplyTag.Error) { // eok, cerr -> cerr return ConsumedError(u.Reply.Error, inp.TokenPos); } // eok, eerr return EmptyError(mergeError(t.Reply.Error, u.Reply.Error), inp.TokenPos); } // cerr if (t.Tag == ResultTag.Consumed && t.Reply.Tag == ReplyTag.Error) { return ConsumedError(t.Reply.Error, inp.TokenPos); } // eerr return EmptyError(t.Reply.Error, inp.TokenPos); }; public static Parser Flatten(this Parser> mma) => mma.Bind(identity); } ================================================ FILE: LanguageExt.Parsec/ParserIOs/Expr.cs ================================================ using System; using LanguageExt; using static LanguageExt.Prelude; using static LanguageExt.Parsec.PrimIO; namespace LanguageExt.Parsec; public static class ExprIO { /// /// Convert an OperatorTable and basic term parser into a fully fledged /// expression parser /// /// buildExpressionParser(table,term) builds an expression parser for /// terms term with operators from table, taking the associativity /// and precedence specified in table into account. Prefix and postfix /// operators of the same precedence can only occur once (i.e. --2 is /// not allowed if '-' is prefix negate). Prefix and postfix operators /// of the same precedence associate to the left (i.e. if ++ is /// postfix increment, than -2++ equals -1, not -3). /// /// The buildExpressionParser function takes care of all the complexity /// involved in building expression parser. /// /// See remarks. /// /// /// This is an example of an expression parser that handles prefix signs, /// postfix increment and basic arithmetic. /// /// Parser〈int〉 expr = null; /// /// var binary = fun((string name, Func〈int, int, int〉 f, Assoc assoc) =〉 /// Operator.Infix〈int〉( assoc, /// from x in reservedOp(name) /// select fun ); /// /// var prefix = fun((string name, Func〈int, int〉 f) =〉 /// Operator.Prefix〈int〉(from x in reservedOp(name) /// select fun ); /// /// var postfix = fun((string name, Func〈int, int〉 f) =〉 /// Operator.Postfix〈int〉(from x in reservedOp(name) /// select fun ); /// /// Operator〈int〉[][] table = [ [ prefix("-",negate), prefix("+",id) ] /// , [ postfix("++", incr) ] /// , [ binary("*", mult) Assoc.Left), binary("/", div, Assoc.Left) ] /// , [ binary("+", add, Assoc.Left), binary("-", subtr, Assoc.Left) ] ]; /// /// var term = either(parens(expr),natural).label("simple expression") /// /// expr = buildExpressionParser(table,term).label("expression") /// /// var res = parse(expr, "(50 + 20) / 2"); /// public static Parser buildExpressionParser( Operator[][] operators, Parser simpleExpr) => operators.AsIterable().FoldBack( simpleExpr, (term, ops) => makeParser(ops, term)); static Parser makeParser( Operator[] ops, Parser term) { var e3 = Seq.empty>>(); var e2 = Seq.empty>>(); return ops.AsIterable() .Fold((e3, e3, e3, e2, e2), (state, op) => op.SplitOp(state)) .Map((rassoc, lassoc, nassoc, prefix, postfix) => { var rassocOp = choice(rassoc); var lassocOp = choice(lassoc); var nassocOp = choice(nassoc); var prefixOp = choice(prefix).label(""); var postfixOp = choice(postfix).label(""); var ambigious = fun((string assoc, Parser> op) => attempt( from x in op from y in failure($"ambiguous use of a {assoc} associative operator") select y)); var ambigiousRight = ambigious("right", rassocOp); var ambigiousLeft = ambigious("left", lassocOp); var ambigiousNon = ambigious("non", nassocOp); var postfixP = either(postfixOp, result>(x => x)); var prefixP = either(prefixOp, result>(x => x)); var termP = from pre in prefixP from x in term from post in postfixP select post(pre(x)); Func> rassocP = null; Func> rassocP1 = null; rassocP1 = fun((O x) => either(rassocP(x), result(x))); rassocP = fun((O x) => choice( from f in rassocOp from y in (from z in termP from z1 in rassocP1(z) select z1) select f(x, y), ambigiousLeft, ambigiousNon)); Func> lassocP = null; Func> lassocP1 = null; lassocP1 = fun((O x) => either(lassocP(x), result(x))); lassocP = fun((O x) => choice( from f in lassocOp from y in termP from r in lassocP1(f(x, y)) select r, ambigiousRight, ambigiousNon)); var nassocP = fun((O x) => from f in nassocOp from y in termP from r in choice(ambigiousRight, ambigiousLeft, ambigiousNon, result(f(x, y))) select r); return from x in termP from r in choice(rassocP(x), lassocP(x), nassocP(x), result(x)).label("operator") select r; }); } } ================================================ FILE: LanguageExt.Parsec/ParserIOs/Indent.cs ================================================ using System; namespace LanguageExt.Parsec { public static class IndentIO { /// /// Parses only when indented past the level of the reference /// /// /// You must have provided a TokenPos to the initial PString〈TOKEN〉 that gives accurate /// column and line values for this function to work. /// public static Parser indented(int offset, Parser p) => inp => { var pos = inp.Pos; var col = pos.Column + offset; var ln = pos.Line; var toks = inp.Value; var ix = inp.Index; var start = inp.Index; var tpos = inp.TokenPos; for (; ix < inp.EndIndex; ix++) { var npos = tpos(toks[ix]); if (npos.Line != ln && npos.Column < col) { break; } } return parseBlock(p, start, ix, inp); }; /// /// Parses only when indented zero or more characters past the level of the reference /// /// /// You must have provided a TokenPos to the initial PString〈TOKEN〉 that gives accurate /// column and line values for this function to work. /// public static Parser indented(Parser p) => indented(0, p); /// /// Parses only when indented one or more characters past the level of the reference /// /// /// You must have provided a TokenPos to the initial PString〈TOKEN〉 that gives accurate /// column and line values for this function to work. /// public static Parser indented1(Parser p) => indented(1, p); /// /// Parses only when indented two or more characters past the level of the reference /// /// /// You must have provided a TokenPos to the initial PString〈TOKEN〉 that gives accurate /// column and line values for this function to work. /// public static Parser indented2(Parser p) => indented(2, p); /// /// Parses only when indented four or more characters past the level of the reference /// /// /// You must have provided a TokenPos to the initial PString〈TOKEN〉 that gives accurate /// column and line values for this function to work. /// public static Parser indented4(Parser p) => indented(4, p); /// /// Sets a new context for the parser p which represents a span of the input tokens /// /// /// The parse fails if it doesn't consume all tokens in the block /// static ParserResult parseBlock(Parser p, int start, int end, PString inp) { var pstr = new PString(inp.Value, start, end, inp.UserState, inp.TokenPos); var pres = p.Parse(pstr); return new ParserResult( pres.Tag, new Reply( pres.Reply.Tag, pres.Reply.Result!, new PString(inp.Value, pres.Reply.State.Index, inp.EndIndex, pres.Reply.State.UserState, pres.Reply.State.TokenPos), pres.Reply.Error!)); } } } ================================================ FILE: LanguageExt.Parsec/ParserIOs/Item.cs ================================================ using System; using static LanguageExt.Parsec.ParserResultIO; using static LanguageExt.Parsec.InternalIO; using static LanguageExt.Parsec.PrimIO; using LanguageExt.ClassInstances; using LanguageExt.Traits; namespace LanguageExt.Parsec { /// /// Commonly used character parsers. /// public static class ItemIO { /// /// item(c) parses a single I /// /// The parsed character public static Parser item(A c) => satisfy(x => EqDefault.Equals(x,c)).label($"'{c}'"); /// /// The parser satisfy(pred) succeeds for any character for which the /// supplied function pred returns 'True'. /// /// /// The character that is actually parsed. public static Parser satisfy(Func pred) => inp => { if (inp.Index >= inp.EndIndex) { return EmptyError(ParserError.SysUnexpect(inp.Pos, "end of stream"), inp.TokenPos); } else { var ns = newstate(inp); if (ns.Tag == ResultTag.Consumed) { if (pred(ns.Reply.Result!)) { return ns; } else { return EmptyError(ParserError.SysUnexpect(inp.Pos, $"\"{ns.Reply.Result}\""), inp.TokenPos); } } else { return EmptyError(ParserError.SysUnexpect(inp.Pos, "end of stream"), inp.TokenPos); } } }; /// /// oneOf(str) succeeds if the current character is in the supplied list of /// characters str. Returns the parsed character. See also satisfy /// public static Parser oneOf(Seq str) => satisfy(a => str.Exists(b => EqDefault.Equals(a, b))); /// /// As the dual of 'oneOf', noneOf(str) succeeds if the current /// character not in the supplied list of characters str. /// /// var consonant = noneOf("aeiou") /// /// /// The parsed character. public static Parser noneOf(Seq str) => satisfy(a => str.ForAll(b => !EqDefault.Equals(a, b))); /// /// The parser anyChar accepts any kind of character. /// public static Parser anyItem() => satisfy(_ => true); /// /// Parse a string /// public static Parser> str(Seq s) => chain(s.Map(item)); } } ================================================ FILE: LanguageExt.Parsec/ParserIOs/Prim.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using LanguageExt; using static LanguageExt.Prelude; using static LanguageExt.Parsec.Common; using static LanguageExt.Parsec.InternalIO; using static LanguageExt.Parsec.ItemIO; using static LanguageExt.Parsec.ParserResultIO; namespace LanguageExt.Parsec { /// /// The primitive parser combinators /// public static class PrimIO { /// /// Run the parser p with the input provided /// public static ParserResult parse(Parser p, PString input) => p.Parse(input); /// /// Run the parser p with the input provided /// public static ParserResult parse(Parser p, Seq input, Func tokenPos) => p.Parse(input, tokenPos); /// /// Lazy parser - useful in recursive scenarios. /// public static Parser lazyp(Func> fn) => inp => fn()(inp); /// /// This parser is useful to put at the top of LINQ expressions, it /// makes it easier to put breakpoints on the actual first parser /// in an expression. It returns unit /// public static Parser unitp() => inp => EmptyOK(unit, inp); /// /// Special parser for setting user-state that propagates /// through the computation. /// public static Parser setState(S state) => inp => ConsumedOK(unit, inp.SetUserState(state)); /// /// Special parser for getting user-state that was previously /// set with setState /// public static Parser getState() => inp => match(inp.UserState, Some: x => x is S ? ConsumedOK((S)x, inp) : EmptyError(ParserError.Message(inp.Pos, "User state type-mismatch"), inp.TokenPos), None: () => EmptyError(ParserError.Message(inp.Pos, "No user state set"), inp.TokenPos)); /// /// Get the current position of the parser in the source as a line /// and column index (starting at 1 for both) /// public static Parser getPos() => (PString inp) => ConsumedOK(inp.Pos, inp); /// /// Get the current index into the source /// public static Parser getIndex() => (PString inp) => ConsumedOK(inp.Index, inp); /// /// The parser unexpected(msg) always fails with an Unexpect error /// message msg without consuming any input. /// /// /// The parsers 'failure', 'label' and 'unexpected' are the three parsers /// used to generate error messages. Of these, only 'label' is commonly /// used. For an example of the use of unexpected, see the definition /// of 'notFollowedBy'. /// /// Error message to use when parsed public static Parser unexpected(string msg) => inp => EmptyError(ParserError.Unexpect(inp.Pos, msg), inp.TokenPos); /// /// The parser failure(msg) always fails with a Message error /// without consuming any input. /// /// The parsers 'failure', 'label' and 'unexpected' are the three parsers /// used to generate error messages. Of these, only 'label' is commonly /// used. For an example of the use of unexpected, see the definition /// of 'notFollowedBy'. /// /// Error message to use when parsed public static Parser failure(string msg) => inp => EmptyError(ParserError.Message(inp.Pos, msg), inp.TokenPos); /// /// Always success parser. Returns the value provided. /// This is monad return for the Parser monad /// public static Parser result(O value) => inp => EmptyOK(value, inp); /// /// Always fails (with an Unknown error) without consuming any input /// public static Parser zero() => inp => EmptyError(ParserError.Unknown(inp.Pos), inp.TokenPos); /// /// This combinator implements choice. The parser either(p,q) first /// applies p. If it succeeds, the value of p is returned. If p /// fails /without consuming any input/, parser q is tried. /// /// /// This combinator is the mplus behaviour of the Parser monad. /// /// The parser is called /predictive/ since q is only tried when /// parser p didn't consume any input (i.e.. the look ahead is 1). /// /// This non-backtracking behaviour allows for both an efficient /// implementation of the parser combinators and the generation of good /// error messages. /// public static Parser either(Parser p, Parser q) => inp => { var m = p(inp); // meerr if (m.Tag == ResultTag.Empty && m.Reply.Tag == ReplyTag.Error) { var n = q(inp); // neok if (n.Tag == ResultTag.Empty && n.Reply.Tag == ReplyTag.OK) { return EmptyOK(n.Reply.Result, n.Reply.State, mergeError(m.Reply.Error, n.Reply.Error)); } // nerr if (n.Tag == ResultTag.Empty && n.Reply.Tag == ReplyTag.Error) { return EmptyError(mergeError(m.Reply.Error, n.Reply.Error), inp.TokenPos); } // cerr, cok return n; } // cok, cerr, eok return m; }; /// /// choice(ps) tries to apply the parsers in the list ps in order, until one /// of them succeeds. /// /// /// The value of the succeeding parser. /// public static Parser choice(params Parser[] ps) => choicei(toSeq(ps)); /// /// choice(ps) tries to apply the parsers in the list ps in order, until one /// of them succeeds. /// /// /// The value of the succeeding parser. /// public static Parser choice(Seq> ps) => choicei(ps); /// /// Runs a sequence of parsers, if any fail then the failure state is /// returned immediately and subsequence parsers are not run. /// /// /// The result of each parser as an enumerable. /// public static Parser> chain(params Parser[] ps) => chaini(toSeq(ps)); /// /// Runs a sequence of parsers, if any fail then the failure state is /// returned immediately and subsequence parsers are not run. /// /// /// The result of each parser as an enumerable. /// public static Parser> chain(Seq> ps) => chaini(ps); /// /// The parser attempt(p) behaves like parser p, except that it /// pretends that it hasn't consumed any input when an error occurs. /// /// This combinator is used whenever arbitrary look ahead is needed. /// Since it pretends that it hasn't consumed any input when p fails, /// the either combinator will try its second alternative even when the /// first parser failed while consuming input. /// /// See remarks. /// /// /// The attempt combinator can for example be used to distinguish /// identifiers and reserved words. Both reserved words and identifiers /// are a sequence of letters. Whenever we expect a certain reserved /// word where we can also expect an identifier we have to use the attempt /// combinator. Suppose we write: /// /// var expr = either(letExpr, identifier).label("expression"); /// /// var letExpr = from x in str("let") /// ... /// select ...; /// /// var identifier = many1(letter); /// /// If the user writes "lexical", the parser fails with: unexpected /// "x", expecting "t" in "let". Indeed, since the either combinator /// only tries alternatives when the first alternative hasn't consumed /// input, the identifier parser is never tried (because the prefix /// "le" of the str("let") parser is already consumed). The right behaviour /// can be obtained by adding the attempt combinator: /// /// var expr = either(letExpr, identifier).label("expression"); /// /// var letExpr = from x in attempt(str("let")) /// ... /// select ...; /// /// var identifier = many1(letter); /// /// public static Parser attempt(Parser p) => inp => { var res = p(inp); if (res.Tag == ResultTag.Consumed && res.Reply.Tag == ReplyTag.Error) { return EmptyError(res.Reply.Error, inp.TokenPos); } else { return res; } }; /// /// lookAhead(p) parses p without consuming any input. /// /// If p fails and consumes some input, so does lookAhead(p). Combine with /// 'attempt' if this is undesirable. /// public static Parser lookAhead(Parser p) => inp => { var res = p(inp); if (res.Reply.Tag == ReplyTag.OK) { return EmptyOK(res.Reply.Result, inp); } else { return res; } }; /// /// many(p) applies the parser p zero or more times. /// /// /// var identifier = from c in letter /// from cs in many(letterOrDigit) /// select c.Cons(cs) /// /// /// Enumerable of the returned values of p. /// public static Parser> many(Parser p) => inp => { var current = inp; var results = new List(); ParserError error = null; while(true) { var t = p(current); // cok if (t.Tag == ResultTag.Consumed && t.Reply.Tag == ReplyTag.OK) { results.Add(t.Reply.Result); current = t.Reply.State; error = t.Reply.Error; continue; } // eok if (t.Tag == ResultTag.Empty && t.Reply.Tag == ReplyTag.OK) { // eok, eerr return EmptyError>(new ParserError(ParserErrorTag.SysUnexpect, current.Pos, "many: combinator 'many' is applied to a parser that accepts an empty string.", List.empty()), inp.TokenPos); } // cerr if (t.Tag == ResultTag.Consumed && t.Reply.Tag == ReplyTag.Error) { return ConsumedError>(mergeError(error, t.Reply.Error), inp.TokenPos); } // eerr return EmptyOK(toSeq(results), current, mergeError(error, t.Reply.Error)); } }; /// /// many1(p) applies the parser p one or more times. /// /// /// Enumerable of the returned values of p. /// public static Parser> many1(Parser p) => from x in p from xs in many(p) select x.Cons(xs); /// /// skipMany(p) applies the parser p zero or more times, skipping /// its result. /// public static Parser skipMany(Parser p) => either(skipMany1(p), result(unit)); /// /// skipMany(p) applies the parser p one or more times, skipping /// its result. /// public static Parser skipMany1(Parser p) => from x in p from xs in many(p) select unit; /// /// optionOrElse(x, p) tries to apply parser p. If p fails without /// consuming input, it returns the value x, otherwise the value /// returned by p. /// public static Parser optionOrElse(O x, Parser p) => either(p, result(x)); /// /// optional(p) tries to apply parser p. If p fails without /// consuming input, it return 'None', otherwise it returns /// 'Some' the value returned by p. /// public static Parser> optional(Parser p) => inp => { var r = p.Map(Option.Some)(inp); return r.Reply.Tag == ReplyTag.OK ? r : EmptyOK(Option.None, inp); }; /// /// optionalSeq(p) tries to apply parser p. If p fails without /// consuming input, it return an empty IEnumerable, otherwise it returns /// a one item IEnumerable with the result of p. /// /// A list of 0 or 1 parsed items public static Parser> optionalSeq(Parser p) => inp => { var r = p.Map(x => x.Cons(Seq.Empty))(inp); return r.Reply.Tag == ReplyTag.OK ? r : EmptyOK(Seq.Empty, inp); }; /// /// optionalList(p) tries to apply parser p. If p fails without /// consuming input, it return [], otherwise it returns a one /// item Lst with the result of p. /// /// A list of 0 or 1 parsed items public static Parser> optionalList(Parser p) => inp => { var r = p.Map(x => List.create(x))(inp); return r.Reply.Tag == ReplyTag.OK ? r : EmptyOK(List.empty(), inp); }; /// /// optionalArray(p) tries to apply parser p. If p fails without /// consuming input, it return [], otherwise it returns a one /// item array with the result of p. /// /// A list of 0 or 1 parsed items public static Parser optionalArray(Parser p) => inp => { var r = p.Map(x => new[] { x })(inp); return r.Reply.Tag == ReplyTag.OK ? r : EmptyOK(new O [0], inp); }; /// /// between(open,close,p) parses open, followed by p and close. /// /// /// The value returned by p. /// public static Parser between(Parser open, Parser close, Parser inner) => from l in open from v in inner from r in close select v; /// /// sepBy1(p,sep) parses one or more occurrences of p, separated /// by sep. /// /// /// A list of values returned by p. /// public static Parser> sepBy1(Parser p, Parser sep) => from x in p from xs in many(from _ in sep from y in p select y) select x.Cons(xs); /// /// sepBy(p,sep) parses zero or more occurrences of p, separated /// by sep. /// /// /// A list of values returned by p. /// public static Parser> sepBy(Parser p, Parser sep) => either(sepBy1(p, sep), result>(Seq.Empty)); /// /// sepEndBy1(p,sep) parses one or more occurrences of p, /// separated and optionally ended by sep. /// /// /// A list of values returned by p. /// public static Parser> sepEndBy1(Parser p, Parser sep) => from x in p from xs in either(from _ in sep from ys in sepEndBy(p, sep) select ys, result>(Seq.Empty)) select x.Cons(xs); /// /// sepEndBy(p,sep) parses zero or more occurrences of p, /// separated and optionally ended by sep. /// /// /// A list of values returned by p. /// public static Parser> sepEndBy(Parser p, Parser sep) => either(sepEndBy1(p, sep), result>(Seq.Empty)); /// /// endBy1(p,sep) parses one or more occurrences of p, separated /// and ended by sep. /// /// /// A list of values returned by p. /// public static Parser> endBy1(Parser p, Parser sep) => many1(from x in p from _ in sep select x); /// /// endBy(p,sep) parses zerp or more occurrences of p, separated /// and ended by sep. /// /// /// A list of values returned by p. /// public static Parser> endBy(Parser p, Parser sep) => many(from x in p from _ in sep select x); /// /// count(n,p) parses n occurrences of p. If n is smaller or /// equal to zero, the parser equals to result([]). /// /// /// A list of values returned by p. /// public static Parser> count(int n, Parser p) => counti(n, p); /// /// chainr(p,op,x) parses zero or more occurrences of p, separated by op /// /// /// a value obtained by a right associative application of all functions /// returned by op to the values returned by p. If there are no occurrences /// of p, the value x is returned. public static Parser chainr(Parser p, Parser> op, O x) => either(chainr1(p, op), result(x)); /// /// chainl(p,op,x) parses zero or more occurrences of p, separated by op /// /// /// a value obtained by a left associative application of all functions /// returned by op to the values returned by p. If there are no occurrences /// of p, the value x is returned. public static Parser chainl(Parser p, Parser> op, O x) => either(chainr1(p, op), result(x)); /// /// chainr1(p,op) parses one or more occurrences of p, separated by op. /// /// /// A value obtained by a right associative application of all functions /// returned by op to the values returned by p /// public static Parser chainr1(Parser p, Parser> op) { Parser scan = null; var rest = fun((O x) => either(from f in op from y in scan select f(x, y), result(x))); scan = from x in p from y in rest(x) select y; return scan; } /// /// chainl1(p,op) parses one or more occurrences of p, separated by op. /// /// /// A value obtained by a left associative application of all functions /// returned by op to the values returned by p /// public static Parser chainl1(Parser p, Parser> op) { Func> rest = null; rest = fun((O x) => either(from f in op from y in p from r in rest(f(x, y)) select r, result(x))); return from x in p from y in rest(x) select y; } /// /// This parser only succeeds at the end of the input. This is not a /// primitive parser but it is defined using 'notFollowedBy'. /// public static Parser eof() => notFollowedBy(anyItem()).label("end of input"); /// /// notFollowedBy(p) only succeeds when parser p fails. This parser /// does not consume any input.This parser can be used to implement the /// 'longest match' rule. /// /// For example, when recognizing keywords (for /// example 'let'), we want to make sure that a keyword is not followed /// by a legal identifier character, in which case the keyword is /// actually an identifier(for example 'lets'). We can program this /// behaviour as follows: /// /// var keywordLet = attempt (from x in str("let") /// from _ in notFollowedBy letterOrDigit /// select x); /// /// public static Parser notFollowedBy(Parser p) => attempt( either(from c in attempt(p) from u in unexpected(c.ToString()) select u, result(unit))); /// /// Parse a char list and convert into a string /// public static Parser asString(Parser> p) => p.Select(x => new string(x.ToArray())); /// /// Parse a T list and convert into a string /// public static Parser asString(Parser> p) => p.Select(x => String.Join("",x.Select(o =>o.ToString()))); /// /// Parse a T list and convert into a string /// public static Parser asString(Parser p) => p.Select(toString); /// /// Parse a char list and convert into an integer /// public static Parser> asInteger(Parser> p) => p.Select(x => parseInt(new string(x.ToArray()))); /// /// Parse a char list and convert into an integer /// public static Parser> asInteger(Parser p) => p.Select(parseInt); /// /// Parse a char list and convert into an integer /// public static Parser> asInteger(Parser> p, int fromBase) => p.Select(x => parseInt(new string(x.ToArray()), fromBase)); /// /// Parse a char list and convert into an integer /// public static Parser> asInteger(Parser p, int fromBase) => p.Select(x => parseInt(x, fromBase)); /// /// Parse a char list and convert into an double precision floating point value /// public static Parser> asDouble(Parser> p) => p.Select(x => parseDouble(new string(x.ToArray()))); /// /// Parse a char list and convert into an double precision floating point value /// public static Parser> asDouble(Parser p) => p.Select(parseDouble); /// /// Parse a char list and convert into an double precision floating point value /// public static Parser> asFloat(Parser> p) => p.Select(x => parseFloat(new string(x.ToArray()))); /// /// Parse a char list and convert into an double precision floating point value /// public static Parser> asFloat(Parser p) => p.Select(parseFloat); public static Parser> manyUntil(Parser p, Parser end) { Parser> scan = null; scan = either( from _ in end select Seq.Empty, from x in p from xs in scan select x.Cons(xs)); return scan; } /// /// Parse child tokens /// /// Parser that gets the child tokens /// Parser to run on the child tokens /// Token type /// Type of the value to parse /// Parser that parses a set of tokens then uses them as a new stream to parse public static Parser children(Parser> children, Parser p) => inp => { var cres = children(inp); if (cres.Reply.Tag == ReplyTag.OK) { var kids = cres.Reply.Result.ToArray(); var pres = p(new PString(kids, 0, kids.Length, cres.Reply.State.UserState, inp.TokenPos)); return pres.Reply.Tag == ReplyTag.OK && pres.Reply.State.Index < kids.Length ? new ParserResult( pres.Tag, new Reply( ReplyTag.Error, pres.Reply.Result, new PString(inp.Value, cres.Reply.State.Index, inp.EndIndex, pres.Reply.State.UserState, pres.Reply.State.TokenPos), ParserError.Unexpect(pres.Reply.State.Pos, "extra tokens in element that can't be parsed"))) : new ParserResult( pres.Tag, new Reply( pres.Reply.Tag, pres.Reply.Result, new PString(inp.Value, cres.Reply.State.Index, inp.EndIndex, pres.Reply.State.UserState, pres.Reply.State.TokenPos), pres.Reply.Error)); } else { return new ParserResult( cres.Tag, new Reply( cres.Reply.Tag, default(A), cres.Reply.State, cres.Reply.Error)); } }; } } ================================================ FILE: LanguageExt.Parsec/ParserResult.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using LanguageExt; using static LanguageExt.Prelude; using System.Diagnostics; namespace LanguageExt.Parsec { public static class ParserResult { public static ParserResult Consumed(Reply reply) => new ParserResult(ResultTag.Consumed, reply); public static ParserResult Empty(Reply reply) => new ParserResult(ResultTag.Empty, reply); public static ParserResult EmptyOK(T value, PString input, ParserError? error = null) => new ParserResult(ResultTag.Empty, Reply.OK(value, input, error)); public static ParserResult EmptyError(ParserError error) => new ParserResult(ResultTag.Empty, Reply.Error(error)); public static ParserResult ConsumedOK(T value, PString input) => new ParserResult(ResultTag.Consumed, Reply.OK(value, input)); public static ParserResult ConsumedOK(T value, PString input, ParserError error) => new ParserResult(ResultTag.Consumed, Reply.OK(value, input, error)); public static ParserResult ConsumedError(ParserError error) => new ParserResult(ResultTag.Consumed, Reply.Error(error)); } public enum ResultTag { Consumed, Empty } public class ParserResult { public readonly ResultTag Tag; public readonly Reply Reply; internal ParserResult(ResultTag tag, Reply reply) { Tag = tag; Reply = reply; } public ParserResult SetEndIndex(int endIndex) => new ParserResult(Tag, Reply.SetEndIndex(endIndex)); public ParserResult Project(S s, Func project) => new ParserResult(Tag, Reply.Project(s, project)); public override string ToString() => IsFaulted ? Reply?.Error?.ToString() ?? "Error" : $"Success({Reply.Result})"; public bool IsFaulted => Reply.Tag == ReplyTag.Error; public R Match( Func EmptyError, Func ConsumedError, Func, R> Otherwise ) { if (Tag == ResultTag.Empty && Reply.Tag == ReplyTag.Error) { return EmptyError(Reply.Error!); } if (Tag == ResultTag.Consumed && Reply.Tag == ReplyTag.Error) { return ConsumedError(Reply.Error!); } return Otherwise(this); } public R Match( Func EmptyError, Func, R> Otherwise ) { if (Tag == ResultTag.Empty && Reply.Tag == ReplyTag.Error) { return EmptyError(Reply.Error!); } return Otherwise(this); } public R Match( Func, R> Empty, Func, R> Otherwise ) { if (Tag == ResultTag.Empty) { return Empty(Reply); } return Otherwise(this); } public R Match( Func, R> Empty, Func, R> Consumed ) { if (Tag == ResultTag.Empty) { return Empty(Reply); } return Consumed(Reply); } public R Match( Func ConsumedOK, Func ConsumedError, Func EmptyOK, Func EmptyError ) { if (Tag == ResultTag.Empty && Reply.Tag == ReplyTag.OK) { return EmptyOK(Reply.Result, Reply.State, Reply.Error); } if (Tag == ResultTag.Empty && Reply.Tag == ReplyTag.Error) { return EmptyError(Reply.Error); } if (Tag == ResultTag.Consumed && Reply.Tag == ReplyTag.OK) { return ConsumedOK(Reply.Result, Reply.State, Reply.Error); } return ConsumedError(Reply.Error); } public ParserResult Select(Func map) => new ParserResult(Tag, Reply.Select(map)); public Either ToEither() => IsFaulted ? Left(ToString()) : Right(Reply.Result); public Either ToEither(Func f) => IsFaulted ? Left(f(ToString())) : Right(Reply.Result); public Option ToOption() => IsFaulted ? None : Some(Reply.Result); } } ================================================ FILE: LanguageExt.Parsec/ParserResultIO.cs ================================================ using System; using static LanguageExt.Prelude; namespace LanguageExt.Parsec; public static class ParserResultIO { public static ParserResult Consumed(Reply reply) => new ParserResult(ResultTag.Consumed, reply); public static ParserResult Empty(Reply reply) => new ParserResult(ResultTag.Empty, reply); public static ParserResult EmptyOK(O value, PString input, ParserError? error = null) => new ParserResult(ResultTag.Empty, Reply.OK(value, input, error)); public static ParserResult EmptyError(ParserError error, Func tokenPos) => new ParserResult(ResultTag.Empty, Reply.Error(error, tokenPos)); public static ParserResult ConsumedOK(O value, PString input) => new ParserResult(ResultTag.Consumed, Reply.OK(value, input)); public static ParserResult ConsumedOK(O value, PString input, ParserError error) => new ParserResult(ResultTag.Consumed, Reply.OK(value, input, error)); public static ParserResult ConsumedError(ParserError error, Func tokenPos) => new ParserResult(ResultTag.Consumed, Reply.Error(error, tokenPos)); } public class ParserResult { public readonly ResultTag Tag; public readonly Reply Reply; internal ParserResult(ResultTag tag, Reply reply) { Tag = tag; Reply = reply; } public ParserResult SetEndIndex(int endIndex) => new ParserResult(Tag, Reply.SetEndIndex(endIndex)); public ParserResult Project(S s, Func project) => new ParserResult(Tag, Reply.Project(s, project)); public override string ToString() => Reply.Error is null ? "success" : Reply.Error.ToString(); public bool IsFaulted => Reply.Tag == ReplyTag.Error; public R Match( Func EmptyError, Func ConsumedError, Func, R> Otherwise ) { if (Tag == ResultTag.Empty && Reply.Tag == ReplyTag.Error) { return EmptyError(Reply.Error!); } if (Tag == ResultTag.Consumed && Reply.Tag == ReplyTag.Error) { return ConsumedError(Reply.Error!); } return Otherwise(this); } public R Match( Func EmptyError, Func, R> Otherwise ) { if (Tag == ResultTag.Empty && Reply.Tag == ReplyTag.Error) { return EmptyError(Reply.Error!); } return Otherwise(this); } public R Match( Func, R> Empty, Func, R> Otherwise ) { if (Tag == ResultTag.Empty) { return Empty(Reply); } return Otherwise(this); } public R Match( Func, R> Empty, Func, R> Consumed ) { if (Tag == ResultTag.Empty) { return Empty(Reply); } return Consumed(Reply); } public R Match( Func, ParserError, R> ConsumedOK, Func ConsumedError, Func, ParserError, R> EmptyOK, Func EmptyError ) { if (Tag == ResultTag.Empty && Reply.Tag == ReplyTag.OK) { return EmptyOK(Reply.Result!, Reply.State, Reply.Error!); } if (Tag == ResultTag.Empty && Reply.Tag == ReplyTag.Error) { return EmptyError(Reply.Error!); } if (Tag == ResultTag.Consumed && Reply.Tag == ReplyTag.OK) { return ConsumedOK(Reply.Result!, Reply.State, Reply.Error!); } return ConsumedError(Reply.Error!); } public ParserResult Select(Func map) => new ParserResult(Tag, Reply.Select(map)); public Either ToEither() => IsFaulted ? Left(ToString()) : Right(Reply.Result!); public Either ToEither(Func f) => IsFaulted ? Left(f(ToString())) : Right(Reply.Result!); public Option ToOption() => IsFaulted ? None : Some(Reply.Result!); } ================================================ FILE: LanguageExt.Parsec/Parsers/Char.cs ================================================ using System; using System.Linq; using static LanguageExt.Prelude; using static LanguageExt.Parsec.ParserResult; using static LanguageExt.Parsec.Internal; using static LanguageExt.Parsec.Prim; using LanguageExt.Traits; namespace LanguageExt.Parsec; /// /// Commonly used character parsers. /// public static class Char { static Char() { space = satisfy(System.Char.IsWhiteSpace).label("space"); spaces = skipMany(space).label("white space"); control = satisfy(System.Char.IsControl).label("control"); tab = satisfy(c => c == '\t').label("tab"); #pragma warning disable CS0618 // Type or member is obsolete newline = satisfy(c => c == '\n').label("lf new-line"); #pragma warning restore CS0618 // Type or member is obsolete LF = satisfy(c => c == '\n').label("lf new-line"); CR = satisfy(c => c == '\r').label("cr carriage-return"); CRLF = (from cr in ch('\r') from nl in ch('\n') select nl) .label("crlf new-line"); endOfLine = either(LF, CRLF).label("new-line"); digit = satisfy(System.Char.IsDigit).label("digit"); letter = satisfy(System.Char.IsLetter).label("letter"); alphaNum = satisfy(System.Char.IsLetterOrDigit).label("letter or digit"); lower = satisfy(System.Char.IsLower).label("lowercase letter"); upper = satisfy(System.Char.IsUpper).label("uppercase letter"); punctuation = satisfy(System.Char.IsPunctuation).label("punctuation"); separator = satisfy(System.Char.IsSeparator).label("separator"); symbolchar = satisfy(System.Char.IsSymbol).label("symbolchar"); octDigit = satisfy(c => "01234567".Contains(c)).label("octal digit"); hexDigit = satisfy(c => System.Char.IsDigit(c) || "abcdefABCDEF".Contains(c)).label("hexadecimal digit"); anyChar = satisfy(_ => true); } /// /// ch(c) parses a single character c /// /// The parsed character public static Parser ch(char c) => satisfy(x => x == c).label($"'{c}'"); /// /// ch(c) parses a single character c /// /// Eq〈char〉 trait /// The parsed character public static Parser ch(char c) where EQ : Eq => satisfy(x => EQ.Equals(x, c)).label($"'{c}'"); /// /// The parser satisfy(pred) succeeds for any character for which the /// supplied function pred returns 'True'. /// /// /// The character that is actually parsed. public static Parser satisfy(Func pred) => inp => { if (inp.Index >= inp.EndIndex) { return EmptyError(ParserError.SysUnexpect(inp.Pos, "end of stream")); } else { var ns = newstate(inp); if (ns.Tag == ResultTag.Consumed) { if (pred(ns.Reply.Result)) { return ns; } else { return EmptyError(ParserError.SysUnexpect(inp.Pos, $"\"{ns.Reply.Result}\"")); } } else { return EmptyError(ParserError.SysUnexpect(inp.Pos, "end of stream")); } } }; /// /// oneOf(str) succeeds if the current character is in the supplied list of /// characters str. Returns the parsed character. See also satisfy /// public static Parser oneOf(string str) => satisfy(c => str.Contains(c)); /// /// oneOf(str) succeeds if the current character is in the supplied list of /// characters str. Returns the parsed character. See also satisfy /// public static Parser oneOf(params char[] str) => satisfy(c => str.Contains(c)); /// /// As the dual of 'oneOf', noneOf(str) succeeds if the current /// character not in the supplied list of characters str. /// /// var consonant = noneOf("aeiou") /// /// /// The parsed character. public static Parser noneOf(string str) => satisfy(c => !str.Contains(c)); /// /// As the dual of 'oneOf', noneOf(str) succeeds if the current /// character not in the supplied list of characters str. /// /// var consonant = noneOf("aeiou") /// /// /// The parsed character. public static Parser noneOf(params char[] str) => satisfy(c => !str.Contains(c)); /// /// Parses a white space character (any character which satisfies 'System.Char.IsWhiteSpace') /// Returns the parsed character. /// public static readonly Parser space; /// /// Skips zero or more white space characters. See also 'skipMany'. /// public static readonly Parser spaces; /// /// Parses a control character /// Returns the parsed character. /// public static readonly Parser control; /// /// Parses a tab /// Returns the parsed character. /// public static readonly Parser tab; /// /// Equivalent to `LF`. Parses a line-feed newline char (\n). /// Returns the parsed character. /// [Obsolete("This parses only \\n (regardless of environment). Use (explicit) parser `LF` instead. Related parsers: `CRLF` and `endOfLine` (parsing CRLF or LF).")] public static readonly Parser newline; /// /// Parses a carriage-return char (\r) /// Returns the parsed character. /// public static readonly Parser CR; /// /// Parses a line-feed newline char (\n) /// Returns the parsed character. /// public static readonly Parser LF; /// /// Parses a carriage-return then line-feed /// Returns the new-line. /// public static readonly Parser CRLF; /// /// Parses a CRLF (see 'crlf') or LF (see 'newline') end-of-line. /// Returns a newline character(\'\\n\'). /// public static readonly Parser endOfLine; /// /// Parses a digit /// Returns the parsed character. /// public static readonly Parser digit; /// /// Parses a letter /// Returns the parsed character. /// public static readonly Parser letter; /// /// Parses a letter or digit /// Returns the parsed character. /// public static readonly Parser alphaNum; /// /// Parses a lowercase letter /// Returns the parsed character. /// public static readonly Parser lower; /// /// Parses a uppercase letter /// Returns the parsed character. /// public static readonly Parser upper; /// /// Parses a punctuation character /// Returns the parsed character. /// public static readonly Parser punctuation; /// /// Parses a separator character /// Returns the parsed character. /// public static readonly Parser separator; /// /// Parses a symbol character /// Returns the parsed character. /// public static readonly Parser symbolchar; /// /// Parses an octal digit (0-7) /// public readonly static Parser octDigit; /// /// Parses a hex digit (0-F | 0-f) /// public readonly static Parser hexDigit; /// /// The parser anyChar accepts any kind of character. /// public readonly static Parser anyChar; /// /// Parse a string /// public static Parser str(string s) => asString(chain(toSeq(s.AsIterable().Map(ch)))).label($"'{s}'"); /// /// Parse a string case insensitive (char by char) /// Eq〈char〉 trait /// public static Parser str(string s) where EQ: Eq => asString(chain(toSeq(s.AsIterable().Map(ch)))).label($"'{s}'"); } ================================================ FILE: LanguageExt.Parsec/Parsers/Expr.cs ================================================ using System; using LanguageExt; using static LanguageExt.Prelude; using static LanguageExt.Parsec.Prim; namespace LanguageExt.Parsec; public static class Expr { /// /// Convert an OperatorTable and basic term parser into a fully fledged /// expression parser /// /// buildExpressionParser(table,term) builds an expression parser for /// terms term with operators from table, taking the associativity /// and precedence specified in table into account. Prefix and postfix /// operators of the same precedence can only occur once (i.e. --2 is /// not allowed if '-' is prefix negate). Prefix and postfix operators /// of the same precedence associate to the left (i.e. if ++ is /// postfix increment, than -2++ equals -1, not -3). /// /// The buildExpressionParser function takes care of all the complexity /// involved in building expression parser. /// /// See remarks. /// /// /// This is an example of an expression parser that handles prefix signs, /// postfix increment and basic arithmetic. /// /// Parser〈int〉 expr = null; /// /// var binary = fun((string name, Func〈int, int, int〉 f, Assoc assoc) =〉 /// Operator.Infix〈int〉( assoc, /// from x in reservedOp(name) /// select fun ); /// /// var prefix = fun((string name, Func〈int, int〉 f) =〉 /// Operator.Prefix〈int〉(from x in reservedOp(name) /// select fun ); /// /// var postfix = fun((string name, Func〈int, int〉 f) =〉 /// Operator.Postfix〈int〉(from x in reservedOp(name) /// select fun ); /// /// Operator〈int〉[][] table = [ [ prefix("-",negate), prefix("+",id) ] /// , [ postfix("++", incr) ] /// , [ binary("*", mult) Assoc.Left), binary("/", div, Assoc.Left) ] /// , [ binary("+", add, Assoc.Left), binary("-", subtr, Assoc.Left) ] ]; /// ] /// var term = either(parens(expr),natural).label("simple expression") /// /// expr = buildExpressionParser(table,term).label("expression") /// /// var res = parse(expr, "(50 + 20) / 2"); /// public static Parser buildExpressionParser( Operator[][] operators, Parser simpleExpr ) { return operators.AsIterable().FoldBack( simpleExpr, (term, ops) => makeParser(ops, term) ); } static Parser makeParser( Operator[] ops, Parser term ) { var e3 = Seq.empty>>(); var e2 = Seq.empty>>(); return ops.AsIterable() .Fold((e3, e3, e3, e2, e2), (state, op) => op.SplitOp(state)) .Map((rassoc, lassoc, nassoc, prefix, postfix) => { var rassocOp = choice(rassoc); var lassocOp = choice(lassoc); var nassocOp = choice(nassoc); var prefixOp = choice(prefix).label(""); var postfixOp = choice(postfix).label(""); var ambigious = fun((string assoc, Parser> op) => attempt( from x in op from y in failure($"ambiguous use of a {assoc} associative operator") select y)); var ambigiousRight = ambigious("right", rassocOp); var ambigiousLeft = ambigious("left", lassocOp); var ambigiousNon = ambigious("non", nassocOp); var postfixP = either(postfixOp, result>(x => x)); var prefixP = either(prefixOp, result>(x => x)); var termP = from pre in prefixP from x in term from post in postfixP select post(pre(x)); Func> rassocP = null; Func> rassocP1 = null; rassocP1 = fun((T x) => either(rassocP(x), result(x))); rassocP = fun((T x) => choice( from f in rassocOp from y in (from z in termP from z1 in rassocP1(z) select z1) select f(x, y), ambigiousLeft, ambigiousNon)); Func> lassocP = null; Func> lassocP1 = null; lassocP1 = fun((T x) => either(lassocP(x), result(x))); lassocP = fun((T x) => choice( from f in lassocOp from y in termP from r in lassocP1(f(x, y)) select r, ambigiousRight, ambigiousNon)); var nassocP = fun((T x) => from f in nassocOp from y in termP from r in choice(ambigiousRight, ambigiousLeft, ambigiousNon, result(f(x, y))) select r); return from x in termP from r in choice(rassocP(x), lassocP(x), nassocP(x), result(x)).label("operator") select r; }); } } ================================================ FILE: LanguageExt.Parsec/Parsers/Indent.cs ================================================ using System; namespace LanguageExt.Parsec { public static class Indent { /// /// Parses only when indented past the level of the reference /// public static Parser indented(int offset, Parser p) => inp => { var col = inp.Pos.Column + offset; var newpos = inp.Pos; for (var index = inp.Index; index < inp.EndIndex; index++) { var x = inp.Value[index]; if(newpos.Column < col && newpos.Line > inp.Pos.Line && x != ' ' && x != '\t' && x != '\n' && x != '\r') { // first char that's not white-space and is left of the reference var block = inp.SetEndIndex(index); var res = p(block); return res.SetEndIndex(inp.EndIndex); } newpos = x == '\n' ? new Pos(newpos.Line + 1, 0) : x == '\t' ? new Pos(newpos.Line, ((newpos.Column / 4) + 1) * 4) : new Pos(newpos.Line, newpos.Column + 1); } return p(inp); }; /// /// Parses only when indented zero or more characters past the level of the reference /// public static Parser indented(Parser p) => indented(0, p); /// /// Parses only when indented one or more characters past the level of the reference /// public static Parser indented1(Parser p) => indented(1, p); /// /// Parses only when indented two or more characters past the level of the reference /// public static Parser indented2(Parser p) => indented(2, p); /// /// Parses only when indented four or more characters past the level of the reference /// public static Parser indented4(Parser p) => indented(4, p); } } ================================================ FILE: LanguageExt.Parsec/Parsers/Prim.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using static LanguageExt.Prelude; using static LanguageExt.Parsec.Common; using static LanguageExt.Parsec.Internal; using static LanguageExt.Parsec.Char; using static LanguageExt.Parsec.ParserResult; namespace LanguageExt.Parsec; /// /// The primitive parser combinators /// public static class Prim { static Prim() { unitp = inp => EmptyOK(unit, inp); getPos = inp => ConsumedOK(inp.Pos, inp); getIndex = inp => ConsumedOK(inp.Index, inp); eof = notFollowedBy(satisfy(_ => true), "end of input").label("end of input"); } /// /// Run the parser p with the input provided /// public static ParserResult parse(Parser p, PString input) => p.Parse(input); /// /// Run the parser p with the input provided /// public static ParserResult parse(Parser p, string input) => p.Parse(input); /// /// Lazy parser - useful in recursive scenarios. /// public static Parser lazyp(Func> fn) => inp => fn()(inp); /// /// This parser is useful to put at the top of LINQ expressions, it /// makes it easier to put breakpoints on the actual first parser /// in an expression. It returns unit /// public static readonly Parser unitp; /// /// Special parser for setting user-state that propagates /// through the computation. /// public static Parser setState(T state) => inp => ConsumedOK(unit, inp.SetUserState(state)); /// /// Special parser for getting user-state that was previously /// set with setState /// public static Parser getState() => inp => match(inp.UserState, Some: x => x is T ? ConsumedOK((T)x, inp) : EmptyError(ParserError.Message(inp.Pos, "User state type-mismatch")), None: () => EmptyError(ParserError.Message(inp.Pos, "No user state set"))); /// /// Get the current position of the parser in the source as a line /// and column index (starting at 1 for both) /// public static readonly Parser getPos; /// /// Get the current index into the source /// public static readonly Parser getIndex; /// /// The parser unexpected(msg) always fails with an Unexpect error /// message msg without consuming any input. /// /// /// The parsers 'failure', 'label' and 'unexpected' are the three parsers /// used to generate error messages. Of these, only 'label' is commonly /// used. For an example of the use of unexpected, see the definition /// of 'Text.Parsec.Combinator.notFollowedBy'. /// /// Error message to use when parsed public static Parser unexpected(string msg) => inp => EmptyError(ParserError.Unexpect(inp.Pos, msg)); /// /// The parser unexpected(msg) always fails with an Unexpect error /// message msg without consuming any input. /// /// /// The parsers 'failure', 'label' and 'unexpected' are the three parsers /// used to generate error messages. Of these, only 'label' is commonly /// used. For an example of the use of unexpected, see the definition /// of 'Text.Parsec.Combinator.notFollowedBy'. /// /// Location of the failure /// Error message to use when parsed public static Parser unexpected(Pos pos, string msg) => _ => EmptyError(ParserError.Unexpect(pos, msg)); /// /// The parser failure(msg) always fails with a Message error /// without consuming any input. /// /// The parsers 'failure', 'label' and 'unexpected' are the three parsers /// used to generate error messages. Of these, only 'label' is commonly /// used. For an example of the use of unexpected, see the definition /// of 'Text.Parsec.Combinator.notFollowedBy'. /// /// Error message to use when parsed public static Parser failure(string msg) => inp => EmptyError(ParserError.Message(inp.Pos, msg)); /// /// Always success parser. Returns the value provided. /// This is monad return for the Parser monad /// public static Parser result(T value) => inp => EmptyOK(value, inp); /// /// Always fails (with an Unknown error) without consuming any input /// public static Parser zero() => inp => EmptyError(ParserError.Unknown(inp.Pos)); /// /// This combinator implements choice. The parser either(p,q) first /// applies p. If it succeeds, the value of p is returned. If p /// fails /without consuming any input/, parser q is tried. /// /// /// This combinator is the mplus behaviour of the Parser monad. /// /// The parser is called /predictive/ since q is only tried when /// parser p didn't consume any input (i.e.. the look ahead is 1). /// /// This non-backtracking behaviour allows for both an efficient /// implementation of the parser combinators and the generation of good /// error messages. /// public static Parser either(Parser p, Parser q) => inp => { var m = p(inp); // meerr if (m is { Tag: ResultTag.Empty, Reply.Tag: ReplyTag.Error }) { var n = q(inp); // neok if (n is { Tag: ResultTag.Empty, Reply.Tag: ReplyTag.OK }) { return EmptyOK(n.Reply.Result!, n.Reply.State!, mergeError(m.Reply.Error!, n.Reply.Error!)); } // nerr if (n.Tag == ResultTag.Empty && n.Reply.Tag == ReplyTag.Error) { return EmptyError(mergeError(m.Reply.Error!, n.Reply.Error!)); } // cerr, cok return n; } // cok, cerr, eok return m; }; /// /// choice(ps) tries to apply the parsers in the list ps in order, until one /// of them succeeds. /// /// /// The value of the succeeding parser. /// public static Parser choice(params Parser[] ps) => choicei(toSeq(ps)); /// /// choice(ps) tries to apply the parsers in the list ps in order, until one /// of them succeeds. /// /// /// The value of the succeeding parser. /// public static Parser choice(Seq> ps) => choicei(ps); /// /// Runs a sequence of parsers, if any fail then the failure state is /// returned immediately and subsequence parsers are not run. /// /// /// The result of each parser as an enumerable. /// public static Parser> chain(params Parser[] ps) => chaini(toSeq(ps)); /// /// Runs a sequence of parsers, if any fail then the failure state is /// returned immediately and subsequence parsers are not run. /// /// /// The result of each parser as an enumerable. /// public static Parser> chain(Seq> ps) => chaini(ps); /// /// Cons for parser results /// /// /// /// /// public static Parser> cons(Parser p, Parser> ps) => from x in p from xs in ps select x.Cons(xs); /// /// Flattens parser result: Seq of Seq of T => Seq of T /// /// Parser with flattened result sequence public static Parser> flatten(Parser>> ssp) => from xss in ssp select xss.Flatten(); /// /// The parser attempt(p) behaves like parser p, except that it /// pretends that it hasn't consumed any input when an error occurs. /// /// This combinator is used whenever arbitrary look ahead is needed. /// Since it pretends that it hasn't consumed any input when p fails, /// the either combinator will try its second alternative even when the /// first parser failed while consuming input. /// /// See remarks. /// /// /// The attempt combinator can for example be used to distinguish /// identifiers and reserved words. Both reserved words and identifiers /// are a sequence of letters. Whenever we expect a certain reserved /// word where we can also expect an identifier we have to use the attempt /// combinator. Suppose we write: /// /// var expr = either(letExpr, identifier).label("expression"); /// /// var letExpr = from x in str("let") /// ... /// select ...; /// /// var identifier = many1(letter); /// /// If the user writes "lexical", the parser fails with: unexpected /// "x", expecting "t" in "let". Indeed, since the either combinator /// only tries alternatives when the first alternative hasn't consumed /// input, the identifier parser is never tried (because the prefix /// "le" of the str("let") parser is already consumed). The right behaviour /// can be obtained by adding the attempt combinator: /// /// var expr = either(letExpr, identifier).label("expression"); /// /// var letExpr = from x in attempt(str("let")) /// ... /// select ...; /// /// var identifier = many1(letter); /// /// public static Parser attempt(Parser p) => inp => { var res = p(inp); if (res.Tag == ResultTag.Consumed && res.Reply.Tag == ReplyTag.Error) { return EmptyError(res.Reply.Error); } else { return res; } }; /// /// lookAhead(p) parses p without consuming any input. /// /// If p fails and consumes some input, so does lookAhead(p). Combine with /// 'attempt' if this is undesirable. /// public static Parser lookAhead(Parser p) => inp => { var res = p(inp); if (res.Reply.Tag == ReplyTag.OK) { return EmptyOK(res.Reply.Result, inp); } else { return res; } }; /// /// many(p) applies the parser p zero or more times. /// /// /// var identifier = from c in letter /// from cs in many(letterOrDigit) /// select c.Cons(cs) /// /// /// Enumerable of the returned values of p. /// public static Parser> many(Parser p) => inp => { var current = inp; var results = new List(); ParserError error = null; while(true) { var t = p(current); // cok if (t.Tag == ResultTag.Consumed && t.Reply.Tag == ReplyTag.OK) { results.Add(t.Reply.Result); current = t.Reply.State; error = t.Reply.Error; continue; } // eok if (t.Tag == ResultTag.Empty && t.Reply.Tag == ReplyTag.OK) { // eok, eerr return EmptyError>(new ParserError(ParserErrorTag.SysUnexpect, current.Pos, "many: combinator 'many' is applied to a parser that accepts an empty string.", List.empty())); } // cerr if (t.Tag == ResultTag.Consumed && t.Reply.Tag == ReplyTag.Error) { return ConsumedError>(mergeError(error, t.Reply.Error)); } // eerr return EmptyOK(toSeq(results), current, mergeError(error, t.Reply.Error)); } }; /// /// manyn(p, n) applies the parser p n times. /// /// /// var identifier = from c in letter /// from cs in manyn(letterOrDigit, 4) /// select c.Cons(cs) /// /// /// Enumerable of the returned values of p. /// public static Parser> manyn(Parser p, int n) => inp => { var current = inp; var results = new List(); ParserError error = null; int count = 0; while (true) { var t = p(current); count++; // cok if (t.Tag == ResultTag.Consumed && t.Reply.Tag == ReplyTag.OK) { results.Add(t.Reply.Result); current = t.Reply.State; error = t.Reply.Error; if (count == n) { return EmptyOK(toSeq(results), current, mergeError(error, t.Reply.Error)); } continue; } // eok if (t.Tag == ResultTag.Empty && t.Reply.Tag == ReplyTag.OK) { // eok, eerr return EmptyError>(new ParserError(ParserErrorTag.SysUnexpect, current.Pos, "many: combinator 'manyn' is applied to a parser that accepts an empty string.", List.empty())); } // cerr if (t.Tag == ResultTag.Consumed && t.Reply.Tag == ReplyTag.Error) { return ConsumedError>(mergeError(error, t.Reply.Error)); } // eerr return EmptyError>(mergeError(error, t.Reply.Error)); } }; /// /// manyn0(p) applies the parser p zero or up to a maximum of n times. /// /// /// var identifier = from c in letter /// from cs in manyn0(letterOrDigit, 4) /// select c.Cons(cs) /// /// /// Enumerable of the returned values of p. /// public static Parser> manyn0(Parser p, int n) => n <= 0 ? result(Seq.Empty) : inp => { var current = inp; var results = new List(); ParserError error = null; int count = 0; while (true) { var t = p(current); count++; // cok if (t.Tag == ResultTag.Consumed && t.Reply.Tag == ReplyTag.OK) { results.Add(t.Reply.Result); current = t.Reply.State; error = t.Reply.Error; if (count == n) { return EmptyOK(toSeq(results), current, mergeError(error, t.Reply.Error)); } continue; } // eok if (t.Tag == ResultTag.Empty && t.Reply.Tag == ReplyTag.OK) { // eok, eerr return EmptyError>(new ParserError(ParserErrorTag.SysUnexpect, current.Pos, "many: combinator 'manyn0' is applied to a parser that accepts an empty string.", List.empty())); } // cerr if (t.Tag == ResultTag.Consumed && t.Reply.Tag == ReplyTag.Error) { return ConsumedError>(mergeError(error, t.Reply.Error)); } // eerr return EmptyOK(toSeq(results), current, mergeError(error, t.Reply.Error)); } }; /// /// manyn1(p) applies the parser p one or up to a maximum of n times. /// /// /// Enumerable of the returned values of p. /// public static Parser> manyn1(Parser p, int n) => from x in p from xs in manyn0(p, n - 1) select x.Cons(xs); /// /// many1(p) applies the parser p one or more times. /// /// /// Enumerable of the returned values of p. /// public static Parser> many1(Parser p) => from x in p from xs in many(p) select x.Cons(xs); /// /// skipMany(p) applies the parser p zero or more times, skipping /// its result. /// public static Parser skipMany(Parser p) => either(skipMany1(p), result(unit)); /// /// skipMany(p) applies the parser p one or more times, skipping /// its result. /// public static Parser skipMany1(Parser p) => from x in p from xs in many(p) select unit; /// /// optionOrElse(x, p) tries to apply parser p. If p fails without /// consuming input, it returns the value x, otherwise the value /// returned by p. /// public static Parser optionOrElse(T x, Parser p) => either(p, result(x)); /// /// optional(p) tries to apply parser p. If p fails without /// consuming input, it return 'None', otherwise it returns /// 'Some' the value returned by p. /// public static Parser> optional(Parser p) => inp => { var r = p.Map(Option.Some)(inp); return r.Reply.Tag == ReplyTag.OK ? r : EmptyOK(Option.None, inp); }; /// /// optionalList(p) tries to apply parser p. If p fails without /// consuming input, it return [], otherwise it returns a one /// item Lst with the result of p. /// /// A list of 0 or 1 parsed items public static Parser> optionalList(Parser p) => inp => { var r = p.Map(x => List.create(x))(inp); return r.Reply.Tag == ReplyTag.OK ? r : EmptyOK(List.empty(), inp); }; /// /// optionalSeq(p) tries to apply parser p. If p fails without /// consuming input, it return an empty IEnumerable, otherwise it returns /// a one item IEnumerable with the result of p. /// /// A list of 0 or 1 parsed items public static Parser> optionalSeq(Parser p) => inp => { var r = p.Map(x => x.Cons())(inp); return r.Reply.Tag == ReplyTag.OK ? r : EmptyOK(Seq.Empty, inp); }; /// /// optionalArray(p) tries to apply parser p. If p fails without /// consuming input, it return [], otherwise it returns a one /// item array with the result of p. /// /// A list of 0 or 1 parsed items public static Parser optionalArray(Parser p) => inp => { var r = p.Map(x => new[] { x })(inp); return r.Reply.Tag == ReplyTag.OK ? r : EmptyOK(new T [0], inp); }; /// /// between(open,close,p) parses open, followed by p and close. /// /// /// The value returned by p. /// public static Parser between(Parser open, Parser close, Parser inner) => from l in open from v in inner from r in close select v; /// /// sepBy1(p,sep) parses one or more occurrences of p, separated /// by sep. /// /// /// A list of values returned by p. /// public static Parser> sepBy1(Parser p, Parser sep) => from x in p from xs in many(from _ in sep from y in p select y) select x.Cons(xs); /// /// sepBy(p,sep) parses zero or more occurrences of p, separated /// by sep. /// /// /// A list of values returned by p. /// public static Parser> sepBy(Parser p, Parser sep) => either(sepBy1(p, sep), result(Seq.Empty)); /// /// sepEndBy1(p,sep) parses one or more occurrences of p, /// separated and optionally ended by sep. /// /// /// A list of values returned by p. /// public static Parser> sepEndBy1(Parser p, Parser sep) => from x in p from xs in either(from _ in sep from ys in sepEndBy(p, sep) select ys, result(Seq.Empty)) select x.Cons(xs); /// /// sepEndBy(p,sep) parses zero or more occurrences of p, /// separated and optionally ended by sep. /// /// /// A list of values returned by p. /// public static Parser> sepEndBy(Parser p, Parser sep) => either(sepEndBy1(p, sep), result(Seq.Empty)); /// /// endBy1(p,sep) parses one or more occurrences of p, separated /// and ended by sep. /// /// /// A list of values returned by p. /// public static Parser> endBy1(Parser p, Parser sep) => many1(from x in p from _ in sep select x); /// /// endBy(p,sep) parses zero or more occurrences of p, separated /// and ended by sep. /// /// /// A list of values returned by p. /// public static Parser> endBy(Parser p, Parser sep) => many(from x in p from _ in sep select x); /// /// count(n,p) parses n occurrences of p. If n is smaller or /// equal to zero, the parser equals to result([]). /// /// /// A list of values returned by p. /// public static Parser> count(int n, Parser p) => counti(n, p); /// /// chainr(p,op,x) parses zero or more occurrences of p, separated by op /// /// /// a value obtained by a right associative application of all functions /// returned by op to the values returned by p. If there are no occurrences /// of p, the value x is returned. public static Parser chainr(Parser p, Parser> op, T x) => either(chainr1(p, op), result(x)); /// /// chainl(p,op,x) parses zero or more occurrences of p, separated by op /// /// /// a value obtained by a left associative application of all functions /// returned by op to the values returned by p. If there are no occurrences /// of p, the value x is returned. public static Parser chainl(Parser p, Parser> op, T x) => either(chainr1(p, op), result(x)); /// /// chainr1(p,op) parses one or more occurrences of p, separated by op. /// /// /// A value obtained by a right associative application of all functions /// returned by op to the values returned by p /// public static Parser chainr1(Parser p, Parser> op) { Parser scan = null; var rest = fun((T x) => either(from f in op from y in scan select f(x, y), result(x))); scan = from x in p from y in rest(x) select y; return scan; } /// /// chainl1(p,op) parses one or more occurrences of p, separated by op. /// /// /// A value obtained by a left associative application of all functions /// returned by op to the values returned by p /// public static Parser chainl1(Parser p, Parser> op) { Func> rest = null; rest = fun((T x) => either(from f in op from y in p from r in rest(f(x, y)) select r, result(x))); return from x in p from y in rest(x) select y; } /// /// This parser only succeeds at the end of the input. This is not a /// primitive parser but it is defined using 'notFollowedBy'. /// public static readonly Parser eof; /// /// notFollowedBy(p) only succeeds when parser p fails. This parser /// does not consume any input.This parser can be used to implement the /// 'longest match' rule. /// /// For example, when recognizing keywords (for /// example 'let'), we want to make sure that a keyword is not followed /// by a legal identifier character, in which case the keyword is /// actually an identifier(for example 'lets'). We can program this /// behaviour as follows: /// /// var keywordLet = attempt (from x in str("let") /// from _ in notFollowedBy letterOrDigit /// select x); /// /// public static Parser notFollowedBy(Parser p, string? label = null) => attempt( either(from c in attempt(from l in getPos from r in p select (Pos: l, Value: r)) from u in unexpected(c.Pos, label ?? c.Value.ToString()) select u, result(unit))); /// /// Parse a char list and convert into a string /// public static Parser asString(Parser> p) => p.Select(x => new string(x.ToArray())); /// /// Parse a char list and convert into an integer /// public static Parser> asInteger(Parser> p) => p.Select(x => parseInt(new string(x.ToArray()))); /// /// Parse a char list and convert into an integer /// public static Parser> asInteger(Parser> p, int fromBase) => p.Select(x => parseInt(new string(x.ToArray()), fromBase)); /// /// Parse a char list and convert into an double precision floating point value /// public static Parser> asDouble(Parser> p) => p.Select(x => parseDouble(new string(x.ToArray()))); /// /// Parse a char list and convert into an double precision floating point value /// public static Parser> asFloat(Parser> p) => p.Select(x => parseFloat(new string(x.ToArray()))); public static Parser> manyUntil(Parser p, Parser end) { Parser> scan = null; scan = either( from _ in end select Seq.Empty, from x in p from xs in scan select x.Cons(xs)); return scan; } } ================================================ FILE: LanguageExt.Parsec/Parsers/Token.cs ================================================ using System; using System.Linq; using LanguageExt; using static LanguageExt.Prelude; using static LanguageExt.Parsec.Prim; using static LanguageExt.Parsec.Char; namespace LanguageExt.Parsec; /// /// A helper module to parse lexical elements (tokens) /// public static class Token { /// /// Given a LanguageDef, create a token parser. /// /// The expression makeTokenParser(language) creates a 'GenTokenParser' /// record that contains lexical parsers that are defined using the /// definitions in the language record. /// /// The use of this function is quite stylised - one imports the /// appropriate language definition and selects the lexical parsers that /// are needed from the resulting 'GenTokenParser'. /// /// // The parser /// ... /// /// var expr = either( /// parens(expr), /// identifier, /// ... /// ) /// /// // The lexer /// var lexer = makeTokenParser(langDef) /// /// var parens = lexer.Parens(p); /// var braces = lexer.Braces(p); /// var identifier = lexer.Identifier; /// var reserved = lexer.Reserved; /// ... /// public static GenTokenParser makeTokenParser(GenLanguageDef def) { var simpleSpace = skipMany1(satisfy(System.Char.IsWhiteSpace)); Parser multiLineComment = null; Parser inCommentMulti = null; Parser inCommentSingle = null; Func isReservedName = null; var startEnd = List.append(def.CommentEnd.ToArray(), def.CommentStart.ToArray()).Distinct().ToArray(); inCommentMulti = choice( from x in attempt(str(def.CommentEnd)) select unit, from x in lazyp(() => multiLineComment) from y in inCommentMulti select unit, from x in skipMany1(noneOf(startEnd)) from y in inCommentMulti select unit, from x in oneOf(startEnd) from y in inCommentMulti select unit) .label("end of comment"); inCommentSingle = choice( from x in attempt(str(def.CommentEnd)) select unit, from x in skipMany1(noneOf(startEnd)) from y in inCommentSingle select unit, from x in oneOf(startEnd) from y in inCommentSingle select unit) .label("end of comment"); var inComment = def.NestedComments ? inCommentMulti : inCommentSingle; multiLineComment = from x in attempt(str(def.CommentStart)) from _ in inComment select unit; var oneLineComment = from x in attempt(str(def.CommentLine)) from _ in skipMany(satisfy(c => c != '\n')) select unit; var whiteSpace = def.CommentLine == null && def.CommentStart == null ? skipMany(simpleSpace.label("")) : def.CommentStart == null ? skipMany(either(simpleSpace, multiLineComment).label("")) : def.CommentLine == null ? skipMany(either(simpleSpace, oneLineComment).label("")) : skipMany(choice(simpleSpace, oneLineComment, multiLineComment).label("")); var lexemeStr = lexemeDef(whiteSpace); var lexemeCh = lexemeDef(whiteSpace); var lexemeInt = lexemeDef(whiteSpace); var lexemeDbl = lexemeDef(whiteSpace); var lexemeUnit = lexemeDef(whiteSpace); var lexemeFnIntInt = lexemeDef>(whiteSpace); var lexemeEiIntDbl = lexemeDef>(whiteSpace); var symbol = fun((string name) => lexemeStr(str(name))); var semi = symbol(";"); var comma = symbol(","); var dot = symbol("."); var colon = symbol(":"); var dec = from x in many1(digit) let v = parseInt(new string(x.ToArray())) from n in v.Match( Some: result, None: () => failure("Not a valid decimal value")) select n; var octal = (from _ in ch('o') from x in many1(octDigit) let v = parseInt(new string(x.ToArray()), 8) from n in v.Match( Some: result, None: () => failure("Not a valid octal value")) select n) .label("octal number"); var hexadecimal = (from _ in ch('x') from x in many1(hexDigit) let v = parseInt(new string(x.ToArray()), 16) from n in v.Match( Some: result, None: () => failure("Not a valid hexadecimal value")) select n) .label("hexadecimal number"); var zeroNumber = (from _ in ch('0') from r in choice(hexadecimal, octal, dec, result(0)) select r) .label(""); var nat = either(zeroNumber, dec); var sign = choice(from _ in ch('-') select fun((double d) => -d), from _ in ch('+') select fun((double d) => d), result(fun((double d) => d))); var signi = choice(from _ in ch('-') select fun((int d) => -d), from _ in ch('+') select fun((int d) => d), result(fun((int d) => d))); var int_ = from f in lexemeFnIntInt(signi) from n in nat select f(n); var charEsc = choice(escMap.Map(pair => parseEsc(pair.Item1, pair.Item2))); var charNum = choice(dec, hexadecimal, octal) .Map(System.Char.ConvertFromUtf32) .Map(x => x[0]); var charControl = from _ in ch('^') from c in upper select (char)(c - 64); var escapeCode = choice(charEsc, charNum, charControl).label("escape code"); var charEscape = from _ in ch('\\') from c in escapeCode select c; var charLetter = satisfy(c => c != '\'' && c != '\\' && c > 26); var characterChar = either(charLetter, charEscape).label("literal character"); var charLiteral = lexemeCh( between( ch('\''), ch('\'').label("end of character"), characterChar)) .label("character"); var stringLetter = satisfy(c => c != '"' && c != '\\' && c > 26); var escapeGap = (from _ in many1(space) from c in ch('\\') select c) .label("end of string gap"); var escapeEmpty = ch('&'); var stringEscape = from _ in ch('\\') from esc in choice( from x in escapeGap select Option.None, from x in escapeEmpty select Option.None, from c in escapeCode select Some(c)) select esc; var stringChar = either(from c in stringLetter select Some(c), stringEscape) .label("string character"); var stringLiteral = lexemeStr( from s in between( ch('"'), ch('"'), many(stringChar) ) select new string(s.Somes().ToArray())) .label("literal string"); // -1.05e+003 - optional fractional part var floating = from si in optionOrElse("", oneOf("+-").Select(static x => x.ToString())) from nu in asString(many(digit)) from frac in optionOrElse("", from pt in dot from fr in asString(many(digit)) from ex in optionOrElse("", from e in oneOf("eE") from s in oneOf("+-") from n in asString(many1(digit)) select $"{e}{s}{n}" ) select $"{pt}{fr}{ex}") let all = $"{si}{nu}{frac}" let opt = parseDouble(all) from res in opt.Match( result, () => failure("Invalid floating point value") ) select res; // -1.05e+003 - must have fractional part var fracfloat = from si in optionOrElse("", from x in oneOf("+-") select x.ToString()) from nu in asString(many(digit)) from frac in from pt in dot from fr in asString(many(digit)) from ex in optionOrElse("", from e in oneOf("eE") from s in oneOf("+-") from n in asString(many1(digit)) select $"{e}{s}{n}" ) select $"{pt}{fr}{ex}" let all = $"{si}{nu}{frac}" let opt = parseDouble(all) from res in opt.Match( result, () => failure("Invalid floating point value") ) select res; var natural = lexemeInt(nat).label("natural"); var integer = lexemeInt(int_).label("integer"); var float_ = lexemeDbl(floating).label("float"); var naturalOrFloat = either(attempt(lexemeDbl(fracfloat)).Map(Right), integer.Map(Left)).label("number"); var reservedOp = fun((string name) => lexemeUnit( attempt( from n in str(name) from _ in notFollowedBy(def.OpLetter).label("end of " + name) select unit))); var oper = (from c in def.OpStart from cs in many(def.OpLetter) select new string(c.Cons(cs).ToArray())) .label("operator"); var operator_ = lexemeStr( attempt( from name in oper from op in def.ReservedOpNames.Contains(name) ? unexpected("reserved operator " + name) : result(name) select op)); var theReservedNames = def.CaseSensitive ? def.ReservedNames : def.ReservedNames.Map(x => x.ToLower()).ToLst(); var isReserved = fun((Lst names, string name) => names.Contains( def.CaseSensitive ? name : name.ToLower())); isReservedName = fun((string name) => isReserved(theReservedNames, name)); var ident = (from c in def.IdentStart from cs in many(def.IdentLetter) select new string(c.Cons(cs).ToArray())) .label("identifier"); var identifier = lexemeStr( attempt( from name in ident from r in isReservedName(name) ? unexpected("reserved word " + name) : result(name) select r)); var caseString = fun((string name) => str(name)); //def.CaseSensitive // ? str(name) // : /* TODO - fully implement case insensitive version*/); var reserved = fun((string name) => lexemeStr( attempt( from x in caseString(name) from _ in notFollowedBy(def.IdentLetter).label("end of " + name) select x))); return new GenTokenParser( identifier, reserved, operator_, reservedOp, charLiteral, stringLiteral, natural, integer, float_, naturalOrFloat, lexemeInt(dec), lexemeInt(hexadecimal), lexemeInt(octal), symbol, whiteSpace, semi, comma, colon, dot ); } static Func, Parser> lexemeDef(Parser whiteSpace) { var ws = whiteSpace; return (Parser p) => from x in p from _ in ws select x; } static Parser parseEsc(char c, T code) => from _ in ch(c) select code; static readonly Seq<(char, char)> escMap = toSeq(List.zip("abfnrtv\\\"\'", "\a\b\f\n\r\t\v\\\"\'").ToArray()); } ================================================ FILE: LanguageExt.Parsec/Parsers/Token2.cs ================================================ using System; using System.Linq; using LanguageExt; using static LanguageExt.Prelude; using static LanguageExt.Parsec.Prim; using static LanguageExt.Parsec.Char; namespace LanguageExt.Parsec; /// /// A helper module to parse lexical elements (tokens) /// public static class Token2 { /// /// Given a LanguageDef, create a token parser. /// /// The expression makeTokenParser(language) creates a 'GenTokenParser' /// record that contains lexical parsers that are defined using the /// definitions in the language record. /// /// The use of this function is quite stylised - one imports the /// appropriate language definition and selects the lexical parsers that /// are needed from the resulting 'GenTokenParser'. /// /// // The parser /// ... /// /// var expr = either( /// parens(expr), /// identifier, /// ... /// ) /// /// // The lexer /// var lexer = makeTokenParser(langDef) /// /// var parens = lexer.Parens(p); /// var braces = lexer.Braces(p); /// var identifier = lexer.Identifier; /// var reserved = lexer.Reserved; /// ... /// public static GenTokenParser2 makeTokenParser(GenLanguageDef def) { var simpleSpace = skipMany1(satisfy(System.Char.IsWhiteSpace)); Parser multiLineComment = null; Parser inCommentMulti = null; Parser inCommentSingle = null; Func isReservedName = null; var startEnd = List.append(def.CommentEnd.ToArray(), def.CommentStart.ToArray()).Distinct().ToArray(); inCommentMulti = choice( from x in attempt(str(def.CommentEnd)) select unit, from x in lazyp(() => multiLineComment) from y in inCommentMulti select unit, from x in skipMany1(noneOf(startEnd)) from y in inCommentMulti select unit, from x in oneOf(startEnd) from y in inCommentMulti select unit) .label("end of comment"); inCommentSingle = choice( from x in attempt(str(def.CommentEnd)) select unit, from x in skipMany1(noneOf(startEnd)) from y in inCommentSingle select unit, from x in oneOf(startEnd) from y in inCommentSingle select unit) .label("end of comment"); var inComment = def.NestedComments ? inCommentMulti : inCommentSingle; multiLineComment = from x in attempt(str(def.CommentStart)) from _ in inComment select unit; var oneLineComment = from x in attempt(str(def.CommentLine)) from _ in skipMany(satisfy(c => c != '\n')) select unit; var whiteSpace = def.CommentLine == null && def.CommentStart == null ? skipMany(simpleSpace.label("")) : def.CommentStart == null ? skipMany(either(simpleSpace, multiLineComment).label("")) : def.CommentLine == null ? skipMany(either(simpleSpace, oneLineComment).label("")) : skipMany(choice(simpleSpace, oneLineComment, multiLineComment).label("")); var lexemeStr = lexemeDef(whiteSpace); var lexemeCh = lexemeDef(whiteSpace); var lexemeInt = lexemeDef(whiteSpace); var lexemeDbl = lexemeDef(whiteSpace); var lexemeUnit = lexemeDef(whiteSpace); var lexemeFnIntInt = lexemeDef>(whiteSpace); var lexemeEiIntDbl = lexemeDef>(whiteSpace); var symbol = fun((string name) => lexemeStr(str(name))); var semi = symbol(";"); var comma = symbol(","); var dot = symbol("."); var colon = symbol(":"); var dec = from x in many1(digit) let v = parseInt(new string(x.ToArray())) from n in v.Match( Some: result, None: () => failure("Not a valid decimal value")) select n; var octal = (from _ in ch('o') from x in many1(octDigit) let v = parseInt(new string(x.ToArray()), 8) from n in v.Match( Some: result, None: () => failure("Not a valid octal value")) select n) .label("octal number"); var hexadecimal = (from _ in ch('x') from x in many1(hexDigit) let v = parseInt(new string(x.ToArray()), 16) from n in v.Match( Some: result, None: () => failure("Not a valid hexadecimal value")) select n) .label("hexadecimal number"); var zeroNumber = (from _ in ch('0') from r in choice(hexadecimal, octal, dec, result(0)) select r) .label(""); var nat = either(zeroNumber, dec); var int_ = from f in optional(oneOf("+-")) from n in nat select f == '-' ? -n : n; var charEsc = choice(escMap.Map(pair => parseEsc(pair.Item1, pair.Item2))); var charNum = choice(dec, hexadecimal, octal) .Map(System.Char.ConvertFromUtf32) .Map(x => x[0]); var charControl = from _ in ch('^') from c in upper select (char)(c - 64); var escapeCode = choice(charEsc, charNum, charControl).label("escape code"); var charEscape = from _ in ch('\\') from c in escapeCode select c; var charLetter = satisfy(c => c != '\'' && c != '\\' && c > 26); var characterChar = either(charLetter, charEscape).label("literal character"); var charLiteral = lexemeCh( between( ch('\''), ch('\'').label("end of character"), characterChar)) .label("character"); var stringLetter = satisfy(c => c != '"' && c != '\\' && c > 26); var escapeGap = (from _ in many1(space) from c in ch('\\') select c) .label("end of string gap"); var escapeEmpty = ch('&'); var stringEscape = from _ in ch('\\') from esc in choice( from x in escapeGap select Option.None, from x in escapeEmpty select Option.None, from c in escapeCode select Some(c)) select esc; var stringChar = either(from c in stringLetter select Some(c), stringEscape) .label("string character"); var stringLiteral = lexemeStr( from s in between( ch('"'), ch('"'), many(stringChar) ) select new string(s.Somes().ToArray())) .label("literal string"); // -1.05e+003 - optional fractional part var floating = from si in optionOrElse("", oneOf("+-").Select(static x => x.ToString())) from nu in asString(many(digit)) from frac in optionOrElse("", from pt in dot from fr in asString(many(digit)) from ex in optionOrElse("", from e in oneOf("eE") from s in oneOf("+-") from n in asString(many1(digit)) select $"{e}{s}{n}" ) select $"{pt}{fr}{ex}") let all = $"{si}{nu}{frac}" let opt = parseDouble(all) from res in opt.Match( result, static () => failure("Invalid floating point value") ) select res; // -1.05e+003 - must have fractional part var fracfloat = from si in optionOrElse("", from x in oneOf("+-") select x.ToString()) from nu in asString(many(digit)) from frac in from pt in dot from fr in asString(many(digit)) from ex in optionOrElse("", from e in oneOf("eE") from s in oneOf("+-") from n in asString(many1(digit)) select $"{e}{s}{n}" ) select $"{pt}{fr}{ex}" let all = $"{si}{nu}{frac}" let opt = parseDouble(all) from res in opt.Match( result, static () => failure("Invalid floating point value") ) select res; var natural = lexemeInt(nat).label("natural"); var integer = lexemeInt(int_).label("integer"); var float_ = lexemeDbl(floating).label("float"); var naturalOrFloat = either( attempt(lexemeDbl(fracfloat)).Map(static x => (Right(x.Value), x.BeginPos, x.EndPos, x.BeginIndex, x.EndIndex)), integer.Map(static x => (Left(x.Value), x.BeginPos, x.EndPos, x.BeginIndex, x.EndIndex))).label("number"); var reservedOp = fun((string name) => lexemeUnit( attempt( from n in str(name) from _ in notFollowedBy(def.OpLetter).label("end of " + name) select unit))); var oper = (from c in def.OpStart from cs in many(def.OpLetter) select new string(c.Cons(cs).ToArray())) .label("operator"); var operator_ = lexemeStr( attempt( from name in oper from op in def.ReservedOpNames.Contains(name) ? unexpected("reserved operator " + name) : result(name) select op)); var theReservedNames = def.CaseSensitive ? def.ReservedNames : def.ReservedNames.Map(x => x.ToLower()).ToLst(); var isReserved = fun((Lst names, string name) => names.Contains( def.CaseSensitive ? name : name.ToLower())); isReservedName = fun((string name) => isReserved(theReservedNames, name)); var ident = (from c in def.IdentStart from cs in many(def.IdentLetter) select new string(c.Cons(cs).ToArray())) .label("identifier"); var identifier = lexemeStr( attempt( from name in ident from r in isReservedName(name) ? unexpected("reserved word " + name) : result(name) select r)); var caseString = fun((string name) => str(name)); //def.CaseSensitive // ? str(name) // : /* TODO - fully implement case insensitive version*/); var reserved = fun((string name) => lexemeStr( attempt( from x in caseString(name) from _ in notFollowedBy(def.IdentLetter).label("end of " + name) select x))); return new GenTokenParser2( identifier, reserved, operator_, reservedOp, charLiteral, stringLiteral, natural, integer, float_, naturalOrFloat, lexemeInt(dec), lexemeInt(hexadecimal), lexemeInt(octal), symbol, whiteSpace, semi, comma, colon, dot ); } static Func, Parser> lexemeDef_OLD(Parser whiteSpace) { var ws = whiteSpace; return (Parser p) => from x in p from _ in ws select x; } internal static Func, Parser<(A Value, Pos BeginPos, Pos EndPos, int BeginIndex, int EndIndex)>> lexemeDef(Parser whiteSpace) => p => new Parser<(A T, Pos BeginPos, Pos EndPos, int BeginIndex, int EndIndex)>( inp => { var bi = inp.Index; var bp = inp.Pos; var xr = p(inp); if (xr.Reply.Tag == ReplyTag.OK) { var ei = xr.Reply.State.Index; var ep = xr.Reply.State.Pos; var wr = whiteSpace(xr.Reply.State); return (wr.Reply.Tag, wr.Tag) switch { (ReplyTag.OK, ResultTag.Consumed) => ParserResult.ConsumedOK((xr.Reply.Result, bp, ep, bi, ei), wr.Reply.State, xr.Reply.Error), (ReplyTag.OK, ResultTag.Empty) => ParserResult.ConsumedOK((xr.Reply.Result, bp, ep, bi, ei), wr.Reply.State, xr.Reply.Error), (ReplyTag.Error, ResultTag.Consumed) => ParserResult.ConsumedError<(A T, Pos BeginPos, Pos EndPos, int BeginIndex, int EndIndex)>(wr.Reply.Error), (ReplyTag.Error, ResultTag.Empty) => ParserResult.EmptyError<(A T, Pos BeginPos, Pos EndPos, int BeginIndex, int EndIndex)>(wr.Reply.Error), _ => throw new NotSupportedException() }; } else { return xr.Tag switch { ResultTag.Consumed => ParserResult.ConsumedError<(A T, Pos BeginPos, Pos EndPos, int BeginIndex, int EndIndex)>(xr.Reply.Error), ResultTag.Empty => ParserResult.EmptyError<(A T, Pos BeginPos, Pos EndPos, int BeginIndex, int EndIndex)>(xr.Reply.Error), _ => throw new NotSupportedException() }; } }); static Parser parseEsc(char c, A code) => ch(c).Map(_ => code); static readonly Seq<(char, char)> escMap = toSeq(List.zip("abfnrtv\\\"\'", "\a\b\f\n\r\t\v\\\"\'").ToArray()); } ================================================ FILE: LanguageExt.Parsec/Pipes.cs ================================================ using System; using LanguageExt.Common; using LanguageExt.Pipes; using LanguageExt.Parsec; using static LanguageExt.Prelude; using LanguageExt.Traits; namespace LanguageExt; public static class ParsecPipes { /// /// Pipe a string to a PString /// public static PipeT toParserStringT() where M : MonadIO => PipeT.map(x => PString.Zero.SetValue(x)); /// /// Pipe tokens to a PString /// public static PipeT, M, Unit> toTokenStringT(Func? tokenPos) where M : MonadIO => PipeT.map>(xs => new PString(xs, 0, xs.Length, None, tokenPos ?? (_ => Pos.Zero))); /// /// Pipe a string to a PString /// public static Pipe toParserString() => Pipe.map(x => PString.Zero.SetValue(x)); /// /// Pipe tokens to a PString /// public static Pipe, Unit> toTokenString(Func? tokenPos) => Pipe.map>(xs => new PString(xs, 0, xs.Length, None, tokenPos ?? (_ => Pos.Zero))); /// /// Convert a parser to a pipe that awaits a PString and yields the result of the parse operation /// If the parser fails then the pipe fails /// public static PipeT ToPipeT(this Parser ma) where M : MonadIO => from t in PipeT.awaiting() from r in ma.Parse(t).ToEither() switch { Either.Right (var x) => IO.pure(x), Either.Left (var e) => IO.fail(Errors.ParseError(e)), _ => throw new NotSupportedException() } from _ in PipeT.yield(r) select unit; /// /// Convert a parser to a pipe that awaits a string and yields the result of the parse operation /// The value is only forwarded if the parsing succeeds /// public static PipeT, OUT, M, Unit> ToPipeT(this Parser ma) where M : MonadIO => from t in PipeT.awaiting, OUT>() from _ in ma.Parse(t).ToEither() switch { Either.Right (var x) => PipeT.yield, OUT>(x), Either.Left => Pure(default), _ => throw new NotSupportedException() } select unit; /// /// Convert a parser to a pipe that awaits a PString and yields the result of the parse operation /// If the parser fails then the pipe fails /// public static Pipe ToPipe(this Parser ma) => from t in Pipe.awaiting() from r in ma.Parse(t).ToEither() switch { Either.Right (var x) => IO.pure(x), Either.Left (var e) => IO.fail(Errors.ParseError(e)), _ => throw new NotSupportedException() } from _ in Pipe.yield(r) select unit; /// /// Convert a parser to a pipe that awaits a string and yields the result of the parse operation /// The value is only forwarded if the parsing succeeds /// public static Pipe, OUT, Unit> ToPipe(this Parser ma) => from t in Pipe.awaiting, OUT>() from _ in ma.Parse(t).ToEither() switch { Either.Right (var x) => Pipe.yield, OUT>(x), Either.Left => Pure(default), _ => throw new NotSupportedException() } select unit; } ================================================ FILE: LanguageExt.Parsec/Pos.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace LanguageExt.Parsec { /// /// Represents a parser source position /// public class Pos : IEquatable, IComparable { public readonly int Line; public readonly int Column; public Pos(int line, int column) { Line = line; Column = column; } public static readonly Pos Zero = new Pos(0, 0); public bool Equals(Pos? other) => other is not null && Line == other.Line && Column == other.Column; public override bool Equals(object? obj) => ((obj as Pos)?.Equals(this)).GetValueOrDefault(); public override int GetHashCode() => Tuple.Create(Line, Column).GetHashCode(); public override string ToString() => $"(line {Line + 1}, column {Column + 1})"; public int CompareTo(Pos? other) => other is null ? 1 : Line < other.Line ? -1 : Line > other.Line ? 1 : Column.CompareTo(other.Column); public static bool operator ==(Pos lhs, Pos rhs) => lhs.Equals(rhs); public static bool operator !=(Pos lhs, Pos rhs) => !lhs.Equals(rhs); public static bool operator < (Pos lhs, Pos rhs) => lhs.CompareTo(rhs) < 0; public static bool operator >(Pos lhs, Pos rhs) => lhs.CompareTo(rhs) > 0; public static bool operator <=(Pos lhs, Pos rhs) => lhs.CompareTo(rhs) <= 0; public static bool operator >=(Pos lhs, Pos rhs) => lhs.CompareTo(rhs) >= 0; public static R Compare( Pos lhs, Pos rhs, Func EQ, Func GT, Func LT ) { var res = lhs.CompareTo(rhs); if( res < 0) { return LT(); } if (res > 0) { return GT(); } return EQ(); } } } ================================================ FILE: LanguageExt.Parsec/README.nuget.md ================================================ # LanguageExt.Parsec `LanguageExt.Parsec` are the parser-combinators for the [language-ext functional programming framework](https://github.com/louthy/language-ext). The framework uses and abuses the features of C# to provide a pure functional-programming 'Base Class Library' that, if you squint, can look like extensions to the language itself. The desire here is to make programming in C# much more robust by helping the engineer's inertia flow in the direction of declarative and pure functional code rather than imperative. Using these techniques for large code-bases can bring tangible benefits to long-term maintenance by removing hidden complexity and by easing the engineer's cognitive load. ## Features ### [Functional effects and IO](https://louthy.github.io/language-ext/LanguageExt.Core/Effects/index.html) | Location | Feature | Description | |----------|--------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `Core` | `IO` | [A synchronous and asynchronous side-effect: an IO monad](https://louthy.github.io/language-ext/LanguageExt.Core/Effects/IO/index.html) | | `Core` | `Eff` | [A synchronous and asynchronous side-effect with error handling](https://louthy.github.io/language-ext/LanguageExt.Core/Effects/Eff/Eff%20no%20runtime/index.html) | | `Core` | `Eff` | [Same as `Eff` but with an injectable runtime for dependency-injection: a unit testable IO monad](https://louthy.github.io/language-ext/LanguageExt.Core/Effects/Eff/Eff%20with%20runtime/index.html) | | `Core` | Pipes | [A clean and powerful stream processing system that lets you build and connect reusable streaming components](https://louthy.github.io/language-ext/LanguageExt.Core/Effects/Pipes/index.html) | | `Core` | StreamT | [less powerful (than Pipes), but easier to use streaming effects transformer](https://louthy.github.io/language-ext/LanguageExt.Core/Effects/StreamT/index.html) | ### [Atomic concurrency and collections](https://louthy.github.io/language-ext/LanguageExt.Core/Concurrency/index.html) | Location | Feature | Description | |----------|------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------| | `Core` | `Atom` | [A lock-free atomically mutable reference for working with shared state](https://louthy.github.io/language-ext/LanguageExt.Core/Concurrency/Atom) | | `Core` | `Ref` | [An atomic reference to be used in the transactional memory system](https://louthy.github.io/language-ext/LanguageExt.Core/Concurrency/STM) | | `Core` | `AtomHashMap` | [An immutable `HashMap` with a lock-free atomically mutable reference](https://louthy.github.io/language-ext/LanguageExt.Core/Concurrency/AtomHashMap) | | `Core` | `AtomSeq` | [An immutable `Seq` with a lock-free atomically mutable reference](https://louthy.github.io/language-ext/LanguageExt.Core/Concurrency/AtomSeq) | | `Core` | `VectorClock` | [Understand distributed causality](https://louthy.github.io/language-ext/LanguageExt.Core/Concurrency/VectorClock) | | `Core` | `VersionVector` | [A vector clock with some versioned data](https://louthy.github.io/language-ext/LanguageExt.Core/Concurrency/VersionVector) | | `Core` | `VersionHashMap ` | [Distrubuted atomic versioning of keys in a hash-map](https://louthy.github.io/language-ext/LanguageExt.Core/Concurrency/VersionHashMap) | ### [Immutable collections](https://louthy.github.io/language-ext/LanguageExt.Core/Immutable%20Collections/index.html) | Location | Feature | Description | |----------|----------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `Core` | `Arr` | [Immutable array](https://louthy.github.io/language-ext/LanguageExt.Core/Immutable%20Collections/Arr/index.html) | | `Core` | `Seq` | [Lazy immutable list, evaluate at-most-once](https://louthy.github.io/language-ext/LanguageExt.Core/Immutable%20Collections/Seq/index.html) - very, very fast! | | `Core` | `Iterable` | [Wrapper around `IEnumerable` with support for traits](https://louthy.github.io/language-ext/LanguageExt.Core/Immutable%20Collections/Iterable/index.html) - enables the higher-kinded traits to work with enumerables. | | `Core` | `Lst` | [Immutable list](https://louthy.github.io/language-ext/LanguageExt.Core/Immutable%20Collections/List/index.html) - use `Seq` over `Lst` unless you need `InsertAt` | | `Core` | `Map` | [Immutable map](https://louthy.github.io/language-ext/LanguageExt.Core/Immutable%20Collections/Map/index.html) | | `Core` | `Map` | [Immutable map with Ord constraint on `K`](https://louthy.github.io/language-ext/LanguageExt.Core/Immutable%20Collections/Map/index.html) | | `Core` | `HashMap` | [Immutable hash-map](https://louthy.github.io/language-ext/LanguageExt.Core/Immutable%20Collections/HashMap/index.html) | | `Core` | `HashMap` | [Immutable hash-map with Eq constraint on `K`](https://louthy.github.io/language-ext/LanguageExt.Core/Immutable%20Collections/HashMap/index.html) | | `Core` | `Set` | [Immutable set](https://louthy.github.io/language-ext/LanguageExt.Core/Immutable%20Collections/Set/index.html) | | `Core` | `Set` | [Immutable set with Ord constraint on `A`](https://louthy.github.io/language-ext/LanguageExt.Core/Immutable%20Collections/Set/index.html) | | `Core` | `HashSet` | [Immutable hash-set](https://louthy.github.io/language-ext/LanguageExt.Core/Immutable%20Collections/HashSet/index.html) | | `Core` | `HashSet` | [Immutable hash-set with Eq constraint on `A`](https://louthy.github.io/language-ext/LanguageExt.Core/Immutable%20Collections/HashSet/index.html) | | `Core` | `Que` | [Immutable queue](https://louthy.github.io/language-ext/LanguageExt.Core/Immutable%20Collections/Queue/index.html) | | `Core` | `Stck` | [Immutable stack](https://louthy.github.io/language-ext/LanguageExt.Core/Immutable%20Collections/Stack/index.html) | ### [Optional and alternative value monads](https://louthy.github.io/language-ext/LanguageExt.Core/Monads/Alternative%20Monads/index.html) | Location | Feature | Description | |----------|---------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `Core` | `Option` | [Option monad](https://louthy.github.io/language-ext/LanguageExt.Core/Monads/Alternative%20Monads/Option/index.html) | | `Core` | `OptionT` | [Option monad-transformer](https://louthy.github.io/language-ext/LanguageExt.Core/Monads/Alternative%20Monads/OptionT/index.html) | | `Core` | `Either` | [Right/Left choice monad](https://louthy.github.io/language-ext/LanguageExt.Core/Monads/Alternative%20Monads/Either/index.html) | | `Core` | `EitherT` | [Right/Left choice monad-transformer](https://louthy.github.io/language-ext/LanguageExt.Core/Monads/Alternative%20Monads/EitherT/index.html) | | `Core` | `Fin` | [`Error` handling monad, like `Either`](https://louthy.github.io/language-ext/LanguageExt.Core/Monads/Alternative%20Monads/Fin/index.html) | | `Core` | `FinT` | [`Error` handling monad-transformer](https://louthy.github.io/language-ext/LanguageExt.Core/Monads/Alternative%20Monads/FinT/index.html) | | `Core` | `Try` | [Exception handling monad](https://louthy.github.io/language-ext/LanguageExt.Core/Monads/Alternative%20Monads/Try/index.html) | | `Core` | `TryT` | [Exception handling monad-transformer](https://louthy.github.io/language-ext/LanguageExt.Core/Monads/Alternative%20Monads/TryT/index.html) | | `Core` | `Validation` | [Validation applicative and monad](https://louthy.github.io/language-ext/LanguageExt.Core/Monads/Alternative%20Monads/Validation/index.html) for collecting multiple errors before aborting an operation | | `Core` | `ValidationT` | [Validation applicative and monad-transformer](https://louthy.github.io/language-ext/LanguageExt.Core/Monads/Alternative%20Monads/ValidationT/index.html) | ### [State managing monads](https://louthy.github.io/language-ext/LanguageExt.Core/Monads/State%20and%20Environment%20Monads/index.html) | Location | Feature | Description | |----------|--------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `Core` | `Reader` | [Reader monad](https://louthy.github.io/language-ext/LanguageExt.Core/Monads/State%20and%20Environment%20Monads/Reader/Reader/index.html) | | `Core` | `ReaderT` | [Reader monad-transformer](https://louthy.github.io/language-ext/LanguageExt.Core/Monads/State%20and%20Environment%20Monads/Reader/ReaderT/index.html) | | `Core` | `Writer` | [Writer monad that logs to a `W` constrained to be a Monoid](https://louthy.github.io/language-ext/LanguageExt.Core/Monads/State%20and%20Environment%20Monads/Writer/Writer/index.html) | | `Core` | `WriterT` | [Writer monad-transformer](https://louthy.github.io/language-ext/LanguageExt.Core/Monads/State%20and%20Environment%20Monads/Writer/WriterT/index.html) | | `Core` | `State` | [State monad](https://louthy.github.io/language-ext/LanguageExt.Core/Monads/State%20and%20Environment%20Monads/State/State/index.html) | | `Core` | `StateT` | [State monad-transformer](https://louthy.github.io/language-ext/LanguageExt.Core/Monads/State%20and%20Environment%20Monads/State/StateT/index.html) | ### [Parser combinators](https://louthy.github.io/language-ext/LanguageExt.Parsec/index.html) | Location | Feature | Description | |----------|----------------|--------------------------------------------------------------------------------------------------------------------------------| | `Parsec` | `Parser` | [String parser monad and full parser combinators library](https://louthy.github.io/language-ext/LanguageExt.Parsec/index.html) | | `Parsec` | `Parser` | [Parser monad that can work with any input stream type](https://louthy.github.io/language-ext/LanguageExt.Parsec/index.html) | ### [Pretty](https://louthy.github.io/language-ext/LanguageExt.Core/Pretty/index.html) | Location | Feature | Description | |----------|----------|--------------------------------------------------| | `Core` | `Doc` | Produce nicely formatted text with smart layouts | ### [Differencing](https://louthy.github.io/language-ext/LanguageExt.Core/DataTypes/Patch/index.html) | Location | Feature | Description | |----------|-----------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `Core` | `Patch` | Uses patch-theory to efficiently calculate the difference (`Patch.diff(list1, list2)`) between two collections of `A` and build a patch which can be applied (`Patch.apply(patch, list)`) to one to make the other (think git diff). | ### [Traits](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/index.html) The traits are major feature of `v5`+ language-ext that makes generic programming with higher-kinds a reality. Check out Paul's [series on Higher Kinds](https://paullouth.com/higher-kinds-in-c-with-language-ext/) to get a deeper insight. | Location | Feature | Description | |----------|----------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `Core` | `MonoidK` | [A monoid on applicative functors](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Alternative/index.html) | | `Core` | `Applicative` | [Applicative functor](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Applicative/index.html) | | `Core` | `Eq` | [Ad-hoc equality trait](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Eq/index.html) | | `Core` | `Fallible` | [Trait that describes types that can fail](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Fallible/index.html) | | `Core` | `Foldable` | [Aggregation over a structure](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Foldable/index.html) | | `Core` | `Functor` | [Functor `Map`](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Functor/index.html) | | `Core` | `Has` | [Used in runtimes to enable DI-like capabilities](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Has/index.html) | | `Core` | `Hashable` | [Ad-hoc has-a-hash-code trait](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Hashable/index.html) | | `Core` | `Local` | [Creates a local environment to run a computation ](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Local/index.html) | | `Core` | `Monad` | [Monad trait](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Monads/Monad/index.html) | | `Core` | `MonadT` | [Monad transformer trait](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Monads/MonadT/index.html) | | `Core` | `Monoid` | [A monoid is a type with an identity `Empty` and an associative binary operation `+`](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Monoid/index.html) | | `Core` | `MonoidK` | [Equivalent of monoids for working on higher-kinded types](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/MonoidK/index.html) | | `Core` | `Mutates` | [Used in runtimes to enable stateful operations](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Mutates/index.html) | | `Core` | `Ord` | [Ad-hoc ordering / comparisons](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Ord/index.html) | | `Core` | `Range` | [Abstraction of a range of values](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Range/index.html) | | `Core` | `Readable` | [Generalised Reader monad abstraction](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Readable/index.html) | | `Core` | `SemigroupK` | [A semigroup on functors](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/SemigroupK/index.html) | | `Core` | `Semigroup` | [Provides an associative binary operation `+`](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Semigroup/index.html) | | `Core` | `SemigroupK` | [Equivalent of semigroups for working with higher-kinded types](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/SemigroupK/index.html) | | `Core` | `Stateful` | [Generalised State monad abstraction](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Stateful/index.html) | | `Core` | `Traversable` | [Traversable structures support element-wise sequencing of Applicative effects](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Traversable/index.html) | | `Core` | `Writable` | [Generalised Writer monad abstraction](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Writable/index.html) | ### [Value traits](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Domain/index.html) These work a little like `NewType` but they impart semantic meaning and some common operators for the underlying value. | Location | Feature | Description | |----------|--------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `Core` | `DomainType` | Provides a mapping from `SELF` to an underlying representation: `REPR` | | `Core` | `Identifier ` | Identifiers (like IDs in databases: `PersonId` for example), they are equivalent to `DomaintType` with equality. | | `Core` | `VectorSpace` | Scalable values; can add and subtract self, but can only multiply and divide by a scalar. Can also negate. | | `Core` | `Amount ` | Quantities, such as the amount of money in USD on a bank account or a file size in bytes. Derives `VectorSpace`, `IdentifierLike`, `DomainType`, and is orderable (comparable). | | `Core` | `LocusLike ` | Works with space-like structures. Spaces have absolute and relative distances. Has an origin/zero point and derives `DomainType`, `IdentifierLike`, `AmountLike` and `VectorSpace`. `DISTANCE` must also be an `AmountLike`. | ================================================ FILE: LanguageExt.Parsec/Reply.cs ================================================ using System; using static LanguageExt.Prelude; using System.Diagnostics; namespace LanguageExt.Parsec { public enum ReplyTag { OK, Error } public static partial class Reply { public static Reply OK(T result, PString remaining, ParserError? error = null) => new Reply(result, remaining, error); public static Reply Error(ParserErrorTag tag, Pos pos, string message, Lst expected) => new Reply(new ParserError(tag, pos, message, expected)); public static Reply Error(ParserError error) => new Reply(error); } public class Reply { public readonly ReplyTag Tag; public readonly T? Result; public readonly PString? State; public readonly ParserError? Error; internal Reply(ParserError error) { Tag = ReplyTag.Error; Error = error; State = PString.Zero; } internal Reply(T result, PString state, ParserError? error = null) { Debug.Assert(notnull(result)); Tag = ReplyTag.OK; State = state; Result = result; Error = error; } internal Reply(ReplyTag tag, T result, PString state, ParserError error) { Tag = tag; Result = result; State = state; Error = error; } public Reply Project(S s, Func project) => Tag == ReplyTag.Error ? Reply.Error(Error!) : Reply.OK(project(s, Result!), State!, Error); public Reply Select(Func map) => Tag == ReplyTag.Error ? Reply.Error(Error!) : Reply.OK(map(Result!), State!, Error); internal Reply SetEndIndex(int endIndex) => new Reply(Tag, Result!, State!.SetEndIndex(endIndex), Error!); } } ================================================ FILE: LanguageExt.Parsec/ReplyIO.cs ================================================ using System; using static LanguageExt.Prelude; using System.Diagnostics; namespace LanguageExt.Parsec { public static partial class Reply { public static Reply OK(O result, PString remaining, ParserError? error = null) => new Reply(result, remaining, error); public static Reply Error(ParserErrorTag tag, Pos pos, string message, Lst expected, Func tokenPos) => new Reply(new ParserError(tag, pos, message, expected), tokenPos); public static Reply Error(ParserError error, Func tokenPos) => new Reply(error, tokenPos); } public class Reply { public readonly ReplyTag Tag; public readonly O? Result; public readonly PString State; public readonly ParserError? Error; internal Reply(ParserError error, Func tokenPos) { Tag = ReplyTag.Error; Error = error; State = PString.Zero(tokenPos); } internal Reply(O result, PString state, ParserError? error = null) { Debug.Assert(notnull(result)); Tag = ReplyTag.OK; State = state; Result = result; Error = error; } internal Reply(ReplyTag tag, O result, PString state, ParserError error) { Tag = tag; Result = result; State = state; Error = error; } public Reply Project(S s, Func project) => Tag == ReplyTag.Error ? Reply.Error(Error!, State.TokenPos) : Reply.OK(project(s, Result!), State, Error); public Reply Select(Func map) => Tag == ReplyTag.Error ? Reply.Error(Error!, State.TokenPos) : Reply.OK(map(Result!), State, Error); internal Reply SetEndIndex(int endIndex) => new Reply(Tag, Result!, State.SetEndIndex(endIndex), Error!); } } ================================================ FILE: LanguageExt.Parsec/Sidedness.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace LanguageExt.Parsec { public enum Sidedness { Onside, Offside } } ================================================ FILE: LanguageExt.Parsec/StringAndCollectionExt.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace LanguageExt.Parsec { public static class StringAndCollectionExtensions { public static PString ToPString(this string value) => PString.Zero.SetValue(value); public static PString ToPString(this IEnumerable value, Func tokenPos) => PString.Zero(tokenPos).SetValue(value.ToArray()); public static PString ToPString(this Seq value, Func tokenPos) => PString.Zero(tokenPos).SetValue(value.ToArray()); } } ================================================ FILE: LanguageExt.Rx/Atom.Extensions.cs ================================================ using System; using System.Diagnostics.Contracts; using System.Reactive.Linq; using LanguageExt.Traits; namespace LanguageExt; public static class AtomExtensions { /// /// Observe changes to the `Atom` /// /// Atom to observe /// Value type /// Observable atom [Pure] public static IObservable OnChange(this Atom atom) => Observable.FromEvent( add => atom.Change += new AtomChangedEvent(add), remove => atom.Change -= new AtomChangedEvent(remove)); /// /// Creates an observable that watches for changes to the `Atom`, but /// also gets primed with the initial state on subscription, so there's /// always at least one value that flows downstream. /// /// Atom to observe /// Value type /// Observable atom [Pure] public static IObservable Latest(this Atom atom) => Observable.Return(atom.Value) .Merge(atom.OnChange()); /// /// Observe changes to the `Atom` /// /// Atom to observe /// Value type /// Observable atom [Pure] public static IObservable OnChange(this Atom atom) => Observable.FromEvent( add => atom.Change += new AtomChangedEvent(add), remove => atom.Change -= new AtomChangedEvent(remove)); /// /// Creates an observable that watches for changes to the `Atom`, but /// also gets primed with the initial state on subscription, so there's /// always at least one value that flows downstream. /// /// Atom to observe /// Value type /// Observable atom [Pure] public static IObservable Latest(this Atom atom) => Observable.Return(atom.Value) .Merge(atom.OnChange()); /// /// Observe changes to the `Ref` /// /// Ref to observe /// Value type /// Observable ref [Pure] public static IObservable OnChange(this Ref atom) => Observable.FromEvent( add => atom.Change += new AtomChangedEvent(add), remove => atom.Change -= new AtomChangedEvent(remove)); /// /// Creates an observable that watches for changes to the `Atom`, but /// also gets primed with the initial state on subscription, so there's /// always at least one value that flows downstream. /// /// Atom to observe /// Value type /// Observable atom [Pure] public static IObservable Latest(this Ref atom) => Observable.Return(atom.Value) .Merge(atom.OnChange()); /// /// Observe changes to the `AtomHashMap` /// /// This publishes the full patch of a change, which may contain multiple /// key updates (if done from within a transaction for-example). /// `AtomHashMap` to observe /// Value type /// Observable `AtomHashMap` [Pure] public static IObservable> OnChange(this AtomHashMap atom) => Observable.FromEvent>( add => atom.Change += new AtomHashMapChangeEvent(add), remove => atom.Change -= new AtomHashMapChangeEvent(remove)); /// /// Observe changes to the `AtomHashMap` /// /// This publishes the full patch of a change, which may contain multiple /// key updates (if done from within a transaction for-example). /// `AtomHashMap` to observe /// Value type /// Observable `AtomHashMap` [Pure] public static IObservable> OnChange( this AtomHashMap atom) where EqK : Eq => Observable.FromEvent>( add => atom.Change += new AtomHashMapChangeEvent(add), remove => atom.Change -= new AtomHashMapChangeEvent(remove)); /// /// Observe changes to the `AtomHashMap` /// /// This publishes the changes to individual key-values within the `AtomHashMap` /// Value type /// Observable `(K, Change〈V〉)` [Pure] public static IObservable<(K, Change)> OnEntryChange(this AtomHashMap atom) => atom.OnChange() .SelectMany(static p => p.Changes .AsIterable() .Filter(static c => c.Value.HasChanged)); /// /// Observe changes to the `AtomHashMap` /// /// This publishes the changes to individual key-values within the `AtomHashMap` /// Value type /// Observable `(K, Change〈V〉)` [Pure] public static IObservable<(K, Change)> OnEntryChange(this AtomHashMap atom) where EqK : Eq => atom.OnChange() .SelectMany(static p => p.Changes .AsEnumerable() .Filter(static c => c.Value.HasChanged)); /// /// Observe changes to the `AtomHashMap` /// /// This publishes the latest state of an `AtomHashMap` /// `AtomHashMap` to observe /// Value type /// Observable `HashMap` [Pure] public static IObservable> OnMapChange(this AtomHashMap atom) where EqK : Eq => atom.OnChange().Select(p => p.To); } ================================================ FILE: LanguageExt.Rx/Cast.cs ================================================  namespace LanguageExt { internal static class CastExtensions { public static A Cast(this Option ma) => (A)ma; public static L CastLeft(this Either ma) => (L)ma; public static R CastRight(this Either ma) => (R)ma; } } ================================================ FILE: LanguageExt.Rx/Check.cs ================================================ namespace LanguageExt { internal static class Check { internal static T NullReturn(T value) => Prelude.isnull(value) ? Prelude.raise(new ResultIsNullException()) : value; } } ================================================ FILE: LanguageExt.Rx/Eff.Extensions.cs ================================================ using System; using System.Collections.Concurrent; using System.Threading; using LanguageExt.Common; using LanguageExt.Effects; using static LanguageExt.Prelude; namespace LanguageExt; public static class EffRxExtensions { /// /// Consume an observable stream /// /// Each item is passed to the `next` function. When the stream completes the Aff returns /// which allows subsequent operations to be composed. /// Observable to consume /// Next function to call /// Cancellation token /// Bound type /// Aff of unit public static Eff Consume( this IObservable ma, Func> next) => from t in cancelTokenEff from _ in Eff.Lift( () => { using var wait = new AutoResetEvent(false); var items = new ConcurrentQueue(); var done = false; Exception? exception = default; using var disp = ma.Subscribe( onNext: x => { items.Enqueue(x); wait.Set(); }, onError: e => { exception = e; done = true; wait.Set(); }, onCompleted: () => { done = true; wait.Set(); }); while (true) { wait.WaitOne(); if (done) { return unit; } if (t.IsCancellationRequested) { return Errors.Cancelled; } if (exception != null) { return Error.New(exception); } while (items.TryDequeue(out var item)) { var res = next(item).Run(); if (res.IsFail) return Fin.Fail((Error)res); } } }) select unit; /// /// Fold an observable stream /// /// Each item is passed to the `next` function, with a state value. The state is aggregated over the /// stream by passing the return value from `next` to the subsequent calls. When the stream completes, the /// aggregate state is returned. /// /// Observable to fold /// Next function to call /// Bound type /// Cancellation token /// Aff of S public static Eff Fold(this IObservable ma, S initialState, Func> next) { var state = Atom(initialState); return ma.Consume(a => next(state, a).Map(x => ignore(state.Swap(_ => x)))) .Map(_ => state.Value); } } ================================================ FILE: LanguageExt.Rx/Either.Extensions.cs ================================================ using System; using System.Diagnostics.Contracts; using System.Reactive.Linq; using static LanguageExt.Prelude; namespace LanguageExt; public static class EitherRxExtensions { public static IObservable ToObservable(this Either ma) => ma.Match(_ => Observable.Empty(), Observable.Return); /// /// Match the two states of the Either and return a stream of non-null R2s. /// [Pure] public static IObservable MatchObservable(this Either> self, Func Right, Func Left) => self.IsRight ? self.CastRight().Select(Right).Select(Check.NullReturn) : Observable.Return(Check.NullReturn(Left(self.CastLeft()))); /// /// Match the two states of the IObservable Either and return a stream of non-null R2s. /// [Pure] public static IObservable MatchObservable(this IObservable> self, Func Right, Func Left) => self.Select(either => either.Match(Left, Right)); /// /// Match the two states of the Either and return an observable stream of non-null R2s. /// [Pure] public static IObservable MatchObservable(this Either ma, Func> Right, Func Left) => ma.Match(l => Observable.Return(Left(l)), Right); /// /// Match the two states of the Either and return an observable stream of non-null R2s. /// [Pure] public static IObservable MatchObservable(this Either ma, Func> Right, Func> Left) => ma.Match(Left, Right); } ================================================ FILE: LanguageExt.Rx/EitherUnsafe.Extensions.cs ================================================ using System; using System.Collections.Generic; using System.Text; using System.Diagnostics; namespace LanguageExt { public class EitherUnsafeRxExtensions { public static IObservable ToObservable(this EitherUnsafe ma) => ma.Match(Observable.Return, _ => Observable.Empty()); /// /// Match the two states of the Either and return a stream of non-null R2s. /// [Pure] public static IObservable MatchObservable(this EitherUnsafe> self, Func Right, Func Left) => self.IsRight ? self.CastRight().Select(Right).Select(Check.NullReturn) : Observable.Return(Check.NullReturn(Left(self.CastLeft()))); /// /// Match the two states of the IObservable Either and return a stream of non-null R2s. /// [Pure] public static IObservable MatchObservable(this IObservable> self, Func Right, Func Left) => self.Select(either => matchUnsafe(either, Right, Left)); /// /// Match the two states of the Either and return an observable stream of non-null R2s. /// [Pure] public static IObservable MatchObservable(this EitherUnsafe ma, Func> Right, Func Left) => ma.Match(Right, l => Observable.Return(Left(l))); /// /// Match the two states of the Either and return an observable stream of non-null R2s. /// [Pure] public static IObservable MatchObservable(this EitherUnsafe ma, Func> Right, Func> Left) => ma.Match(Right, Left); } } ================================================ FILE: LanguageExt.Rx/LanguageExt.Rx.csproj ================================================ TRACE;DEBUG 1701;1702;1705;IDE1006;CS1591;CS1573;CS1712;CS1711;CS1572;CS1587 CONTRACTS_FULL enable net10.0 5.0.0-beta-77 LanguageExt.Rx LanguageExt.Rx Paul Louth Functional language extensions for C# Copyright (c) Paul Louth. All rights reserved. README.nuget.md Support for Reactive Extensions overloads for various types in the LanguageExt functional framework C#, Functional, Language Extension, Monad, Option, Either, Reader, Writer, State, List, Set, Map, Queue, Memo, Memoization, Immutable, Lambda, Pattern Matching, Tuple lang-ext-small.png https://github.com/louthy/language-ext MIT false bin\$(Configuration)\$(TargetFramework)\$(AssemblyName).xml library 5.0.0.0 5.0.0.0 default ================================================ FILE: LanguageExt.Rx/Option.Extensions.cs ================================================ using System; using System.Diagnostics.Contracts; using System.Reactive.Linq; namespace LanguageExt; public static class OptionRxExtensions { public static IObservable Choose(this IObservable ma, Func> f) => ma.Select(f) .Where(x => x.IsSome) .Select(x => (B)x); public static IObservable ToObservable(this Option ma) => ma.IsSome ? Observable.Return(ma.Cast()) : Observable.Empty(); /// /// Match the two states of the Option and return an observable stream of non-null Rs. /// /// Return type /// Some handler. Must not return null. /// None handler. Must not return null. /// A stream of non-null Rs [Pure] public static IObservable MatchObservable(this Option ma, Func> Some, Func None) => ma.IsSome ? Some(ma.Cast()) : Observable.Return(None()); /// /// Match the two states of the Option and return an observable stream of non-null Rs. /// /// Return type /// Some handler. Must not return null. /// None handler. Must not return null. /// A stream of non-null Rs [Pure] public static IObservable MatchObservable(this Option ma, Func> Some, Func> None) => ma.IsSome ? Some(ma.Cast()) : None(); } ================================================ FILE: LanguageExt.Rx/PreludeRx.cs ================================================ using System; using System.Reactive.Linq; using System.Reactive.Threading.Tasks; using System.Threading.Tasks; namespace LanguageExt; public static class PreludeRx { /// /// Execute a function after a specified delay /// /// Function to execute /// Time span to delay for /// IObservable T with the result public static IObservable delay(Func f, TimeSpan delayFor) => delayFor.TotalMilliseconds < 1 ? Observable.Return(f()).Take(1) : Task.Delay(delayFor).ContinueWith(_ => f()).ToObservable().Take(1); /// /// Execute a function at a specific time /// /// /// This will fail to be accurate across a Daylight Saving Time boundary /// /// Function to execute /// DateTime to wake up at. /// IObservable T with the result public static IObservable delay(Func f, DateTime delayUntil) => delay(f, delayUntil.ToUniversalTime() - DateTime.UtcNow); } ================================================ FILE: LanguageExt.Rx/README.nuget.md ================================================ # LanguageExt.Rx `LanguageExt.Rx` are the 'Reactive Extensions' extensions for the [language-ext functional programming framework](https://github.com/louthy/language-ext). The framework uses and abuses the features of C# to provide a pure functional-programming 'Base Class Library' that, if you squint, can look like extensions to the language itself. The desire here is to make programming in C# much more robust by helping the engineer's inertia flow in the direction of declarative and pure functional code rather than imperative. Using these techniques for large code-bases can bring tangible benefits to long-term maintenance by removing hidden complexity and by easing the engineer's cognitive load. ## Features ### [Functional effects and IO](https://louthy.github.io/language-ext/LanguageExt.Core/Effects/index.html) | Location | Feature | Description | |----------|--------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `Core` | `IO` | [A synchronous and asynchronous side-effect: an IO monad](https://louthy.github.io/language-ext/LanguageExt.Core/Effects/IO/index.html) | | `Core` | `Eff` | [A synchronous and asynchronous side-effect with error handling](https://louthy.github.io/language-ext/LanguageExt.Core/Effects/Eff/Eff%20no%20runtime/index.html) | | `Core` | `Eff` | [Same as `Eff` but with an injectable runtime for dependency-injection: a unit testable IO monad](https://louthy.github.io/language-ext/LanguageExt.Core/Effects/Eff/Eff%20with%20runtime/index.html) | | `Core` | Pipes | [A clean and powerful stream processing system that lets you build and connect reusable streaming components](https://louthy.github.io/language-ext/LanguageExt.Core/Effects/Pipes/index.html) | | `Core` | StreamT | [less powerful (than Pipes), but easier to use streaming effects transformer](https://louthy.github.io/language-ext/LanguageExt.Core/Effects/StreamT/index.html) | ### [Atomic concurrency and collections](https://louthy.github.io/language-ext/LanguageExt.Core/Concurrency/index.html) | Location | Feature | Description | |----------|------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------| | `Core` | `Atom` | [A lock-free atomically mutable reference for working with shared state](https://louthy.github.io/language-ext/LanguageExt.Core/Concurrency/Atom) | | `Core` | `Ref` | [An atomic reference to be used in the transactional memory system](https://louthy.github.io/language-ext/LanguageExt.Core/Concurrency/STM) | | `Core` | `AtomHashMap` | [An immutable `HashMap` with a lock-free atomically mutable reference](https://louthy.github.io/language-ext/LanguageExt.Core/Concurrency/AtomHashMap) | | `Core` | `AtomSeq` | [An immutable `Seq` with a lock-free atomically mutable reference](https://louthy.github.io/language-ext/LanguageExt.Core/Concurrency/AtomSeq) | | `Core` | `VectorClock` | [Understand distributed causality](https://louthy.github.io/language-ext/LanguageExt.Core/Concurrency/VectorClock) | | `Core` | `VersionVector` | [A vector clock with some versioned data](https://louthy.github.io/language-ext/LanguageExt.Core/Concurrency/VersionVector) | | `Core` | `VersionHashMap ` | [Distrubuted atomic versioning of keys in a hash-map](https://louthy.github.io/language-ext/LanguageExt.Core/Concurrency/VersionHashMap) | ### [Immutable collections](https://louthy.github.io/language-ext/LanguageExt.Core/Immutable%20Collections/index.html) | Location | Feature | Description | |----------|----------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `Core` | `Arr` | [Immutable array](https://louthy.github.io/language-ext/LanguageExt.Core/Immutable%20Collections/Arr/index.html) | | `Core` | `Seq` | [Lazy immutable list, evaluate at-most-once](https://louthy.github.io/language-ext/LanguageExt.Core/Immutable%20Collections/Seq/index.html) - very, very fast! | | `Core` | `Iterable` | [Wrapper around `IEnumerable` with support for traits](https://louthy.github.io/language-ext/LanguageExt.Core/Immutable%20Collections/Iterable/index.html) - enables the higher-kinded traits to work with enumerables. | | `Core` | `Lst` | [Immutable list](https://louthy.github.io/language-ext/LanguageExt.Core/Immutable%20Collections/List/index.html) - use `Seq` over `Lst` unless you need `InsertAt` | | `Core` | `Map` | [Immutable map](https://louthy.github.io/language-ext/LanguageExt.Core/Immutable%20Collections/Map/index.html) | | `Core` | `Map` | [Immutable map with Ord constraint on `K`](https://louthy.github.io/language-ext/LanguageExt.Core/Immutable%20Collections/Map/index.html) | | `Core` | `HashMap` | [Immutable hash-map](https://louthy.github.io/language-ext/LanguageExt.Core/Immutable%20Collections/HashMap/index.html) | | `Core` | `HashMap` | [Immutable hash-map with Eq constraint on `K`](https://louthy.github.io/language-ext/LanguageExt.Core/Immutable%20Collections/HashMap/index.html) | | `Core` | `Set` | [Immutable set](https://louthy.github.io/language-ext/LanguageExt.Core/Immutable%20Collections/Set/index.html) | | `Core` | `Set` | [Immutable set with Ord constraint on `A`](https://louthy.github.io/language-ext/LanguageExt.Core/Immutable%20Collections/Set/index.html) | | `Core` | `HashSet` | [Immutable hash-set](https://louthy.github.io/language-ext/LanguageExt.Core/Immutable%20Collections/HashSet/index.html) | | `Core` | `HashSet` | [Immutable hash-set with Eq constraint on `A`](https://louthy.github.io/language-ext/LanguageExt.Core/Immutable%20Collections/HashSet/index.html) | | `Core` | `Que` | [Immutable queue](https://louthy.github.io/language-ext/LanguageExt.Core/Immutable%20Collections/Queue/index.html) | | `Core` | `Stck` | [Immutable stack](https://louthy.github.io/language-ext/LanguageExt.Core/Immutable%20Collections/Stack/index.html) | ### [Optional and alternative value monads](https://louthy.github.io/language-ext/LanguageExt.Core/Monads/Alternative%20Monads/index.html) | Location | Feature | Description | |----------|---------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `Core` | `Option` | [Option monad](https://louthy.github.io/language-ext/LanguageExt.Core/Monads/Alternative%20Monads/Option/index.html) | | `Core` | `OptionT` | [Option monad-transformer](https://louthy.github.io/language-ext/LanguageExt.Core/Monads/Alternative%20Monads/OptionT/index.html) | | `Core` | `Either` | [Right/Left choice monad](https://louthy.github.io/language-ext/LanguageExt.Core/Monads/Alternative%20Monads/Either/index.html) | | `Core` | `EitherT` | [Right/Left choice monad-transformer](https://louthy.github.io/language-ext/LanguageExt.Core/Monads/Alternative%20Monads/EitherT/index.html) | | `Core` | `Fin` | [`Error` handling monad, like `Either`](https://louthy.github.io/language-ext/LanguageExt.Core/Monads/Alternative%20Monads/Fin/index.html) | | `Core` | `FinT` | [`Error` handling monad-transformer](https://louthy.github.io/language-ext/LanguageExt.Core/Monads/Alternative%20Monads/FinT/index.html) | | `Core` | `Try` | [Exception handling monad](https://louthy.github.io/language-ext/LanguageExt.Core/Monads/Alternative%20Monads/Try/index.html) | | `Core` | `TryT` | [Exception handling monad-transformer](https://louthy.github.io/language-ext/LanguageExt.Core/Monads/Alternative%20Monads/TryT/index.html) | | `Core` | `Validation` | [Validation applicative and monad](https://louthy.github.io/language-ext/LanguageExt.Core/Monads/Alternative%20Monads/Validation/index.html) for collecting multiple errors before aborting an operation | | `Core` | `ValidationT` | [Validation applicative and monad-transformer](https://louthy.github.io/language-ext/LanguageExt.Core/Monads/Alternative%20Monads/ValidationT/index.html) | ### [State managing monads](https://louthy.github.io/language-ext/LanguageExt.Core/Monads/State%20and%20Environment%20Monads/index.html) | Location | Feature | Description | |----------|--------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `Core` | `Reader` | [Reader monad](https://louthy.github.io/language-ext/LanguageExt.Core/Monads/State%20and%20Environment%20Monads/Reader/Reader/index.html) | | `Core` | `ReaderT` | [Reader monad-transformer](https://louthy.github.io/language-ext/LanguageExt.Core/Monads/State%20and%20Environment%20Monads/Reader/ReaderT/index.html) | | `Core` | `Writer` | [Writer monad that logs to a `W` constrained to be a Monoid](https://louthy.github.io/language-ext/LanguageExt.Core/Monads/State%20and%20Environment%20Monads/Writer/Writer/index.html) | | `Core` | `WriterT` | [Writer monad-transformer](https://louthy.github.io/language-ext/LanguageExt.Core/Monads/State%20and%20Environment%20Monads/Writer/WriterT/index.html) | | `Core` | `State` | [State monad](https://louthy.github.io/language-ext/LanguageExt.Core/Monads/State%20and%20Environment%20Monads/State/State/index.html) | | `Core` | `StateT` | [State monad-transformer](https://louthy.github.io/language-ext/LanguageExt.Core/Monads/State%20and%20Environment%20Monads/State/StateT/index.html) | ### [Parser combinators](https://louthy.github.io/language-ext/LanguageExt.Parsec/index.html) | Location | Feature | Description | |----------|----------------|--------------------------------------------------------------------------------------------------------------------------------| | `Parsec` | `Parser` | [String parser monad and full parser combinators library](https://louthy.github.io/language-ext/LanguageExt.Parsec/index.html) | | `Parsec` | `Parser` | [Parser monad that can work with any input stream type](https://louthy.github.io/language-ext/LanguageExt.Parsec/index.html) | ### [Pretty](https://louthy.github.io/language-ext/LanguageExt.Core/Pretty/index.html) | Location | Feature | Description | |----------|----------|--------------------------------------------------| | `Core` | `Doc` | Produce nicely formatted text with smart layouts | ### [Differencing](https://louthy.github.io/language-ext/LanguageExt.Core/DataTypes/Patch/index.html) | Location | Feature | Description | |----------|-----------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `Core` | `Patch` | Uses patch-theory to efficiently calculate the difference (`Patch.diff(list1, list2)`) between two collections of `A` and build a patch which can be applied (`Patch.apply(patch, list)`) to one to make the other (think git diff). | ### [Traits](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/index.html) The traits are major feature of `v5`+ language-ext that makes generic programming with higher-kinds a reality. Check out Paul's [series on Higher Kinds](https://paullouth.com/higher-kinds-in-c-with-language-ext/) to get a deeper insight. | Location | Feature | Description | |----------|----------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `Core` | `MonoidK` | [A monoid on applicative functors](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Alternative/index.html) | | `Core` | `Applicative` | [Applicative functor](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Applicative/index.html) | | `Core` | `Eq` | [Ad-hoc equality trait](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Eq/index.html) | | `Core` | `Fallible` | [Trait that describes types that can fail](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Fallible/index.html) | | `Core` | `Foldable` | [Aggregation over a structure](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Foldable/index.html) | | `Core` | `Functor` | [Functor `Map`](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Functor/index.html) | | `Core` | `Has` | [Used in runtimes to enable DI-like capabilities](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Has/index.html) | | `Core` | `Hashable` | [Ad-hoc has-a-hash-code trait](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Hashable/index.html) | | `Core` | `Local` | [Creates a local environment to run a computation ](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Local/index.html) | | `Core` | `Monad` | [Monad trait](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Monads/Monad/index.html) | | `Core` | `MonadT` | [Monad transformer trait](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Monads/MonadT/index.html) | | `Core` | `Monoid` | [A monoid is a type with an identity `Empty` and an associative binary operation `+`](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Monoid/index.html) | | `Core` | `MonoidK` | [Equivalent of monoids for working on higher-kinded types](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/MonoidK/index.html) | | `Core` | `Mutates` | [Used in runtimes to enable stateful operations](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Mutates/index.html) | | `Core` | `Ord` | [Ad-hoc ordering / comparisons](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Ord/index.html) | | `Core` | `Range` | [Abstraction of a range of values](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Range/index.html) | | `Core` | `Readable` | [Generalised Reader monad abstraction](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Readable/index.html) | | `Core` | `SemigroupK` | [A semigroup on functors](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/SemigroupK/index.html) | | `Core` | `Semigroup` | [Provides an associative binary operation `+`](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Semigroup/index.html) | | `Core` | `SemigroupK` | [Equivalent of semigroups for working with higher-kinded types](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/SemigroupK/index.html) | | `Core` | `Stateful` | [Generalised State monad abstraction](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Stateful/index.html) | | `Core` | `Traversable` | [Traversable structures support element-wise sequencing of Applicative effects](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Traversable/index.html) | | `Core` | `Writable` | [Generalised Writer monad abstraction](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Writable/index.html) | ### [Value traits](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Domain/index.html) These work a little like `NewType` but they impart semantic meaning and some common operators for the underlying value. | Location | Feature | Description | |----------|--------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `Core` | `DomainType` | Provides a mapping from `SELF` to an underlying representation: `REPR` | | `Core` | `Identifier ` | Identifiers (like IDs in databases: `PersonId` for example), they are equivalent to `DomaintType` with equality. | | `Core` | `VectorSpace` | Scalable values; can add and subtract self, but can only multiply and divide by a scalar. Can also negate. | | `Core` | `Amount ` | Quantities, such as the amount of money in USD on a bank account or a file size in bytes. Derives `VectorSpace`, `IdentifierLike`, `DomainType`, and is orderable (comparable). | | `Core` | `LocusLike ` | Works with space-like structures. Spaces have absolute and relative distances. Has an origin/zero point and derives `DomainType`, `IdentifierLike`, `AmountLike` and `VectorSpace`. `DISTANCE` must also be an `AmountLike`. | ================================================ FILE: LanguageExt.Rx/Validation.Extensions.cs ================================================ using System; using System.Diagnostics.Contracts; using System.Reactive.Linq; using LanguageExt.Traits; namespace LanguageExt; public static class ValidationRxExtensions { /// /// Match the two states of the Validation and return an observable stream of non-null R2s. /// [Pure] public static IObservable MatchObservable( this Validation ma, Func> Succ, Func Fail) where F : Monoid, Eq => ma.Match( Succ: Succ, Fail: x => Observable.Return(Fail(x))); /// /// Match the two states of the Validation and return an observable stream of non-null R2s. /// [Pure] public static IObservable MatchObservable( this Validation ma, Func> Succ, Func> Fail) where F : Monoid, Eq => ma.Match(Succ: Succ, Fail: Fail); } ================================================ FILE: LanguageExt.Streaming/Buffer.cs ================================================ namespace LanguageExt; /// /// Settings for `Conduit` channels /// /// Bound value type public abstract record Buffer { static Buffer() { Unbounded = new UnboundedBuffer(); Single = new SingleBuffer(); New = new NewBuffer(); } internal Buffer() { } /// /// Store an unbounded number of messages in a FIFO queue /// public static readonly Buffer Unbounded; /// /// Bounded number of messages to `1` /// public static readonly Buffer Single; /// /// `Newest(1)` /// public static readonly Buffer New; /// /// Store a bounded number of messages, specified by the 'size' argument /// /// Bounded size of the buffer public static Buffer Bounded(uint size) => size == 1 ? new SingleBuffer() : new BoundedBuffer(size); /// /// Only store the 'Latest' message, beginning with an initial value /// /// /// 'Latest' is never empty nor full. /// public static Buffer Latest(A value) => new LatestBuffer(value); /// /// Like `Bounded`, but `Post` never fails (the buffer is never full). /// Instead, old elements are discarded to make room for new elements /// /// Size of the buffer public static Buffer Newest(uint size) => size == 1 ? new NewBuffer() : new NewestBuffer(size); } public sealed record UnboundedBuffer : Buffer; public sealed record BoundedBuffer(uint Size) : Buffer; public sealed record SingleBuffer : Buffer; public sealed record LatestBuffer(A Value) : Buffer; public sealed record NewestBuffer(uint Size) : Buffer; public sealed record NewBuffer : Buffer; ================================================ FILE: LanguageExt.Streaming/Conduit/Conduit.Extensions.cs ================================================ using LanguageExt.Pipes; using LanguageExt.Traits; namespace LanguageExt; public static class ConduitExtensions { public static Conduit As(this K, B> ma) => (Conduit)ma; } ================================================ FILE: LanguageExt.Streaming/Conduit/Conduit.Module.cs ================================================ using System; using Ch = System.Threading.Channels; namespace LanguageExt; public static class Conduit { /// /// Create a new unbounded Conduit /// /// Label for debugging purposes /// Value type /// Constructed Conduit with an `Sink` and an `Source` public static Conduit make() => make(Buffer.Unbounded); /// /// Create a new Conduit with the buffer settings provided /// /// Buffer settings /// Label for debugging purposes /// Value type /// Constructed Conduit with an `Sink` and an `Source` /// Thrown for invalid buffer settings public static Conduit make(Buffer buffer) { var channel = MakeChannel(buffer); return new Conduit(Transducer.identity(), channel, Transducer.identity()); } static Ch.Channel MakeChannel(Buffer buffer) { Ch.Channel channel; switch (buffer) { case UnboundedBuffer: channel = Ch.Channel.CreateUnbounded(); break; case BoundedBuffer(var size): { var opts = new Ch.BoundedChannelOptions((int)size) { FullMode = Ch.BoundedChannelFullMode.Wait }; channel = Ch.Channel.CreateBounded(opts); break; } case SingleBuffer: { var opts = new Ch.BoundedChannelOptions(1) { FullMode = Ch.BoundedChannelFullMode.Wait }; channel = Ch.Channel.CreateBounded(opts); break; } case LatestBuffer(var initial): { var opts = new Ch.BoundedChannelOptions(1) { FullMode = Ch.BoundedChannelFullMode.DropOldest }; channel = Ch.Channel.CreateBounded(opts); channel.Writer.TryWrite(initial); break; } case NewestBuffer(var size): { var opts = new Ch.BoundedChannelOptions((int)size) { FullMode = Ch.BoundedChannelFullMode.DropOldest }; channel = Ch.Channel.CreateBounded(opts); break; } case NewBuffer: { var opts = new Ch.BoundedChannelOptions(1) { FullMode = Ch.BoundedChannelFullMode.DropOldest }; channel = Ch.Channel.CreateBounded(opts); break; } default: throw new NotSupportedException(); } return channel; } } ================================================ FILE: LanguageExt.Streaming/Conduit/Conduit.TraitImpl.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public class Conduit : Cofunctor>, Functor> { public static K, X> Comap(Func f, K, B> fb) => fb.As().Comap(f); public static K, C> Map(Func f, K, B> ma) => ma.As().Map(f); } ================================================ FILE: LanguageExt.Streaming/Conduit/Conduit.cs ================================================ using System; using LanguageExt.Common; using LanguageExt.Pipes; using LanguageExt.Traits; namespace LanguageExt; /// /// Represents a channel with an internal queue. A channel has: /// /// * A sink: an input transducer that manipulates values before being placed into the internal queue. /// * A buffer: `System.Threading.Channels.Channel`. /// * A source: an output transducer that manipulates values after being taken from the internal queue. /// /// Both sides of the conduit can be manipulated: /// /// The sink is a co-functor and can be mapped using `Comap` or `CoTransform`, these transform values _before_ they get /// to the conduit's buffer. /// /// The source is a functor, so you can `Map` or `Transform` in the usual way to map values on their way out of the /// buffer. /// /// Control of the internal buffer is provided by passing a `Buffer` value to `Conduit.make`. This allows you to set /// various parameters for the internal queue, such as the maximum number of items to hold in the queue, and what /// strategy to use when the queue is full. The default is `Buffer.Unbounded`. /// /// `ToProducer` and `ToConsumer` enable the `Conduit` components to be used in composed pipe effects. /// /// Input value type /// Output value type public abstract class Conduit : K, B> { /// /// Access the underlying `Sink` for more direct manipulation. /// /// public abstract Sink Sink { get; } /// /// Access the underlying `Source` for more direct manipulation. /// /// public abstract Source Source { get; } /// /// Post a value to the `Sink` /// /// /// Raises `Errors.SinkFull` if the `Sink` is full or closed. /// /// Value to post /// IO computation that represents the posting public abstract IO Post(A value); /// /// Complete and close the Sink /// public abstract IO Complete(); /// /// Complete and close the Sink with an `Error` /// public abstract IO Fail(Error Error); /// /// Iterate the stream, flowing values downstream to the reducer, which aggregates a /// result value. /// /// State to reduce /// Reducer /// State type /// Reduced state public abstract IO Reduce(S state, ReducerIO reducer); /// /// Iterate the stream, flowing values downstream to the reducer, which aggregates a /// result value. /// /// State to reduce /// Reducer /// State type /// Reduced state public abstract K Reduce(S state, ReducerIO reducer) where M : MonadIO; /// /// Functor map /// public abstract Conduit Map(Func f); /// /// Functor map /// public Conduit Select(Func f) => Map(f); /// /// Transform with a transducer /// /// Transducer to use to transform /// Transformed source public abstract Conduit Transform(Transducer transducer); /// /// Contravariant functor map /// public abstract Conduit Comap(Func f); /// /// Co-transform with a transducer /// /// Transducer to use to transform /// Transformed source public abstract Conduit CoTransform(Transducer transducer); /// /// Convert the `Sink` to a `ConsumerT` pipe component /// /// Monad to lift (must support `IO`) /// `ConsumerT` public abstract ConsumerT ToConsumerT() where M : MonadIO; /// /// Convert the `Sink` to a `Consumer` pipe component /// /// `Consumer` public abstract Consumer ToConsumer(); /// /// Convert `Source` to a `ProducerT` pipe component /// /// Monad to lift (must support `IO`) /// `ProducerT` public abstract ProducerT ToProducerT() where M : MonadIO; /// /// Convert `Source` to a `Producer` pipe component /// /// `Producer` public abstract Producer ToProducer(); /// /// Filter values. Yielding downstream when `true` /// /// Filter function /// SourceT where the only values yield are those that pass the predicate public Conduit Where(Func f) => Filter(f); /// /// Filter values. Yielding downstream when `true` /// /// Filter function /// SourceT where the only values yield are those that pass the predicate public abstract Conduit Filter(Func f); /// /// Skip items in the source /// /// Amount to skip /// Transformed source public abstract Conduit Skip(int amount); /// /// Limit the number of items processed /// /// Number to take /// Transformed source public abstract Conduit Take(int amount); /// /// Fold the values flowing through. Values are yielded downstream when either the predicate returns /// `false`, or the source completes. /// /// Binary operator /// Predicate /// Initial state /// State type /// Stream of aggregate states public abstract Conduit FoldWhile(Func Fold, Func Pred, S Init); /// /// Fold the values flowing through. Values are yielded downstream when either the predicate returns /// `true`, or the source completes. /// /// Binary operator /// Predicate /// Initial state /// State type /// Stream of aggregate states public abstract Conduit FoldUntil(Func Fold, Func Pred, S Init); /// /// Fold the values flowing through. Values are yielded downstream when either the schedule expires, the /// predicate returns `false`, or the source completes. /// /// Schedule to control the rate of processing /// Binary operator /// Predicate /// Initial state /// State type /// Stream of aggregate states public abstract Conduit FoldWhile( Schedule Time, Func Fold, Func Pred, S Init); /// /// Fold the values flowing through. Values are yielded downstream when either the schedule expires, the /// predicate returns `true`, or the source completes. /// /// Schedule to control the rate of processing /// Binary operator /// Predicate /// Initial state /// /// Stream of aggregate states public abstract Conduit FoldUntil( Schedule Time, Func Fold, Func Pred, S Init); } ================================================ FILE: LanguageExt.Streaming/Conduit/Internal/Conduit.ABC.cs ================================================ using System; using System.Collections.Concurrent; using System.Threading.Channels; using LanguageExt.Common; using LanguageExt.Pipes; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; /// /// Represents a channel with an internal queue. A channel has: /// /// * A sink: an input transducer that manipulates values before being placed into the internal queue. /// * A buffer: `System.Threading.Channels.Channel`. /// * A source: an output transducer that manipulates values after being taken from the internal queue. /// /// Both sides of the conduit can be manipulated: /// /// The sink is a co-functor and can be mapped using `Comap` or `CoTransform`, these transform values _before_ they get /// to the conduit's buffer. /// /// The source is a functor, so you can `Map` or `Transform` in the usual way to map values on their way out of the /// buffer. /// /// Control of the internal buffer is provided by passing a `Buffer` value to `Conduit.make`. This allows you to set /// various parameters for the internal queue, such as the maximum number of items to hold in the queue, and what /// strategy to use when the queue is full. The default is `Buffer.Unbounded`. /// /// `ToProducer` and `ToConsumer` enable the `Conduit` components to be used in composed pipe effects. /// /// Input value type /// Buffer value type /// Output value type internal class Conduit : Conduit { readonly Transducer sink; readonly Channel channel; readonly Transducer source; internal Conduit(Transducer sink, Channel channel, Transducer source) { this.sink = sink; this.channel = channel; this.source = source; Source = LanguageExt.Source.lift(channel).Transform(source); } /// /// Post a value to the `Sink` /// /// /// Raises `Errors.SinkFull` if the `Sink` is full or closed. /// /// Value to post /// IO computation that represents the posting public override IO Post(A value) => sink.Reduce( (_, x) => IO.liftVAsync(async e => { if (await channel.Writer.WaitToWriteAsync(e.Token)) { await channel.Writer.WriteAsync(x); return Reduced.Unit; } else { throw Errors.SinkFull; } })) (unit, value) .Map(r => r.Value); /// /// Complete and close the Sink /// public override IO Complete() => IO.lift(_ => channel.Writer.TryComplete()).Map(unit); /// /// Complete and close the Sink with an `Error` /// public override IO Fail(Error Error) => IO.lift(_ => channel.Writer.TryComplete(Error)).Map(unit); /// /// Iterate the stream, flowing values downstream to the reducer, which aggregates a /// result value. /// /// State to reduce /// Reducer /// State type /// Reduced state public override IO Reduce(S state, ReducerIO reducer) => IO.liftVAsync(async e => { while (await channel.Reader.WaitToReadAsync(e.Token)) { var value = await channel.Reader.ReadAsync(); switch (await source.Reduce(reducer)(state, value).RunAsync(e)) { case { Continue: true, Value: var nstate }: state = nstate; break; case { Value: var nstate }: return nstate; } } return state; }); /// /// Iterate the stream, flowing values downstream to the reducer, which aggregates a /// result value. /// /// State to reduce /// Reducer /// State type /// Reduced state public override K Reduce(S state, ReducerIO reducer) => M.LiftIOMaybe(IO.liftVAsync(async e => { while (await channel.Reader.WaitToReadAsync(e.Token)) { var value = await channel.Reader.ReadAsync(); switch (await source.Reduce(reducer)(state, value).RunAsync(e)) { case { Continue: true, Value: var nstate }: state = nstate; break; case { Value: var nstate }: return nstate; } } return state; })); /// /// Functor map /// public override Conduit Map(Func f) => With(source.Map(f)); /// /// Contravariant functor map /// public override Conduit Comap(Func f) => With (sink.Comap(f)); /// /// Access the underlying `Sink` for more direct manipulation. /// /// public override Sink Sink => LanguageExt.Sink.lift(channel).Comap(sink); /// /// Access the underlying `Source` for more direct manipulation. /// /// public override Source Source { get; } /// /// Convert the `Sink` to a `ConsumerT` pipe component /// /// Monad to lift (must support `IO`) /// `ConsumerT` public override ConsumerT ToConsumerT() => Sink.ToConsumerT(); /// /// Convert the `Sink` to a `Consumer` pipe component /// /// `Consumer` public override Consumer ToConsumer() => Sink.ToConsumer(); /// /// Convert `Source` to a `ProducerT` pipe component /// /// Monad to lift (must support `IO`) /// `ProducerT` public override ProducerT ToProducerT() => Source.ToProducerT(); /// /// Convert `Source` to a `Producer` pipe component /// /// `Producer` public override Producer ToProducer() => Source.ToProducer(); /// /// Transform with a transducer /// /// Transducer to use to transform /// Transformed source public override Conduit Transform(Transducer transducer) => With(source.Compose(transducer)); /// /// Co-transform with a transducer /// /// Transducer to use to transform /// Transformed source public override Conduit CoTransform(Transducer transducer) => With(transducer.Compose(sink)); /// /// Filter values. Yielding downstream when `true` /// /// Filter function /// SourceT where the only values yield are those that pass the predicate public override Conduit Filter(Func f) => With(source.Filter(f)); /// /// Skip items in the source /// /// Amount to skip /// Transformed source public override Conduit Skip(int amount) => With(source.Skip(amount)); /// /// Limit the number of items processed /// /// Number to take /// Transformed source public override Conduit Take(int amount) => With(source.Take(amount)); /// /// Fold the values flowing through. Values are yielded downstream when either the predicate returns /// `false`, or the source completes. /// /// Binary operator /// Predicate /// Initial state /// State type /// Stream of aggregate states public override Conduit FoldWhile(Func Fold, Func Pred, S Init) => With(source.FoldWhile(Fold, Pred, Init)); /// /// Fold the values flowing through. Values are yielded downstream when either the predicate returns /// `true`, or the source completes. /// /// Binary operator /// Predicate /// Initial state /// State type /// Stream of aggregate states public override Conduit FoldUntil(Func Fold, Func Pred, S Init) => With(source.FoldUntil(Fold, Pred, Init)); /// /// Fold the values flowing through. Values are yielded downstream when either the schedule expires, the /// predicate returns `false`, or the source completes. /// /// Schedule to control the rate of processing /// Binary operator /// Predicate /// Initial state /// State type /// Stream of aggregate states public override Conduit FoldWhile( Schedule Time, Func Fold, Func Pred, S Init) => With(source.FoldWhile(Time, Fold, Pred, Init)); /// /// Fold the values flowing through. Values are yielded downstream when either the schedule expires, the /// predicate returns `true`, or the source completes. /// /// Schedule to control the rate of processing /// Binary operator /// Predicate /// Initial state /// /// Stream of aggregate states public override Conduit FoldUntil( Schedule Time, Func Fold, Func Pred, S Init) => With(source.FoldUntil(Time, Fold, Pred, Init)); /// /// New conduit with all the same properties except the Source, which is provided as the argument. /// /// Source to use /// Source bound-value type /// Transformed conduit internal Conduit With(Transducer src) => new (sink, channel, src); /// /// New conduit with all the same properties except the Sink, which is provided as the argument. /// /// Sink to use /// Source bound-value type /// Transformed conduit internal Conduit With(Transducer snk) => new (snk, channel, source); } ================================================ FILE: LanguageExt.Streaming/ConduitT/ConduitT.Extensions.cs ================================================ using LanguageExt.Pipes; using LanguageExt.Traits; namespace LanguageExt; public static class ConduitTExtensions { public static ConduitT As(this K, B> ma) where M : MonadIO, Monad, Fallible => (ConduitT)ma; /// /// Convert the conduit's `Sink` to a `Consumer` pipe component /// /// `Consumer` public static Consumer ToConsumer(this ConduitT, A, B> conduit) => new (conduit.ToConsumerT().Proxy); /// /// Convert the conduit's `Source` to a `Producer` pipe component /// /// `Producer` public static Producer ToProducer(this ConduitT, A, B> conduit) => new(conduit.ToProducerT().Proxy); } ================================================ FILE: LanguageExt.Streaming/ConduitT/ConduitT.Module.cs ================================================ using System; using LanguageExt.Traits; using Ch = System.Threading.Channels; namespace LanguageExt; public static class ConduitT { /// /// Create a new unbounded Conduit /// /// Label for debugging purposes /// Value type /// Constructed Conduit with an `Sink` and an `Source` public static ConduitT make() where M : MonadIO, Fallible => make(Buffer.Unbounded); /// /// Create a new Conduit with the buffer settings provided /// /// Buffer settings /// Label for debugging purposes /// Value type /// Constructed Conduit with an `Sink` and an `Source` /// Thrown for invalid buffer settings public static ConduitT make(Buffer buffer) where M : MonadIO, Fallible { var channel = MakeChannel(buffer); return new ConduitT(TransducerM.identity(), channel, TransducerM.identity()); } /// /// Create a new unbounded Conduit /// /// Label for debugging purposes /// Value type /// Constructed Conduit with an `Sink` and an `Source` public static K> makeM() where M : MonadIO, Fallible => M.Pure(make(Buffer.Unbounded)); /// /// Create a new Conduit with the buffer settings provided /// /// Buffer settings /// Label for debugging purposes /// Value type /// Constructed Conduit with an `Sink` and an `Source` /// Thrown for invalid buffer settings public static K> makeM(Buffer buffer) where M : MonadIO, Fallible => M.Pure(make(Buffer.Unbounded)); static Ch.Channel> MakeChannel(Buffer buffer) where M : Maybe.MonadIO, Monad, Fallible { Ch.Channel> channel; switch (buffer) { case UnboundedBuffer: channel = Ch.Channel.CreateUnbounded>(); break; case BoundedBuffer(var size): { var opts = new Ch.BoundedChannelOptions((int)size) { FullMode = Ch.BoundedChannelFullMode.Wait }; channel = Ch.Channel.CreateBounded>(opts); break; } case SingleBuffer: { var opts = new Ch.BoundedChannelOptions(1) { FullMode = Ch.BoundedChannelFullMode.Wait }; channel = Ch.Channel.CreateBounded>(opts); break; } case LatestBuffer(var initial): { var opts = new Ch.BoundedChannelOptions(1) { FullMode = Ch.BoundedChannelFullMode.DropOldest }; channel = Ch.Channel.CreateBounded>(opts); channel.Writer.TryWrite(M.Pure(initial)); break; } case NewestBuffer(var size): { var opts = new Ch.BoundedChannelOptions((int)size) { FullMode = Ch.BoundedChannelFullMode.DropOldest }; channel = Ch.Channel.CreateBounded>(opts); break; } case NewBuffer: { var opts = new Ch.BoundedChannelOptions(1) { FullMode = Ch.BoundedChannelFullMode.DropOldest }; channel = Ch.Channel.CreateBounded>(opts); break; } default: throw new NotSupportedException(); } return channel; } } ================================================ FILE: LanguageExt.Streaming/ConduitT/ConduitT.TraitImpl.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public class ConduitT : Cofunctor>, Functor> where M : MonadIO, Monad, Fallible { public static K, X> Comap(Func f, K, B> fb) => fb.As().Comap(f); public static K, C> Map(Func f, K, B> ma) => ma.As().Map(f); } ================================================ FILE: LanguageExt.Streaming/ConduitT/ConduitT.cs ================================================ using System; using LanguageExt.Common; using LanguageExt.Pipes; using LanguageExt.Traits; namespace LanguageExt; /// /// /// Represents a channel with an internal queue. A channel has: /// /// /// * A `Sink`: an input DSL that manipulates values before being placed into the internal queue. /// * An internal queue: usually a `System.Threading.Channels.Channel`. /// * A `Source`: an output DSL that manipulates values after being taken from the internal queue. /// /// /// Both sides of the `ConduitT` can be manipulated: /// /// /// The `Sink` is a `Cofunctor` and can be mapped using `Comap`, this transforms values _before_ they get to the /// channel. /// /// /// The `Source` is a monad-transformer, so you can `Map`, `Bind`, `Apply`, in the usual way to map values on their way /// out. They manipulate values as they leave the channel through the `Source`. /// /// /// Control of the internal queue is provided by passing a `Buffer` value to `ConduitT.make`. This allows you to set /// various parameters for the internal queue, such as the maximum number of items to hold in the queue, and what /// strategy to use when the queue is full. The default is `Buffer.Unbounded`. /// /// /// `ToProducer` and `ToConsumer` enable the `ConduitT` components to be used in composed pipe effects. /// /// /// Sink /// Source /// Input value type /// Output value type public abstract class ConduitT : K, B> where M : MonadIO, Monad, Fallible { /// /// Post a value to the Sink /// /// /// Raises `Errors.SinkFull` if the Sink is full or closed. /// /// Value to post /// IO computation that represents the posting public abstract K Post(A value); /// /// Post a value to the Sink /// /// /// Raises `Errors.SinkFull` if the sink is full or closed. /// /// Operation to post /// IO computation that represents the posting public abstract K PostM(K ma); /// /// Complete and close the sink /// public abstract K Complete(); /// /// Complete and close the sink with an `Error` /// public abstract K Fail(Error Error); /// /// Iterate the stream, flowing values downstream to the reducer, which aggregates a /// result value. This is returned lifted. /// /// Note, this is recursive, so `M` needs to be able to support recursion without /// blowing the stack. If you have the `IO` monad in your stack, then this will automatically /// be the case. /// Initial state /// Reducer /// State type /// Lifted aggregate state public abstract K Reduce(S state, ReducerM reducer); /// /// Functor map /// public abstract ConduitT Map(Func f); /// /// Functor map /// public ConduitT Select(Func f) => Map(f); /// /// Contravariant functor map /// public abstract ConduitT Comap(Func f); /// /// Access the underlying `SinkT` for more direct manipulation. /// /// public abstract SinkT Sink { get; } /// /// Access the underlying `SourceT` for more direct manipulation. /// /// public abstract SourceT Source { get; } /// /// Convert the conduit's `Sink` to a `ConsumerT` pipe component /// /// `ConsumerT` public abstract ConsumerT ToConsumerT(); /// /// Convert the conduit's `Source` to a `ProducerT` pipe component /// /// `ProducerT` public abstract ProducerT ToProducerT(); /// /// Transform with a transducer /// /// Transducer to use to transform /// Transformed source public abstract ConduitT Transform(TransducerM transducer); /// /// Co-transform with a transducer /// /// Transducer to use to transform /// Transformed source public abstract ConduitT CoTransform(TransducerM transducer); /// /// Filter values. Yielding downstream when `true` /// /// Filter function /// SourceT where the only values yield are those that pass the predicate public ConduitT Where(Func f) => Filter(f); /// /// Filter values. Yielding downstream when `true` /// /// Filter function /// SourceT where the only values yield are those that pass the predicate public abstract ConduitT Filter(Func f); /// /// Skip items in the source /// /// Number to skip /// Transformed source public abstract ConduitT Skip(int amount); /// /// Limit the number of items processed /// /// Number to take /// Transformed source public abstract ConduitT Take(int amount); /// /// Fold the values flowing through. Values are yielded downstream when either the predicate returns /// `false`, or the source completes. /// /// Binary operator /// Predicate /// Initial state /// State type /// Stream of aggregate states public abstract ConduitT FoldWhile(Func Fold, Func Pred, S Init); /// /// Fold the values flowing through. Values are yielded downstream when either the predicate returns /// `true`, or the source completes. /// /// Binary operator /// Predicate /// Initial state /// State type /// Stream of aggregate states public abstract ConduitT FoldUntil(Func Fold, Func Pred, S Init); /// /// Fold the values flowing through. Values are yielded downstream when either the schedule expires, the /// predicate returns `false`, or the source completes. /// /// Schedule to control the rate of processing /// Binary operator /// Predicate /// Initial state /// State type /// Stream of aggregate states public abstract ConduitT FoldWhile( Schedule Time, Func Fold, Func Pred, S Init); /// /// Fold the values flowing through. Values are yielded downstream when either the schedule expires, the /// predicate returns `true`, or the source completes. /// /// Schedule to control the rate of processing /// Binary operator /// Predicate /// Initial state /// /// Stream of aggregate states public abstract ConduitT FoldUntil( Schedule Time, Func Fold, Func Pred, S Init); } ================================================ FILE: LanguageExt.Streaming/ConduitT/Internal/ConduitT.ABC.cs ================================================ using System; using System.Threading; using System.Threading.Channels; using System.Threading.Tasks; using LanguageExt.Common; using LanguageExt.Pipes; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; class ConduitT : ConduitT where M : MonadIO, Monad, Fallible { readonly TransducerM sink; readonly Channel> channel; readonly TransducerM source; internal ConduitT(TransducerM sink, Channel> channel, TransducerM source) { this.sink = sink; this.channel = channel; this.source = source; Source = SourceT.liftM(channel).Transform(source); } /// /// Post a value to the `Sink` /// /// /// Raises `Errors.SinkFull` if the `Sink` is full or closed. /// /// Value to post /// IO computation that represents the posting public override K Post(A value) => sink.Reduce((_, b) => Write(M.Pure(b)).Map(Reduced.Continue))(unit, value) .Map(r => r.Value); /// /// Post a value to the `Sink` /// /// /// Raises `Errors.SinkFull` if the `Sink` is full or closed. /// /// Value to post /// IO computation that represents the posting public override K PostM(K ma) => ma.Bind(Post); /// /// Complete and close the Sink /// public override K Complete() => M.LiftIOMaybe(IO.lift(_ => channel.Writer.TryComplete()).Map(unit)); /// /// Complete and close the Sink with an `Error` /// public override K Fail(Error Error) => M.LiftIOMaybe(IO.lift(_ => channel.Writer.TryComplete(Error)).Map(unit)); /// /// Iterate the stream, flowing values downstream to the reducer, which aggregates a /// result value. /// /// State to reduce /// Reducer /// State type /// Reduced state public override K Reduce(S state, ReducerM reducer) { return M.LiftIO(IO.lift(e => go(state, e.Token).Map(r => r.Value))).Flatten(); K> go(S s0, CancellationToken token) { if(token.IsCancellationRequested) return M.Pure(Reduced.Done(s0)); if (channel.Reader.WaitToReadAsync(token).GetAwaiter().GetResult()) { var mb = channel.Reader.ReadAsync(token).GetAwaiter().GetResult(); return mb.Bind(b => source.Reduce((s1, c) => reducer(s1, c) .Bind(s2 => s2.Continue ? go(s2.Value, token) : M.Pure(s2)) .Catch(M.Pure(Reduced.Done(s0))))(s0, b)); // TODO: Deal with errors? } else { return M.Pure(Reduced.Done(s0)); } } } /// /// Functor map /// public override ConduitT Map(Func f) => With(source.Compose(TransducerM.map(f))); /// /// Contravariant functor map /// public override ConduitT Comap(Func f) => With (TransducerM.map(f).Compose(sink)); /// /// Access the underlying `Sink` for more direct manipulation. /// /// public override SinkT Sink => SinkT.lift(channel).Comap(sink); /// /// Access the underlying `Source` for more direct manipulation. /// /// public override SourceT Source { get; } /// /// Convert the `Sink` to a `ConsumerT` pipe component /// /// Monad to lift (must support `IO`) /// `ConsumerT` public override ConsumerT ToConsumerT() => Sink.ToConsumerT(); /// /// Convert `Source` to a `ProducerT` pipe component /// /// Monad to lift (must support `IO`) /// `ProducerT` public override ProducerT ToProducerT() => Source.ToProducerT(); /// /// Transform with a transducer /// /// Transducer to use to transform /// Transformed source public override ConduitT Transform(TransducerM transducer) => With(source.Compose(transducer)); /// /// Co-transform with a transducer /// /// Transducer to use to transform /// Transformed source public override ConduitT CoTransform(TransducerM transducer) => With(transducer.Compose(sink)); /// /// Filter values. Yielding downstream when `true` /// /// Filter function /// SourceT where the only values yield are those that pass the predicate public override ConduitT Filter(Func f) => With(source.Filter(f)); /// /// Skip items in the source /// /// Amount to skip /// Transformed source public override ConduitT Skip(int amount) => With(source.Skip(amount)); /// /// Limit the number of items processed /// /// Number to take /// Transformed source public override ConduitT Take(int amount) => With(source.Take(amount)); /// /// Fold the values flowing through. Values are yielded downstream when either the predicate returns /// `false`, or the source completes. /// /// Binary operator /// Predicate /// Initial state /// State type /// Stream of aggregate states public override ConduitT FoldWhile(Func Fold, Func Pred, S Init) => With(source.FoldWhile(Fold, Pred, Init)); /// /// Fold the values flowing through. Values are yielded downstream when either the predicate returns /// `true`, or the source completes. /// /// Binary operator /// Predicate /// Initial state /// State type /// Stream of aggregate states public override ConduitT FoldUntil(Func Fold, Func Pred, S Init) => With(source.FoldUntil(Fold, Pred, Init)); /// /// Fold the values flowing through. Values are yielded downstream when either the schedule expires, the /// predicate returns `false`, or the source completes. /// /// Schedule to control the rate of processing /// Binary operator /// Predicate /// Initial state /// State type /// Stream of aggregate states public override ConduitT FoldWhile( Schedule Time, Func Fold, Func Pred, S Init) => With(source.FoldWhile(Time, Fold, Pred, Init)); /// /// Fold the values flowing through. Values are yielded downstream when either the schedule expires, the /// predicate returns `true`, or the source completes. /// /// Schedule to control the rate of processing /// Binary operator /// Predicate /// Initial state /// /// Stream of aggregate states public override ConduitT FoldUntil( Schedule Time, Func Fold, Func Pred, S Init) => With(source.FoldUntil(Time, Fold, Pred, Init)); /// /// New conduit with all the same properties except the Source, which is provided as the argument. /// /// Source to use /// Source bound-value type /// Transformed conduit ConduitT With(TransducerM src) => new (sink, channel, src); /// /// New conduit with all the same properties except the Sink, which is provided as the argument. /// /// Sink to use /// Source bound-value type /// Transformed conduit ConduitT With(TransducerM sink) => new (sink, channel, source); K Write(K value) { return M.LiftIOMaybe(IO.liftVAsync(e => go(e, value, channel))); static async ValueTask go(EnvIO e, K mb, Channel> ch) { if (await ch.Writer.WaitToWriteAsync(e.Token)) { await ch.Writer.WriteAsync(mb); return unit; } else { throw Errors.SinkFull; } } } } ================================================ FILE: LanguageExt.Streaming/Event/Event.Module.cs ================================================ using System; namespace LanguageExt; public static class Event { /// /// Create an `Event` from an event delegate /// /// Delegate to which the event will listen for values /// Value type /// Event that can be passed around in a first-class manner public static Event from(ref Action eventDelegate) => new (ref eventDelegate); } ================================================ FILE: LanguageExt.Streaming/Event/Event.cs ================================================ using System; using System.Threading; using System.Threading.Channels; using LanguageExt.Traits; using LanguageExt.UnsafeValueAccess; using L = LanguageExt; namespace LanguageExt; /// /// Adds itself to the `Action` event-delegate and then forwards anything posted by the event /// to any subscribers to this type. We're trying to make events a bit more 'first class, /// rather than the 'runt of the litter' that they are now. /// /// So, as long as you can find a way to make your event into a single argument action, you /// can then use it with the streaming functionality within this library. /// /// Value type public class Event : IDisposable { readonly AtomHashMap, Subscription> subscribers = AtomHashMap, Subscription>.Empty; Action @delegate; int disposed; /// /// Construct an event from subscribing to the delegate /// /// The delegate that this type will subscribe to public Event(ref Action @delegate) { @delegate += Post; this.@delegate = @delegate; } /// /// Subscribe to this event /// public IO> Subscribe() => SubscribeInternal() .Map(s => Source.lift(s.Channel)); /// /// Subscribe to this event /// public SourceT Subscribe() where M : MonadIO, Fallible => from sub in SubscribeInternal() from val in SourceT.lift(sub.Channel) select val; /// /// Subscribe a channel to this event /// /// /// IO SubscribeInternal() => IO.lift(e => { var channel = Channel.CreateUnbounded(); var sub = new Subscription(this, channel); subscribers.AddOrUpdate(channel, sub); e.Resources.Acquire(sub); return sub; }); public IO Unsubscribe(Subscription sub) => IO.lift(e => subscribers.Swap(map => { if (map.ContainsKey(sub.Channel)) { sub.Channel.Writer.TryComplete(); map = map.Remove(sub.Channel); } return map; })); public IO UnsubscribeAll() => IO.lift(e => { foreach (var sub in subscribers.Values) { sub.Channel.Writer.TryComplete(); } return subscribers.Clear(); }); void Post(A value) { foreach (var sub in subscribers.Values) { sub.Channel.Writer.TryWrite(value); } } public void Dispose() { if (Interlocked.CompareExchange(ref disposed, 1, 0) == 0) { #pragma warning disable CS8601 // Possible null reference assignment. @delegate -= Post; UnsubscribeAll().Run(); } } public class Subscription : IDisposable { internal Channel Channel; readonly Event @event; int disposed; internal Subscription(Event @event, Channel channel) { this.@event = @event; Channel = channel; } public void Dispose() { if (Interlocked.CompareExchange(ref disposed, 1, 0) == 0) { @event.Unsubscribe(this).Run(); } } } } ================================================ FILE: LanguageExt.Streaming/LanguageExt.Streaming.csproj ================================================  TRACE;DEBUG 1701;1702;1705;IDE1006;CS1591;CS1573;CS1712;CS1711;CS1572;CS1587 CONTRACTS_FULL net10.0 enable LanguageExt.Pipes 5.0.0-beta-77 LanguageExt.Streaming LanguageExt.Streaming Paul Louth Compositional pipelines for language-ext README.nuget.md Copyright (c) Paul Louth. All rights reserved. Streaming is a clean and powerful stream processing library that lets you build and connect reusable streaming components C#, Functional, Language Extension, Monad, Option, Either, Reader, Writer, State, List, Set, Map, Queue, Memo, Memoization, Immutable, Lambda, Pattern Matching, Tuple lang-ext-small.png https://github.com/louthy/language-ext MIT false bin\$(Configuration)\$(TargetFramework)\$(AssemblyName).xml library 5.0.0.0 5.0.0.0 default true / ================================================ FILE: LanguageExt.Streaming/Pipes/Consumer/Consumer.Extensions.cs ================================================ using System; using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt.Pipes; public static partial class ConsumerExtensions { /// /// Transformation from `PipeT` to `Consumer`. /// public static Consumer ToConsumer(this K>, A> pipe) => new(pipe.As()); /// /// Transformation from `Pipe` to `Consumer`. /// public static Consumer ToConsumer(this K, A> pipe) => new(pipe.As().Proxy); /// /// Downcast /// public static Consumer As(this K, A> ma) => (Consumer)ma; /// /// Monad bind /// public static Consumer SelectMany( this K, A> ma, Func> f, Func g) => Consumer.liftM(ma).SelectMany(f, g); /// /// Monad bind /// public static Consumer SelectMany( this IO ma, Func> f, Func g) => Consumer.liftIO(ma).SelectMany(f, g); /// /// Monad bind /// public static Consumer SelectMany( this Pure ma, Func> f, Func g) => Consumer.pure(ma.Value).SelectMany(f, g); /// /// Monad bind /// public static Consumer SelectMany( this Lift ff, Func> f, Func g) => Consumer.lift(ff.Function).SelectMany(f, g); /// /// Monad bind /// public static Consumer Bind( this K, A> ma, Func> f, Func g) => Consumer.liftM(ma).Bind(f); /// /// Monad bind /// public static Consumer Bind( this IO ma, Func> f, Func g) => Consumer.liftIO(ma).Bind(f); /// /// Monad bind /// public static Consumer Bind( this Pure ma, Func> f, Func g) => Consumer.pure(ma.Value).Bind(f); /// /// Monad bind /// public static Consumer Bind( this Lift ff, Func> f, Func g) => Consumer.lift(ff.Function).Bind(f); /// /// Monad bind operation /// public static Consumer SelectMany( this K, A> ma, Func> bind, Func project) => ma.Bind(a => bind(a) switch { { Flag: true } => Consumer.pure(project(a, default)), var guard => Consumer.error(guard.OnFalse()) }).As(); /// /// Monad bind operation /// public static Consumer SelectMany( this Guard ma, Func, B>> bind, Func project) => ma switch { { Flag: true } => bind(default).Map(b => project(default, b)).As(), var guard => Consumer.error(guard.OnFalse()) }; } ================================================ FILE: LanguageExt.Streaming/Pipes/Consumer/Consumer.Module.cs ================================================ using System; using System.Threading.Tasks; using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt.Pipes; /// /// `ConsumerT` streaming consumer monad-transformer /// public static class Consumer { /// /// Await a value from upstream /// /// Effect runtime type /// Stream value to consume /// public static Consumer awaiting() => PipeT.awaiting, IN, Void>().ToConsumer(); /// /// Await a value from upstream and then ignore it /// /// Effect runtime type /// Stream value to consume /// public static Consumer awaitIgnore() => new PipeTAwait, Unit>(_ => PipeT.pure, Unit>(default)).ToConsumer(); /// /// Create a consumer that simply returns a bound value without awaiting anything /// /// Effect runtime type /// Stream value to await /// Bound value type /// public static Consumer pure(A value) => PipeT.pure, A>(value).ToConsumer(); /// /// Create a consumer that always fails /// /// Effect runtime type /// Stream value to await /// Bound value type /// public static Consumer error(Error value) => PipeT.fail, A>(value).ToConsumer(); /// /// Create a consumer that yields nothing at all /// /// Effect runtime type /// Stream value to consume /// Bound value type /// public static Consumer empty() => PipeT.empty, A>().ToConsumer(); /// /// Create a consumer that simply returns a bound value without yielding anything /// /// Effect runtime type /// Stream value to consume /// Bound value type /// public static Consumer lift(Func f) => PipeT.lift, A>(f).ToConsumer(); /// /// Create a lazy consumer /// /// Effect runtime type /// Stream value to consume /// Bound value type /// public static Consumer liftT(Func> f) => PipeT.liftT(() => f().Proxy).ToConsumer(); /// /// Create an asynchronous lazy consumer /// /// Effect runtime type /// Stream value to consume /// Bound value type /// public static Consumer liftT(Func>> f) => PipeT.liftT(() => f().Map(x => x.Proxy)).ToConsumer(); /// /// Create an asynchronous consumer /// /// Effect runtime type /// Stream value to consume /// Bound value type /// public static Consumer liftT(ValueTask> f) => PipeT.liftT(f.Map(x => x.Proxy)).ToConsumer(); /// /// Create a consumer that simply returns the bound value of the lifted monad without yielding anything /// /// Effect runtime type /// Stream value to consume /// Bound value type /// public static Consumer liftM(K, A> ma) => PipeT.liftM, A>(ma).ToConsumer(); /// /// Create a consumer that simply returns the bound value of the lifted monad without yielding anything /// /// Effect runtime type /// Stream value to consume /// Bound value type /// public static Consumer liftIO(IO ma) => PipeT.liftIO, A>(ma).ToConsumer(); /// /// Continually repeat the provided operation /// /// Effect runtime type /// Stream value to consume /// Bound value type /// public static Consumer repeat(Consumer ma) => PipeT.repeat(ma.Proxy).ToConsumer(); /// /// Repeat the provided operation based on the schedule provided /// /// Effect runtime type /// Stream value to consume /// Bound value type /// public static Consumer repeat(Schedule schedule, Consumer ma) => PipeT.repeat(schedule, ma.Proxy).ToConsumer(); /// /// Continually lift and repeat the provided operation /// /// Effect runtime type /// Stream value to consume /// Bound value type /// public static Consumer repeatM(K, A> ma) => PipeT.repeatM, A>(ma).ToConsumer(); /// /// Repeat the provided operation based on the schedule provided /// /// Effect runtime type /// Stream value to consume /// Bound value type /// public static Consumer repeatM(Schedule schedule, K, A> ma) => PipeT.repeatM, A>(schedule, ma).ToConsumer(); } ================================================ FILE: LanguageExt.Streaming/Pipes/Consumer/Consumer.Monad.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using LanguageExt.Traits; namespace LanguageExt.Pipes; public class Consumer : MonadUnliftIO>, MonadT, Eff> { static K, B> Monad>.Bind( K, A> ma, Func, B>> f) => ma.As().Bind(x => f(x).As()); static K, B> Monad>.Recur(A value, Func, Next>> f) => Monad.unsafeRecur(value, f); static K, B> Functor>.Map( Func f, K, A> ma) => ma.As().Map(f); static K, A> Applicative>.Pure(A value) => Consumer.pure(value); static K, B> Applicative>.Apply( K, Func> mf, K, A> ma) => ma.As().ApplyBack(mf.As()); static K, B> Applicative>.Apply( K, Func> mf, Memo, A> mma) => new PipeTMemo, A>(mma.Lower().Map(ma => ma.As().Proxy.Kind()).Lift()) .ApplyBack(mf.As().Proxy) .ToConsumer(); static K, A> MonadT, Eff>.Lift(K, A> ma) => Consumer.liftM(ma); static K, A> MonadIO>.LiftIO(IO ma) => Consumer.liftIO(ma); static K, B> MonadUnliftIO>.MapIO(K, A> ma, Func, IO> f) => ma.As().MapIO(f); static K, IO> MonadUnliftIO>.ToIO(K, A> ma) => ma.As().MapIO(IO.pure); static K, B> Applicative>.Action( K, A> ma, K, B> mb) => Consumer.liftM(ma.As().Run().Action(mb.As().Run())); static K, A> Applicative>.Actions( IterableNE, A>> fas) => fas.Select(fa => fa.As().Proxy.Kind()) .Actions() .ToConsumer(); } ================================================ FILE: LanguageExt.Streaming/Pipes/Consumer/Consumer.Operators.cs ================================================ using LanguageExt.Traits; namespace LanguageExt.Pipes; public static partial class ConsumerExtensions { extension(K, A>) { /// /// Downcast /// public static Consumer operator +(K, A> ma) => (Consumer) ma; /// /// Downcast /// public static Consumer operator >>(K, A> ma, Lower lower) => +ma; } } ================================================ FILE: LanguageExt.Streaming/Pipes/Consumer/Consumer.cs ================================================ using System; using System.Diagnostics.Contracts; using System.Threading; using LanguageExt.Traits; namespace LanguageExt.Pipes; /// /// `Consumer` streaming consumer monad-transformer instance /// /// /// Unlike the general purpose `ConsumerT`, which will lift any monad, this type only lifts the `Eff` monad. /// public readonly record struct Consumer(PipeT, A> Proxy) : K, A> { [Pure] public Consumer Map(Func f) => Proxy.Map(f); [Pure] public Consumer MapM(Func, Eff> f) => Proxy.MapM(ma => f(ma.As())); [Pure] public Consumer MapIO(Func, IO> f) => Proxy.MapIO(f); [Pure] public Consumer ApplyBack(Consumer> ff) => Proxy.ApplyBack(ff.Proxy); [Pure] public Consumer Action(Consumer fb) => Proxy.Action(fb.Proxy); [Pure] public Consumer Bind(Func> f) => Proxy.Bind(x => f(x).Proxy); [Pure] public Consumer Bind(Func> f) => Proxy.Bind(f); [Pure] public Consumer Bind(Func> f) => Proxy.Bind(f).ToConsumer(); [Pure] public Consumer Bind(Func> f) => Proxy.Bind(f); [Pure] public Consumer Bind(Func> f) => Proxy.Bind(f); [Pure] internal Eff Run() => Proxy.Run().As(); [Pure] public Consumer Select(Func f) => Proxy.Map(f); [Pure] public Consumer SelectMany(Func> f, Func g) => Proxy.SelectMany(x => f(x).Proxy, g); [Pure] public Consumer SelectMany(Func> f, Func g) => Proxy.SelectMany(f, g); [Pure] public Consumer SelectMany(Func> f, Func g) => Proxy.SelectMany(f, g); [Pure] public Consumer SelectMany(Func> f, Func g) => Proxy.SelectMany(f, g); [Pure] public Consumer SelectMany(Func> f, Func g) => Proxy.SelectMany(f, g); [Pure] public static implicit operator Consumer(ConsumerT, A> consumer) => consumer.Proxy; [Pure] public static implicit operator Consumer(PipeT, A> pipe) => pipe.ToConsumer(); [Pure] public static implicit operator Consumer(Pipe pipe) => pipe.ToConsumer(); [Pure] public static implicit operator Consumer(Pure rhs) => Consumer.pure(rhs.Value); } ================================================ FILE: LanguageExt.Streaming/Pipes/ConsumerT/ConsumerT.Extensions.cs ================================================ using System; using System.Diagnostics.Contracts; using LanguageExt.Traits; namespace LanguageExt.Pipes; public static partial class ConsumerTExtensions { /// /// Transformation from `PipeT` to `ConsumerT`. /// public static ConsumerT ToConsumer(this K, A> pipe) where M : MonadIO => new(pipe.As()); /// /// Downcast /// public static ConsumerT As(this K, A> ma) where M : MonadIO => (ConsumerT)ma; /// /// Convert to the `Eff` version of `Consumer` /// public static Consumer ToEff(this K>, A> ma) => ma.As(); /// /// Monad bind /// public static ConsumerT SelectMany( this K ma, Func> f, Func g) where M : MonadIO => ConsumerT.liftM(ma).SelectMany(f, g); /// /// Monad bind /// public static ConsumerT SelectMany( this IO ma, Func> f, Func g) where M : MonadIO => ConsumerT.liftIO(ma).SelectMany(f, g); /// /// Monad bind /// public static ConsumerT SelectMany( this Pure ma, Func> f, Func g) where M : MonadIO => ConsumerT.pure(ma.Value).SelectMany(f, g); /// /// Monad bind /// public static ConsumerT SelectMany( this Lift ff, Func> f, Func g) where M : MonadIO => ConsumerT.lift(ff.Function).SelectMany(f, g); /// /// Monad bind /// public static ConsumerT Bind( this K ma, Func> f, Func g) where M : MonadIO => ConsumerT.liftM(ma).Bind(f); /// /// Monad bind /// public static ConsumerT Bind( this IO ma, Func> f, Func g) where M : MonadIO => ConsumerT.liftIO(ma).Bind(f); /// /// Monad bind /// public static ConsumerT Bind( this Pure ma, Func> f, Func g) where M : MonadIO => ConsumerT.pure(ma.Value).Bind(f); /// /// Monad bind /// public static ConsumerT Bind( this Lift ff, Func> f, Func g) where M : MonadIO => ConsumerT.lift(ff.Function).Bind(f); /// /// Monad bind operation /// public static ConsumerT SelectMany( this K, A> ma, Func> bind, Func project) where M : MonadIO, Fallible => ma.Bind(a => bind(a) switch { { Flag: true } => ConsumerT.pure(project(a, default)), var guard => ConsumerT.fail(guard.OnFalse()) }).As(); /// /// Monad bind operation /// public static ConsumerT SelectMany( this Guard ma, Func, B>> bind, Func project) where M : MonadIO, Fallible => ma switch { { Flag: true } => bind(default).Map(b => project(default, b)).As(), var guard => ConsumerT.fail(guard.OnFalse()) }; [Pure] public static ConsumerT MapIO(this ConsumerT ma, Func, IO> f) where M : MonadUnliftIO => ma.Proxy.MapIO(f); } ================================================ FILE: LanguageExt.Streaming/Pipes/ConsumerT/ConsumerT.Module.cs ================================================ using System; using System.Threading.Tasks; using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt.Pipes; /// /// `ConsumerT` streaming consumer monad-transformer /// public static class ConsumerT { /// /// Await a value from upstream /// /// Stream value to consume /// Lifted monad type /// public static ConsumerT awaiting() where M : MonadIO => PipeT.awaiting(); /// /// Await a value from upstream and then ignore it /// /// Stream value to consume /// Lifted monad type /// public static ConsumerT awaitIgnore() where M : MonadIO => new PipeTAwait(_ => PipeT.pure(default)); /// /// Create a consumer that simply returns a bound value without awaiting anything /// /// Stream value to await /// Lifted monad type /// Bound value type /// public static ConsumerT pure(A value) where M : MonadIO => PipeT.pure(value); /// /// Create a consumer that always fails /// /// Stream value to await /// Failure type /// Lifted monad type /// Bound value type /// public static ConsumerT fail(E value) where M : MonadIO, Fallible => PipeT.fail(value); /// /// Create a consumer that always fails /// /// Stream value to await /// Lifted monad type /// Bound value type /// public static ConsumerT error(Error value) where M : MonadIO, Fallible => PipeT.fail(value); /// /// Create a consumer that yields nothing at all /// /// Stream value to consume /// Lifted monad type /// Bound value type /// public static ConsumerT empty() where M : MonadIO, MonoidK => PipeT.empty(); /// /// Create a consumer that simply returns a bound value without yielding anything /// /// Stream value to consume /// Lifted monad type /// Bound value type /// public static ConsumerT lift(Func f) where M : MonadIO => PipeT.lift(f); /// /// Create a lazy consumer /// /// Stream value to consume /// Lifted monad type /// Bound value type /// public static ConsumerT liftT(Func> f) where M : MonadIO => PipeT.liftT(() => f().Proxy); /// /// Create an asynchronous lazy consumer /// /// Stream value to consume /// Lifted monad type /// Bound value type /// public static ConsumerT liftT(Func>> f) where M : MonadIO => PipeT.liftT(() => f().Map(x => x.Proxy)); /// /// Create an asynchronous consumer /// /// Stream value to consume /// Lifted monad type /// Bound value type /// public static ConsumerT liftT(ValueTask> f) where M : MonadIO => PipeT.liftT(f.Map(x => x.Proxy)); /// /// Create a consumer that simply returns the bound value of the lifted monad without yielding anything /// /// Stream value to consume /// Lifted monad type /// Bound value type /// public static ConsumerT liftM(K ma) where M : MonadIO => PipeT.liftM(ma); /// /// Create a consumer that simply returns the bound value of the lifted monad without yielding anything /// /// Stream value to consume /// Lifted monad type /// Bound value type /// public static ConsumerT liftIO(IO ma) where M : MonadIO => PipeT.liftIO(ma); /// /// Continually repeat the provided operation /// /// Stream value to consume /// Lifted monad type /// Bound value type /// public static ConsumerT repeat(ConsumerT ma) where M : MonadIO => PipeT.repeat(ma.Proxy).ToConsumer(); /// /// Repeat the provided operation based on the schedule provided /// /// Stream value to consume /// Lifted monad type /// Bound value type /// public static ConsumerT repeat(Schedule schedule, ConsumerT ma) where M : MonadIO => PipeT.repeat(schedule, ma.Proxy).ToConsumer(); /// /// Continually lift and repeat the provided operation /// /// Stream value to consume /// Lifted monad type /// Bound value type /// public static ConsumerT repeatM(K ma) where M : MonadIO => PipeT.repeatM(ma).ToConsumer(); /// /// Repeat the provided operation based on the schedule provided /// /// Stream value to consume /// Lifted monad type /// Bound value type /// public static ConsumerT repeatM(Schedule schedule, K ma) where M : MonadIO => PipeT.repeatM(schedule, ma).ToConsumer(); } ================================================ FILE: LanguageExt.Streaming/Pipes/ConsumerT/ConsumerT.Monad.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt.Pipes; public class ConsumerT : MonadT, M>, MonadUnliftIO> where M : MonadIO { static K, B> Monad>.Bind( K, A> ma, Func, B>> f) => ma.As().Bind(x => f(x).As()); static K, B> Monad>.Recur(A value, Func, Next>> f) => Monad.unsafeRecur(value, f); static K, B> Functor>.Map( Func f, K, A> ma) => ma.As().Map(f); static K, A> Applicative>.Pure(A value) => ConsumerT.pure(value); static K, B> Applicative>.Apply( K, Func> mf, K, A> ma) => ma.As().ApplyBack(mf.As()); static K, B> Applicative>.Apply( K, Func> mf, Memo, A> ma) => new PipeTMemo(ma.Lower().Map(ma => ma.As().Proxy.Kind()).Lift()) .ApplyBack(mf.As().Proxy) .ToConsumer(); static K, A> MonadT, M>.Lift(K ma) => ConsumerT.liftM(ma); static K, A> MonadIO>.LiftIO(IO ma) => ConsumerT.liftIO(ma); static K, B> MonadUnliftIO>.MapIO(K, A> ma, Func, IO> f) => ma.As().MapM(m => M.MapIOMaybe(m, f)); static K, IO> MonadUnliftIO>.ToIO(K, A> ma) => ma.As().MapM(M.ToIOMaybe); static K, B> Applicative>.Action( K, A> ma, K, B> mb) => ConsumerT.liftM(ma.As().Run().Action(mb.As().Run())); static K, A> Applicative>.Actions( IterableNE, A>> fas) => fas.Select(fa => fa.As().Proxy.Kind()) .Actions() .ToConsumer(); } ================================================ FILE: LanguageExt.Streaming/Pipes/ConsumerT/ConsumerT.Operators.cs ================================================ using LanguageExt.Traits; namespace LanguageExt.Pipes; public static partial class ConsumerTExtensions { extension(K, A>) where M : MonadIO { /// /// Downcast /// public static ConsumerT operator +(K, A> ma) => (ConsumerT) ma; /// /// Downcast /// public static ConsumerT operator >>(K, A> ma, Lower lower) => +ma; } } ================================================ FILE: LanguageExt.Streaming/Pipes/ConsumerT/ConsumerT.cs ================================================ using System; using System.Diagnostics.Contracts; using System.Threading; using LanguageExt.Traits; namespace LanguageExt.Pipes; /// /// `ConsumerT` streaming consumer monad-transformer instance /// public readonly record struct ConsumerT(PipeT Proxy) : K, A> where M : MonadIO { [Pure] public ConsumerT Map(Func f) => Proxy.Map(f); [Pure] public ConsumerT MapM(Func, K> f) => Proxy.MapM(f); [Pure] public ConsumerT ApplyBack(ConsumerT> ff) => Proxy.ApplyBack(ff.Proxy); [Pure] public ConsumerT Action(ConsumerT fb) => Proxy.Action(fb.Proxy); [Pure] public ConsumerT Bind(Func> f) => Proxy.Bind(x => f(x).Proxy); [Pure] public ConsumerT Bind(Func> f) => Proxy.Bind(f); [Pure] public ConsumerT Bind(Func> f) => Proxy.Bind(f); [Pure] public ConsumerT Bind(Func> f) => Proxy.Bind(f); [Pure] public ConsumerT Bind(Func> f) => Proxy.Bind(f); [Pure] internal K Run() => Proxy.Run(); [Pure] public ConsumerT Select(Func f) => Proxy.Map(f); [Pure] public ConsumerT SelectMany(Func> f, Func g) => Proxy.SelectMany(x => f(x).Proxy, g); [Pure] public ConsumerT SelectMany(Func> f, Func g) => Proxy.SelectMany(f, g); [Pure] public ConsumerT SelectMany(Func> f, Func g) => Proxy.SelectMany(f, g); [Pure] public ConsumerT SelectMany(Func> f, Func g) => Proxy.SelectMany(f, g); [Pure] public ConsumerT SelectMany(Func> f, Func g) => Proxy.SelectMany(f, g); [Pure] public static implicit operator ConsumerT(PipeT pipe) => pipe.ToConsumer(); [Pure] public static implicit operator ConsumerT(Pure rhs) => ConsumerT.pure(rhs.Value); } ================================================ FILE: LanguageExt.Streaming/Pipes/Effect/Effect.Extensions.cs ================================================ using System; using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt.Pipes; public static partial class EffectExtensions { /// /// Transformation from `PipeT` to `EffectT`. /// public static Effect ToEffect(this K>, A> pipe) => new(pipe.As()); /// /// Downcast /// public static Effect As(this K, A> ma) => (Effect)ma; /// /// Monad bind /// public static Effect SelectMany( this K, A> ma, Func> f, Func g) => Effect.liftM(ma).SelectMany(f, g); /// /// Monad bind /// public static Effect SelectMany( this IO ma, Func> f, Func g) => Effect.liftIO(ma).SelectMany(f, g); /// /// Monad bind /// public static Effect SelectMany( this Pure ma, Func> f, Func g) => Effect.pure(ma.Value).SelectMany(f, g); /// /// Monad bind /// public static Effect SelectMany( this Lift ff, Func> f, Func g) => Effect.lift(ff.Function).SelectMany(f, g); /// /// Monad bind /// public static Effect Bind( this K, A> ma, Func> f) => Effect.liftM(ma).Bind(f); /// /// Monad bind /// public static Effect Bind( this IO ma, Func> f) => Effect.liftIO(ma).Bind(f); /// /// Monad bind /// public static Effect Bind( this Pure ma, Func> f) => Effect.pure(ma.Value).Bind(f); /// /// Monad bind /// public static Effect Bind( this Lift ff, Func> f) => Effect.lift(ff.Function).Bind(f); /// /// Monad bind operation /// public static Effect SelectMany( this K, A> ma, Func> bind, Func project) => ma.Bind(a => bind(a) switch { { Flag: true } => Effect.pure(project(a, default)), var guard => Effect.error(guard.OnFalse()) }).As(); /// /// Monad bind operation /// public static Effect SelectMany( this Guard ma, Func, B>> bind, Func project) => ma switch { { Flag: true } => bind(default).Map(b => project(default, b)).As(), var guard => Effect.error(guard.OnFalse()) }; } ================================================ FILE: LanguageExt.Streaming/Pipes/Effect/Effect.Module.cs ================================================ using System; using System.Threading.Tasks; using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt.Pipes; /// /// `Effect` streaming effect monad-transformer /// public static class Effect { /// /// Create an effect that simply returns a bound value without yielding anything /// /// Effect runtime type /// Bound value type /// public static Effect pure(A value) => PipeT.pure, A>(value); /// /// Create an effect that always fails /// /// Effect runtime type /// Bound value type /// public static Effect error(Error value) => PipeT.error, A>(value); /// /// Create an effect that yields nothing at all /// /// Effect runtime type /// Bound value type /// public static Effect empty() => PipeT.empty, A>(); /// /// Create an effect that lazily returns a bound value without yielding anything /// /// Effect runtime type /// Bound value type /// public static Effect lift(Func f) => PipeT.lift, A>(f); /// /// Create an effect that simply returns the bound value of the lifted monad without yielding anything /// /// Effect runtime type /// Bound value type /// public static Effect liftM(K, A> ma) => PipeT.liftM, A>(ma); /// /// Create an effect that simply returns the bound value of the lifted monad without yielding anything /// /// Effect runtime type /// Bound value type /// public static Effect liftIO(IO ma) => PipeT.liftIO, A>(ma); /// /// Create a lazy proxy /// /// Effect runtime type /// Bound value type /// public static Effect liftT(Func> f) => PipeT.liftT(() => f().Proxy); /// /// Create an asynchronous lazy proxy /// /// Effect runtime type /// Bound value type /// public static Effect liftT(Func>> f) => PipeT.liftT(() => f().Map(p => p.Proxy)); /// /// Create an asynchronous proxy /// /// Effect runtime type /// Bound value type /// public static Effect liftT(ValueTask> f) => PipeT.liftT(f.Map(p => p.Proxy)); /// /// Continually repeat the provided operation /// /// Effect runtime type /// Bound value type /// public static Effect repeat(Effect ma) => PipeT.repeat(ma.Proxy).ToEffect(); /// /// Repeat the provided operation based on the schedule provided /// /// Effect runtime type /// Bound value type /// public static Effect repeat(Schedule schedule, Effect ma) => PipeT.repeat(schedule, ma.Proxy).ToEffect(); /// /// Continually lift and repeat the provided operation /// /// Effect runtime type /// Bound value type /// public static Effect repeatM(K, A> ma) => PipeT.repeatM, A>(ma).ToEffect(); /// /// Repeat the provided operation based on the schedule provided /// /// Effect runtime type /// Bound value type /// public static Effect repeatM(Schedule schedule, K, A> ma) => PipeT.repeatM, A>(schedule, ma).ToEffect(); } ================================================ FILE: LanguageExt.Streaming/Pipes/Effect/Effect.Monad.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using LanguageExt.Async.Linq; using LanguageExt.Traits; namespace LanguageExt.Pipes; public class Effect : MonadT, Eff>, MonadUnliftIO> { static K, B> Monad>.Bind(K, A> ma, Func, B>> f) => ma.As().Bind(x => f(x).As()); static K, B> Monad>.Recur(A value, Func, Next>> f) => Monad.unsafeRecur(value, f); static K, B> Functor>.Map(Func f, K, A> ma) => ma.As().Map(f); static K, A> Applicative>.Pure(A value) => Effect.pure(value); static K, B> Applicative>.Apply(K, Func> mf, K, A> ma) => ma.As().ApplyBack(mf.As()); static K, B> Applicative>.Apply(K, Func> mf, Memo, A> ma) => new PipeTMemo, A>(ma.Lower().Map(ma => ma.As().Proxy.Kind()).Lift()) .ApplyBack(mf.As().Proxy) .ToEffect(); static K, A> MonadT, Eff>.Lift(K, A> ma) => Effect.liftM(ma); static K, A> MonadIO>.LiftIO(IO ma) => Effect.liftIO(ma); static K, B> MonadUnliftIO>.MapIO(K, A> ma, Func, IO> f) => ma.As().MapIO(f); static K, IO> MonadUnliftIO>.ToIO(K, A> ma) => ma.MapIO(IO.pure); static K, ForkIO> MonadUnliftIO>.ForkIO( K, A> ma, Option timeout) => Effect.liftM(ma.As().Run().ForkIO(timeout)); static K, B> Applicative>.Action( K, A> ma, K, B> mb) => Effect.liftM(ma.As().Run().Action(mb.As().Run())); static K, A> Applicative>.Actions( IterableNE, A>> fas) => fas.Select(fa => fa.As().Proxy.Kind()) .Actions() .ToEffect(); } ================================================ FILE: LanguageExt.Streaming/Pipes/Effect/Effect.Operators.cs ================================================ using LanguageExt.Traits; namespace LanguageExt.Pipes; public static partial class EffectExtensions { extension(K, A>) { /// /// Downcast /// public static Effect operator +(K, A> ma) => (Effect) ma; /// /// Downcast /// public static Effect operator >>(K, A> ma, Lower lower) => +ma; } } ================================================ FILE: LanguageExt.Streaming/Pipes/Effect/Effect.cs ================================================ using System; using System.Diagnostics.Contracts; using System.Threading; using System.Threading.Tasks; using LanguageExt.Traits; namespace LanguageExt.Pipes; public readonly record struct Effect(PipeT, A> Proxy) : K, A> { [Pure] public Effect Map(Func f) => Proxy.Map(f); [Pure] public Effect MapM(Func, Eff> f) => Proxy.MapM(mx => f(mx.As())).ToEffect(); [Pure] public Effect MapIO(Func, IO> f) => Proxy.MapIO(f); [Pure] public Effect ApplyBack(Effect> ff) => Proxy.ApplyBack(ff.Proxy); [Pure] public Effect Action(Effect fb) => Proxy.Action(fb.Proxy); [Pure] public Effect Bind(Func> f) => Proxy.Bind(x => f(x).Proxy); [Pure] public Effect Bind(Func, B>> f) => Proxy.Bind(f); [Pure] public Effect Bind(Func> f) => Proxy.Bind(f); [Pure] public Effect Bind(Func> f) => Proxy.Bind(f); [Pure] public Effect Bind(Func> f) => Proxy.Bind(f); [Pure] public Effect Select(Func f) => Proxy.Map(f); [Pure] public Effect SelectMany(Func> f, Func g) => Proxy.SelectMany(x => f(x).Proxy, g); [Pure] public Effect SelectMany(Func, B>> f, Func g) => Proxy.SelectMany(f, g); [Pure] public Effect SelectMany(Func> f, Func g) => Proxy.SelectMany(f, g); [Pure] public Effect SelectMany(Func> f, Func g) => Proxy.SelectMany(f, g); [Pure] public Effect SelectMany(Func> f, Func g) => Proxy.SelectMany(f, g); [Pure] public Eff Run() => Proxy.Run().As(); [Pure] public static implicit operator Effect(PipeT, A> pipe) => pipe.ToEffect(); [Pure] public static implicit operator Effect(EffectT, A> pipe) => pipe.Proxy; [Pure] public static implicit operator Effect(Pure rhs) => Effect.pure(rhs.Value); [Pure] public static Effect operator |(Schedule lhs, Effect rhs) => ReferenceEquals(lhs, Schedule.Forever) ? Effect.repeat(rhs) : Effect.repeat(lhs, rhs); [Pure] public static Effect operator |(Effect lhs, Schedule rhs) => ReferenceEquals(rhs, Schedule.Forever) ? Effect.repeat(lhs) : Effect.repeat(rhs, lhs); } ================================================ FILE: LanguageExt.Streaming/Pipes/EffectT/EffectT.Extensions.cs ================================================ using System; using System.Diagnostics.Contracts; using LanguageExt.Traits; namespace LanguageExt.Pipes; public static partial class EffectTExtensions { /// /// Transformation from `PipeT` to `EffectT`. /// [Pure] public static EffectT ToEffect(this K, A> pipe) where M : MonadIO => new(pipe.As()); /// /// Downcast /// [Pure] public static EffectT As(this K, A> ma) where M : MonadIO => (EffectT)ma; /// /// Convert to the `Eff` version of `Effect` /// [Pure] public static Effect ToEff(this K>, A> ma) => ma.As(); /// /// Convert to the `Eff` version of `Effect` /// [Pure] public static EffectT, A> FromEff(this K, A> ma) => new(ma.As().Proxy); /// /// Monad bind /// [Pure] public static EffectT SelectMany( this K ma, Func> f, Func g) where M : MonadIO => EffectT.liftM(ma).SelectMany(f, g); /// /// Monad bind /// [Pure] public static EffectT SelectMany( this IO ma, Func> f, Func g) where M : MonadIO => EffectT.liftIO(ma).SelectMany(f, g); /// /// Monad bind /// [Pure] public static EffectT SelectMany( this Pure ma, Func> f, Func g) where M : MonadIO => EffectT.pure(ma.Value).SelectMany(f, g); /// /// Monad bind /// [Pure] public static EffectT SelectMany( this Lift ff, Func> f, Func g) where M : MonadIO => EffectT.lift(ff.Function).SelectMany(f, g); /// /// Monad bind /// [Pure] public static EffectT Bind( this K ma, Func> f) where M : MonadIO => EffectT.liftM(ma).Bind(f); /// /// Monad bind /// [Pure] public static EffectT Bind( this IO ma, Func> f) where M : MonadIO => EffectT.liftIO(ma).Bind(f); /// /// Monad bind /// [Pure] public static EffectT Bind( this Pure ma, Func> f) where M : MonadIO => EffectT.pure(ma.Value).Bind(f); /// /// Monad bind /// [Pure] public static EffectT Bind( this Lift ff, Func> f) where M : MonadIO => EffectT.lift(ff.Function).Bind(f); /// /// Monad bind operation /// [Pure] public static EffectT SelectMany( this K, A> ma, Func> bind, Func project) where M : MonadIO, Fallible => ma.Bind(a => bind(a) switch { { Flag: true } => EffectT.pure(project(a, default)), var guard => EffectT.fail(guard.OnFalse()) }).As(); /// /// Monad bind operation /// [Pure] public static EffectT SelectMany( this Guard ma, Func, B>> bind, Func project) where M : MonadIO, Fallible => ma switch { { Flag: true } => bind(default).Map(b => project(default, b)).As(), var guard => EffectT.fail(guard.OnFalse()) }; [Pure] public static EffectT MapIO(this K, A> ma, Func, IO> f) where M : MonadUnliftIO => ma.As().MapM(m => M.MapIOMaybe(m, f)); } ================================================ FILE: LanguageExt.Streaming/Pipes/EffectT/EffectT.Module.cs ================================================ using System; using System.Threading.Tasks; using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt.Pipes; /// /// `EffectT` streaming effect monad-transformer /// public static class EffectT { /// /// Create an effect that simply returns a bound value without yielding anything /// /// Lifted monad type /// Bound value type /// public static EffectT pure(A value) where M : MonadIO => PipeT.pure(value); /// /// Create an effect that always fails /// /// Failure type /// Lifted monad type /// Bound value type /// public static EffectT fail(E value) where M : MonadIO, Fallible => PipeT.fail(value); /// /// Create an effect that always fails /// /// Lifted monad type /// Bound value type /// public static EffectT error(Error value) where M : MonadIO, Fallible => PipeT.error(value); /// /// Create an effect that yields nothing at all /// /// Lifted monad type /// Bound value type /// public static EffectT empty() where M : MonadIO, MonoidK => PipeT.empty(); /// /// Create an effect that lazily returns a bound value without yielding anything /// /// Lifted monad type /// Bound value type /// public static EffectT lift(Func f) where M : MonadIO => PipeT.lift(f); /// /// Create an effect that simply returns the bound value of the lifted monad without yielding anything /// /// Lifted monad type /// Bound value type /// public static EffectT liftM(K ma) where M : MonadIO => PipeT.liftM(ma); /// /// Create an effect that simply returns the bound value of the lifted monad without yielding anything /// /// Lifted monad type /// Bound value type /// public static EffectT liftIO(IO ma) where M : MonadIO => PipeT.liftIO(ma); /// /// Create a lazy proxy /// /// Lifted monad type /// Bound value type /// public static EffectT liftT(Func> f) where M : MonadIO => PipeT.liftT(() => f().Proxy); /// /// Create an asynchronous lazy proxy /// /// Lifted monad type /// Bound value type /// public static EffectT liftT(Func>> f) where M : MonadIO => PipeT.liftT(() => f().Map(p => p.Proxy)); /// /// Create an asynchronous proxy /// /// Lifted monad type /// Bound value type /// public static EffectT liftT(ValueTask> f) where M : MonadIO => PipeT.liftT(f.Map(p => p.Proxy)); /// /// Continually repeat the provided operation /// /// Lifted monad type /// Bound value type /// public static EffectT repeat(EffectT ma) where M : MonadIO => PipeT.repeat(ma.Proxy).ToEffect(); /// /// Repeat the provided operation based on the schedule provided /// /// Lifted monad type /// Bound value type /// public static EffectT repeat(Schedule schedule, EffectT ma) where M : MonadIO => PipeT.repeat(schedule, ma.Proxy).ToEffect(); /// /// Continually lift and repeat the provided operation /// /// Lifted monad type /// Bound value type /// public static EffectT repeatM(K ma) where M : MonadIO => PipeT.repeatM(ma).ToEffect(); /// /// Repeat the provided operation based on the schedule provided /// /// Lifted monad type /// Bound value type /// public static EffectT repeatM(Schedule schedule, K ma) where M : MonadIO => PipeT.repeatM(schedule, ma).ToEffect(); } ================================================ FILE: LanguageExt.Streaming/Pipes/EffectT/EffectT.Monad.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using LanguageExt.Async.Linq; using LanguageExt.Traits; namespace LanguageExt.Pipes; public class EffectT : MonadT, M>, MonadUnliftIO> where M : MonadIO { static K, B> Monad>.Bind(K, A> ma, Func, B>> f) => ma.As().Bind(x => f(x).As()); static K, B> Monad>.Recur(A value, Func, Next>> f) => Monad.unsafeRecur(value, f); static K, B> Functor>.Map(Func f, K, A> ma) => ma.As().Map(f); static K, A> Applicative>.Pure(A value) => EffectT.pure(value); static K, B> Applicative>.Apply(K, Func> mf, K, A> ma) => ma.As().ApplyBack(mf.As()); static K, B> Applicative>.Apply(K, Func> mf, Memo, A> ma) => new PipeTMemo(ma.Lower().Map(ma => ma.As().Proxy.Kind()).Lift()) .ApplyBack(mf.As().Proxy) .ToEffect(); static K, A> MonadT, M>.Lift(K ma) => EffectT.liftM(ma); static K, A> MonadIO>.LiftIO(IO ma) => EffectT.liftIO(ma); static K, B> MonadUnliftIO>.MapIO(K, A> ma, Func, IO> f) => ma.As().MapM(m => M.MapIOMaybe(m, f)); static K, IO> MonadUnliftIO>.ToIO(K, A> ma) => ma.As().MapM(M.ToIOMaybe); static K, ForkIO> MonadUnliftIO>.ForkIO( K, A> ma, Option timeout) => MonadT.lift, M, ForkIO>(ma.As().Run().ForkIOMaybe(timeout)); static K, B> Applicative>.Action( K, A> ma, K, B> mb) => EffectT.liftM(ma.As().Run().Action(mb.As().Run())); static K, A> Applicative>.Actions( IterableNE, A>> fas) => fas.Select(fa => fa.As().Proxy.Kind()) .Actions() .ToEffect(); } ================================================ FILE: LanguageExt.Streaming/Pipes/EffectT/EffectT.Operators.cs ================================================ using LanguageExt.Traits; namespace LanguageExt.Pipes; public static partial class EffectTExtensions { extension(K, A>) where M : MonadIO { /// /// Downcast /// public static EffectT operator +(K, A> ma) => (EffectT) ma; /// /// Downcast /// public static EffectT operator >>(K, A> ma, Lower lower) => +ma; } } ================================================ FILE: LanguageExt.Streaming/Pipes/EffectT/EffectT.cs ================================================ using System; using System.Diagnostics.Contracts; using System.Threading; using System.Threading.Tasks; using LanguageExt.Traits; namespace LanguageExt.Pipes; public readonly record struct EffectT(PipeT Proxy) : K, A> where M : MonadIO { [Pure] public EffectT Map(Func f) => Proxy.Map(f); [Pure] public EffectT MapM(Func, K> f) => Proxy.MapM(f); [Pure] public EffectT ApplyBack(EffectT> ff) => Proxy.ApplyBack(ff.Proxy); [Pure] public EffectT Action(EffectT fb) => Proxy.Action(fb.Proxy); [Pure] public EffectT Bind(Func> f) => Proxy.Bind(x => f(x).Proxy); [Pure] public EffectT Bind(Func> f) => Proxy.Bind(f); [Pure] public EffectT Bind(Func> f) => Proxy.Bind(f); [Pure] public EffectT Bind(Func> f) => Proxy.Bind(f); [Pure] public EffectT Bind(Func> f) => Proxy.Bind(f); [Pure] public EffectT Select(Func f) => Proxy.Map(f); [Pure] public EffectT SelectMany(Func> f, Func g) => Proxy.SelectMany(x => f(x).Proxy, g); [Pure] public EffectT SelectMany(Func> f, Func g) => Proxy.SelectMany(f, g); [Pure] public EffectT SelectMany(Func> f, Func g) => Proxy.SelectMany(f, g); [Pure] public EffectT SelectMany(Func> f, Func g) => Proxy.SelectMany(f, g); [Pure] public EffectT SelectMany(Func> f, Func g) => Proxy.SelectMany(f, g); [Pure] public K Run() => Proxy.Run(); [Pure] public static implicit operator EffectT(PipeT pipe) => pipe.ToEffect(); [Pure] public static implicit operator EffectT(Pure rhs) => EffectT.pure(rhs.Value); [Pure] public static EffectT operator |(Schedule lhs, EffectT rhs) => ReferenceEquals(lhs, Schedule.Forever) ? EffectT.repeat(rhs) : EffectT.repeat(lhs, rhs); [Pure] public static EffectT operator |(EffectT lhs, Schedule rhs) => ReferenceEquals(rhs, Schedule.Forever) ? EffectT.repeat(lhs) : EffectT.repeat(rhs, lhs); } ================================================ FILE: LanguageExt.Streaming/Pipes/Pipe/Pipe.Extensions.cs ================================================ using System; using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt.Pipes; public static partial class PipeExtensions { /// /// Downcast /// public static Pipe As(this K, A> ma) => (Pipe)ma; /// /// Monad bind /// public static Pipe SelectMany( this K, A> ma, Func> f, Func g) => Pipe.liftM(ma).SelectMany(f, g); /// /// Monad bind /// public static Pipe SelectMany( this IO ma, Func> f, Func g) => Pipe.liftIO(ma).SelectMany(f, g); /// /// Monad bind /// public static Pipe SelectMany( this Pure ma, Func> f, Func g) => Pipe.pure(ma.Value).SelectMany(f, g); /// /// Monad bind /// public static Pipe SelectMany( this Lift ff, Func> f, Func g) => Pipe.lift(ff.Function).SelectMany(f, g); /// /// Monad bind /// public static Pipe Bind( this K, A> ma, Func> f) => Pipe.liftM(ma).Bind(f); /// /// Monad bind /// public static Pipe Bind( this IO ma, Func> f) => Pipe.liftIO(ma).Bind(f); /// /// Monad bind /// public static Pipe Bind( this Pure ma, Func> f) => Pipe.pure(ma.Value).Bind(f); /// /// Monad bind /// public static Pipe Bind( this Lift ff, Func> f) => Pipe.lift(ff.Function).Bind(f); /// /// Monad bind operation /// public static Pipe SelectMany( this K, A> ma, Func> bind, Func project) => ma.Bind(a => bind(a) switch { { Flag: true } => Pipe.pure(project(a, default)), var guard => Pipe.error(guard.OnFalse()) }).As(); /// /// Monad bind operation /// public static Pipe SelectMany( this Guard ma, Func, B>> bind, Func project) => ma switch { { Flag: true } => bind(default).Map(b => project(default, b)).As(), var guard => Pipe.error(guard.OnFalse()) }; } ================================================ FILE: LanguageExt.Streaming/Pipes/Pipe/Pipe.Module.cs ================================================ using System; using System.Collections.Generic; using System.Threading.Tasks; using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt.Pipes; /// /// `Pipe` streaming producer monad-transformer /// public static class Pipe { /// /// Yield a value downstream /// /// Effect runtime type /// Stream value to consume /// Stream value to produce /// public static Pipe yield(OUT value) => PipeT.yield, IN, OUT>(value); /// /// Yield all values downstream /// /// Effect runtime type /// Stream value to consume /// Stream value to produce /// public static Pipe yieldAll(IEnumerable values) => PipeT.yieldAll, IN, OUT>(values); /// /// Yield all values downstream /// /// Effect runtime type /// Stream value to consume /// Stream value to produce /// public static Pipe yieldAll(IAsyncEnumerable values) => PipeT.yieldAll, IN, OUT>(values); /// /// Evaluate the `M` monad repeatedly, yielding its bound values downstream /// /// Effect runtime type /// Stream value to consume /// Stream value to produce /// public static Pipe yieldRepeat(K, OUT> ma) => PipeT.yieldRepeat, IN, OUT>(ma); /// /// Evaluate the `IO` monad repeatedly, yielding its bound values downstream /// /// Effect runtime type /// Stream value to consume /// Stream value to produce /// public static Pipe yieldRepeatIO(IO ma) => PipeT.yieldRepeatIO, IN, OUT>(ma); /// /// Await a value from upstream /// /// Effect runtime type /// Stream value to consume /// Stream value to produce /// Pipe public static Pipe awaiting() => PipeT.awaiting, IN, OUT>(); /// /// Create a pipe that filters out values that return `false` when applied to a predicate function /// /// Predicate function /// Effect runtime type /// Stream value to consume and produce /// Pipe public static Pipe filter(Func f) => PipeT.filter, A>(f); /// /// Create a pipe from a mapping function /// /// Mapping function /// Effect runtime type /// Stream value to consume /// Stream value to produce /// Pipe public static Pipe map(Func f) => PipeT.map, IN, OUT>(f); /// /// Create a pipe from a mapping function /// /// Mapping function /// Effect runtime type /// Stream value to consume /// Stream value to produce /// Pipe public static Pipe mapM(Func, OUT>> f) => PipeT.mapM(f); /// /// Create a pipe that simply returns a bound value without yielding anything /// /// Effect runtime type /// Stream value to consume /// Stream value to produce /// Bound value type /// public static Pipe pure(A value) => PipeT.pure, A>(value); /// /// Create a pipe that always fails /// /// Effect runtime type /// Stream value to consume /// Stream value to produce /// Bound value type /// public static Pipe error(Error value) => PipeT.error, A>(value); /// /// Create a pipe that yields nothing at all /// /// Effect runtime type /// Stream value to consume /// Stream value to produce /// Bound value type /// public static Pipe empty() => PipeT.empty, A>(); /// /// Create a pipe that simply returns a bound value without yielding anything /// /// Effect runtime type /// Stream value to consume /// Stream value to produce /// Bound value type /// public static Pipe lift(Func f) => PipeT.lift, A>(f); /// /// Create a lazy pipe /// /// Effect runtime type /// Stream value to consume /// Stream value to produce /// Bound value type /// public static Pipe liftT(Func> f) => PipeT.liftT(() => f().Proxy); /// /// Create an asynchronous lazy pipe /// /// Effect runtime type /// Stream value to consume /// Stream value to produce /// Bound value type /// public static Pipe liftT(Func>> f) => PipeT.liftT(async () => (await f()).Proxy); /// /// Create an asynchronous pipe /// /// Effect runtime type /// Stream value to consume /// Stream value to produce /// Bound value type /// public static Pipe liftT(ValueTask> task) => PipeT.liftT(task.Map(t => t.Proxy)); /// /// Create a pipe that simply returns the bound value of the lifted monad without yielding anything /// /// Effect runtime type /// Stream value to consume /// Stream value to produce /// Bound value type /// public static Pipe liftM(K, A> ma) => PipeT.liftM, A>(ma); /// /// Create a pipe that simply returns the bound value of the lifted monad without yielding anything /// /// Effect runtime type /// Stream value to consume /// Stream value to produce /// Bound value type /// public static Pipe liftM(ValueTask, A>> ma) => PipeT.liftM, A>(ma); /// /// Create a pipe that simply returns the bound value of the lifted monad without yielding anything /// /// Effect runtime type /// Stream value to consume /// Stream value to produce /// Bound value type /// public static Pipe liftIO(IO ma) => PipeT.liftIO, A>(ma); /// /// Continually repeat the provided operation /// /// Effect runtime type /// Stream value to consume /// Stream value to produce /// Bound value type /// public static Pipe repeat(Pipe ma) => PipeT.repeat(ma.Proxy); /// /// Repeat the provided operation based on the schedule provided /// /// Effect runtime type /// Stream value to consume /// Stream value to produce /// Bound value type /// public static Pipe repeat(Schedule schedule, Pipe ma) => PipeT.repeat(schedule, ma.Proxy); /// /// Continually lift and repeat the provided operation /// /// Effect runtime type /// Stream value to consume /// Stream value to produce /// Bound value type /// public static Pipe repeatM(K, A> ma) => PipeT.repeatM, A>(ma); /// /// Repeat the provided operation based on the schedule provided /// /// Effect runtime type /// Stream value to consume /// Stream value to produce /// Bound value type /// public static Pipe repeatM(Schedule schedule, K, A> ma) => PipeT.repeatM, A>(schedule, ma); /// /// Fold the given pipe until the `Schedule` completes. /// Once complete, the pipe yields the aggregated value downstream. /// /// Schedule to run each item /// Fold function /// Initial state /// Pipe to fold /// Effect runtime type /// Stream value to consume /// Stream value to produce /// Bound value type /// public static Pipe fold( Schedule Time, Func Fold, OUT Init, Pipe Item) => Item.Proxy.Fold(Time, Fold, Init); /// /// Fold the given pipe until the predicate is `true`. Once `true` the pipe yields the /// aggregated value downstream. /// /// Fold function /// Until predicate /// Initial state /// Pipe to fold /// Effect runtime type /// Stream value to consume /// Stream value to produce /// Bound value type /// public static Pipe foldUntil( Func Fold, Func<(OUT State, A Value), bool> Pred, OUT Init, Pipe Item) => Item.Proxy.FoldUntil(Fold, Pred, Init); /// /// Fold the given pipe until the predicate is `true` or the `Schedule` completes. /// Once `true`, or completed, the pipe yields the aggregated value downstream. /// /// Schedule to run each item /// Fold function /// Until predicate /// Initial state /// Pipe to fold /// Effect runtime type /// Stream value to consume /// Stream value to produce /// Bound value type /// public static Pipe foldUntil( Schedule Time, Func Fold, Func<(OUT State, A Value), bool> Pred, OUT Init, Pipe Item) => Item.Proxy.FoldUntil(Time, Fold, Pred, Init); /// /// Fold the given pipe while the predicate is `true`. Once `false` the pipe yields the /// aggregated value downstream. /// /// Fold function /// Until predicate /// Initial state /// Pipe to fold /// Effect runtime type /// Stream value to consume /// Stream value to produce /// Bound value type /// public static Pipe foldWhile( Func Fold, Func<(OUT State, A Value), bool> Pred, OUT Init, Pipe Item) => Item.Proxy.FoldWhile(Fold, Pred, Init); /// /// Fold the given pipe while the predicate is `true` or the `Schedule` completes. /// Once `false`, or completed, the pipe yields the aggregated value downstream. /// /// Schedule to run each item /// Fold function /// Until predicate /// Initial state /// Pipe to fold /// Effect runtime type /// Stream value to consume /// Stream value to produce /// Bound value type /// public static Pipe foldWhile( Schedule Time, Func Fold, Func<(OUT State, A Value), bool> Pred, OUT Init, Pipe Item) => Item.Proxy.FoldWhile(Time, Fold, Pred, Init); /// /// Fold the given pipe until the `Schedule` completes. /// Once complete, the pipe yields the aggregated value downstream. /// /// Schedule to run each item /// Fold function /// Initial state /// Effect runtime type /// Stream value to consume /// Stream value to produce /// public static Pipe fold( Schedule Time, Func Fold, OUT Init) => PipeT.awaiting, IN, OUT>().Fold(Time, Fold, Init); /// /// Fold the given pipe until the predicate is `true`. Once `true` the pipe yields the /// aggregated value downstream. /// /// Fold function /// Until predicate /// Initial state /// Effect runtime type /// Stream value to consume /// Stream value to produce /// public static Pipe foldUntil( Func Fold, Func<(OUT State, IN Value), bool> Pred, OUT Init) => PipeT.awaiting, IN, OUT>().FoldUntil(Fold, Pred, Init); /// /// Fold the given pipe until the predicate is `true` or the `Schedule` completes. /// Once `true`, or completed, the pipe yields the aggregated value downstream. /// /// Schedule to run each item /// Fold function /// Until predicate /// Initial state /// Effect runtime type /// Stream value to consume /// Stream value to produce /// public static Pipe foldUntil( Schedule Time, Func Fold, Func<(OUT State, IN Value), bool> Pred, OUT Init) => PipeT.awaiting, IN, OUT>().FoldUntil(Time, Fold, Pred, Init); /// /// Fold the given pipe while the predicate is `true`. Once `false` the pipe yields the /// aggregated value downstream. /// /// Fold function /// Until predicate /// Initial state /// Effect runtime type /// Stream value to consume /// Stream value to produce /// public static Pipe foldWhile( Func Fold, Func<(OUT State, IN Value), bool> Pred, OUT Init) => PipeT.awaiting, IN, OUT>().FoldWhile(Fold, Pred, Init); /// /// Fold the given pipe while the predicate is `true` or the `Schedule` completes. /// Once `false`, or completed, the pipe yields the aggregated value downstream. /// /// Schedule to run each item /// Fold function /// Until predicate /// Initial state /// Effect runtime type /// Stream value to consume /// Stream value to produce /// public static Pipe foldWhile( Schedule Time, Func Fold, Func<(OUT State, IN Value), bool> Pred, OUT Init) => PipeT.awaiting, IN, OUT>().FoldWhile(Time, Fold, Pred, Init); } ================================================ FILE: LanguageExt.Streaming/Pipes/Pipe/Pipe.Monad.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using LanguageExt.Async.Linq; using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt.Pipes; public class Pipe : MonadT, Eff>, MonadUnliftIO> { static K, B> Monad>.Bind( K, A> ma, Func, B>> f) => ma.As().Bind(x => f(x).As()); static K, B> Monad>.Recur(A value, Func, Next>> f) => Monad.unsafeRecur(value, f); static K, B> Functor>.Map( Func f, K, A> ma) => ma.As().Map(f); static K, A> Applicative>.Pure(A value) => Pipe.pure(value); static K, B> Applicative>.Apply( K, Func> mf, K, A> ma) => ma.As().ApplyBack(mf.As()); static K, B> Applicative>.Apply( K, Func> mf, Memo, A> ma) => new Pipe( new PipeTMemo, A>(ma.Lower().Map(ma => ma.As().Proxy.Kind()).Lift()) .ApplyBack(mf.As().Proxy)); static K, B> Applicative>.Action( K, A> ma, K, B> mb) => Pipe.liftM(ma.As().Run().Action(mb.As().Run())); static K, A> Applicative>.Actions(IterableNE, A>> fas) => Pipe.liftM(fas.Select(fa => fa.As().Run().Kind()).Actions()); static K, A> MonadT, Eff>.Lift(K, A> ma) => Pipe.liftM(ma); static K, A> MonadIO>.LiftIO(IO ma) => Pipe.liftIO(ma); static K, B> MonadUnliftIO>.MapIO(K, A> ma, Func, IO> f) => ma.As().MapIO(f); static K, IO> MonadUnliftIO>.ToIO(K, A> ma) => ma.MapIO(IO.pure); } ================================================ FILE: LanguageExt.Streaming/Pipes/Pipe/Pipe.Operators.cs ================================================ using LanguageExt.Traits; namespace LanguageExt.Pipes; public static partial class PipeExtensions { extension(K, A>) { /// /// Downcast /// public static Pipe operator +(K, A> ma) => (Pipe) ma; /// /// Downcast /// public static Pipe operator >>(K, A> ma, Lower lower) => +ma; } } ================================================ FILE: LanguageExt.Streaming/Pipes/Pipe/Pipe.cs ================================================ using System; using System.Diagnostics.Contracts; using System.Threading; using System.Threading.Tasks; using LanguageExt.Traits; namespace LanguageExt.Pipes; /// /// `Pipe` monad-transformer base-type for `Eff`-based streaming components: /// /// * `Producer` is a `Pipe` with the `IN` 'closed off' with `Unit` /// * `Consumer` is a `Pipe` with the `OUT` 'closed off' with `Void` /// * `Effect` is a `Pipe` with the `IN` and `OUT` 'closed off' with `Unit` upstream and `Void` downstream /// /// /// /// Unlike the general purpose `PipeT`, which will lift any monad, this type only lifts the `Eff` monad. /// /// Type of values to be consumed /// Type of values to be produced /// Lifted monad type /// Bound value type public record Pipe(PipeT, A> Proxy) : K, A> { [Pure] public Pipe Compose(Pipe rhs) => Proxy.Compose(rhs.Proxy); [Pure] public Pipe Compose(PipeT, A> rhs) => Proxy.Compose(rhs); [Pure] public Consumer Compose(Consumer rhs) => Proxy.Compose(rhs.Proxy); [Pure] public Consumer Compose(ConsumerT, A> rhs) => Proxy.Compose(rhs.Proxy); [Pure] public static Pipe operator | (Pipe lhs, Pipe rhs) => lhs.Compose(rhs); [Pure] public static Pipe operator | (Pipe lhs, PipeT, A> rhs) => lhs.Compose(rhs); [Pure] public static Producer operator | (Producer lhs, Pipe rhs) => lhs.Compose(rhs); [Pure] public static Producer operator | (ProducerT, A> lhs, Pipe rhs) => lhs.Compose(rhs.Proxy).Proxy; [Pure] public static Consumer operator | (Pipe lhs, Consumer rhs) => lhs.Compose(rhs); [Pure] public static Consumer operator | (Pipe lhs, ConsumerT, A> rhs) => lhs.Compose(rhs); [Pure] public static implicit operator Pipe(Pure rhs) => Pipe.pure(rhs.Value); [Pure] public static implicit operator Pipe(PipeT, A> rhs) => new(rhs); [Pure] public Pipe Map(Func f) => Proxy.Map(f); [Pure] public Pipe MapM(Func, Eff> f) => Proxy.MapM(mx => f(mx.As())); [Pure] public Pipe ApplyBack(Pipe> ff) => Proxy.ApplyBack(ff.Proxy); [Pure] public Pipe Action(Pipe fb) => Proxy.Action(fb.Proxy); [Pure] public Pipe Bind(Func> f) => Proxy.Bind(x => f(x).Proxy); [Pure] public Pipe Bind(Func> f) => Proxy.Bind(f); [Pure] public Pipe Bind(Func, B>> f) => Proxy.Bind(f); [Pure] public Pipe Bind(Func> f) => Proxy.Bind(f); [Pure] public Pipe Bind(Func> f) => Proxy.Bind(f); [Pure] public Pipe BindAsync(Func>> f) => Proxy.BindAsync(async x => (await f(x)).Proxy); [Pure] public Pipe MapIO(Func, IO> f) => Proxy.MapIO(f); [Pure] public Pipe Select(Func f) => Map(f); [Pure] public Pipe SelectMany(Func> f, Func g) => Proxy.SelectMany(x => f(x).Proxy, g); [Pure] public Pipe SelectMany(Func, B>> f, Func g) => Proxy.SelectMany(f, g); [Pure] public Pipe SelectMany(Func> f, Func g) => Proxy.SelectMany(f, g); [Pure] public Pipe SelectMany(Func> f, Func g) => Proxy.SelectMany(f, g); [Pure] public Pipe SelectMany(Func> f, Func g) => Proxy.SelectMany(f, g); /// /// Fold the given pipe until the `Schedule` completes. /// Once complete, the pipe yields the aggregated value downstream. /// /// Schedule to run each item /// Fold function /// Initial state /// Stream value to consume /// Stream value to produce /// Lifted monad type /// Bound value type /// public Pipe Fold( Schedule Time, Func Fold, OUT Init) => Proxy.Fold(Time, Fold, Init); /// /// Fold the given pipe until the predicate is `true`. Once `true` the pipe yields the /// aggregated value downstream. /// /// Fold function /// Until predicate /// Initial state /// Stream value to consume /// Stream value to produce /// Lifted monad type /// Bound value type /// public Pipe FoldUntil( Func Fold, Func<(OUT State, A Value), bool> Pred, OUT Init) => Proxy.FoldUntil(Fold, Pred, Init); /// /// Fold the given pipe until the predicate is `true` or the `Schedule` completes. /// Once `true`, or completed, the pipe yields the aggregated value downstream. /// /// Schedule to run each item /// Fold function /// Until predicate /// Initial state /// Stream value to consume /// Stream value to produce /// Lifted monad type /// Bound value type /// public Pipe FoldUntil( Schedule Time, Func Fold, Func<(OUT State, A Value), bool> Pred, OUT Init) => Proxy.FoldUntil(Time, Fold, Pred, Init); /// /// Fold the given pipe while the predicate is `true`. Once `false` the pipe yields the /// aggregated value downstream. /// /// Fold function /// Until predicate /// Initial state /// Stream value to consume /// Stream value to produce /// Lifted monad type /// Bound value type /// public Pipe FoldWhile( Func Fold, Func<(OUT State, A Value), bool> Pred, OUT Init) => Proxy.FoldWhile(Fold, Pred, Init); /// /// Fold the given pipe while the predicate is `true` or the `Schedule` completes. /// Once `false`, or completed, the pipe yields the aggregated value downstream. /// /// Schedule to run each item /// Fold function /// Until predicate /// Initial state /// Stream value to consume /// Stream value to produce /// Lifted monad type /// Bound value type /// public Pipe FoldWhile( Schedule Time, Func Fold, Func<(OUT State, A Value), bool> Pred, OUT Init) => Proxy.FoldWhile(Time, Fold, Pred, Init); [Pure] internal virtual Eff Run() => Proxy.Run().As(); } ================================================ FILE: LanguageExt.Streaming/Pipes/PipeT/PipeT.Cached.cs ================================================ using LanguageExt.Traits; namespace LanguageExt.Pipes; class PipeTCached where M : MonadIO { public static readonly PipeT unitP = PipeT.pure(default); } ================================================ FILE: LanguageExt.Streaming/Pipes/PipeT/PipeT.DSL.cs ================================================ using System; using System.Threading.Tasks; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt.Pipes; record PipeTEmpty : PipeT where M : MonadIO, MonoidK { public static readonly PipeT Default = new PipeTEmpty(); public override PipeT Map(Func f) => PipeTEmpty.Default; public override PipeT MapM(Func, K> f) => PipeTEmpty.Default; public override PipeT ApplyBack(PipeT> ff) => PipeTEmpty.Default; public override PipeT Action(PipeT fb) => PipeTEmpty.Default; public override PipeT Bind(Func> f) => PipeTEmpty.Default; internal override PipeT ReplaceAwait(Func> producer) => PipeT.empty(); internal override PipeT ReplaceYield(Func> consumer) => PipeT.empty(); internal override PipeT PairEachAwaitWithYield(Func> producer) => PipeT.empty(); internal override PipeT PairEachYieldWithAwait(Func> consumer) => PipeT.empty(); internal override K Run() => M.Empty(); } record PipeTPure(A Value) : PipeT where M : MonadIO { public override PipeT Map(Func f) => PipeT.pure(f(Value)); public override PipeT MapM(Func, K> f) => PipeT.liftM(f(M.Pure(Value))); public override PipeT ApplyBack(PipeT> ff) => ff.Map(f => f(Value)); public override PipeT Action(PipeT fb) => fb; public override PipeT Bind(Func> f) => f(Value); internal override PipeT ReplaceAwait(Func> producer) => PipeT.pure(Value); internal override PipeT ReplaceYield(Func> consumer) => PipeT.pure(Value); internal override PipeT PairEachAwaitWithYield(Func> producer) => producer(default).PairEachYieldWithAwait(_ => PipeT.pure(Value)); internal override PipeT PairEachYieldWithAwait(Func> consumer) => PipeT.pure(Value); internal override K Run() => M.Pure(Value); } record PipeTFail(E Value) : PipeT where M : MonadIO, Fallible { public override PipeT Map(Func f) => PipeT.fail(Value); public override PipeT MapM(Func, K> f) => PipeT.fail(Value); public override PipeT ApplyBack(PipeT> ff) => PipeT.fail(Value); public override PipeT Action(PipeT fb) => PipeT.fail(Value); public override PipeT Bind(Func> f) => PipeT.fail(Value); internal override PipeT ReplaceAwait(Func> producer) => PipeT.fail(Value); internal override PipeT ReplaceYield(Func> consumer) => PipeT.fail(Value); internal override PipeT PairEachAwaitWithYield(Func> producer) => PipeT.fail(Value); internal override PipeT PairEachYieldWithAwait(Func> consumer) => PipeT.fail(Value); internal override K Run() => M.Fail(Value); } record PipeTMemo(Memo, A> Acquire) : PipeT where M : MonadIO { public override PipeT Map(Func f) => new PipeTLazy(() => Acquire.Map(f).As()); public override PipeT MapM(Func, K> f) => new PipeTLazy(() => Acquire.Value.As().MapM(f)); public override PipeT ApplyBack(PipeT> ff) => new PipeTLazy(() => Acquire.Value.As().ApplyBack(ff)); public override PipeT Action(PipeT fb) => new PipeTLazy(() => Acquire.Value.As().Action(fb)); public override PipeT Bind(Func> f) => new PipeTLazy(() => Acquire.Value.As().Bind(f)); internal override PipeT ReplaceAwait(Func> producer) => new PipeTLazy(() => Acquire.Value.As().ReplaceAwait(producer)); internal override PipeT ReplaceYield(Func> consumer) => new PipeTLazy(() => Acquire.Value.As().ReplaceYield(consumer)); internal override PipeT PairEachAwaitWithYield(Func> producer) => new PipeTLazy(() => Acquire.Value.As().PairEachAwaitWithYield(producer)); internal override PipeT PairEachYieldWithAwait(Func> consumer) => new PipeTLazy(() => Acquire.Value.As().PairEachYieldWithAwait(consumer)); internal override K Run() => Acquire.Value.As().Run(); } record PipeTLazy(Func> Acquire) : PipeT where M : MonadIO { public override PipeT Map(Func f) => new PipeTLazy(() => Acquire().Map(f)); public override PipeT MapM(Func, K> f) => new PipeTLazy(() => Acquire().MapM(f)); public override PipeT ApplyBack(PipeT> ff) => new PipeTLazy(() => Acquire().ApplyBack(ff)); public override PipeT Action(PipeT fb) => new PipeTLazy(() => Acquire().Action(fb)); public override PipeT Bind(Func> f) => new PipeTLazy(() => Acquire().Bind(f)); internal override PipeT ReplaceAwait(Func> producer) => new PipeTLazy(() => Acquire().ReplaceAwait(producer)); internal override PipeT ReplaceYield(Func> consumer) => new PipeTLazy(() => Acquire().ReplaceYield(consumer)); internal override PipeT PairEachAwaitWithYield(Func> producer) => new PipeTLazy(() => Acquire().PairEachAwaitWithYield(producer)); internal override PipeT PairEachYieldWithAwait(Func> consumer) => new PipeTLazy(() => Acquire().PairEachYieldWithAwait(consumer)); internal override K Run() => Acquire().Run(); } record PipeTLazyAsync(Func>> Acquire) : PipeT where M : MonadIO { public override PipeT Map(Func f) => new PipeTLazyAsync(() => Acquire().Map(t => t.Map(f))); public override PipeT MapM(Func, K> f) => new PipeTLazyAsync(() => Acquire().Map(t => t.MapM(f))); public override PipeT ApplyBack(PipeT> ff) => new PipeTLazyAsync(() => Acquire().Map(t => t.ApplyBack(ff))); public override PipeT Action(PipeT fb) => new PipeTLazyAsync(() => Acquire().Map(t => t.Action(fb))); public override PipeT Bind(Func> f) => new PipeTLazyAsync(() => Acquire().Map(t => t.Bind(f))); internal override PipeT ReplaceAwait(Func> producer) => new PipeTLazyAsync(() => Acquire().Map(t => t.ReplaceAwait(producer))); internal override PipeT ReplaceYield(Func> consumer) => new PipeTLazyAsync(() => Acquire().Map(t => t.ReplaceYield(consumer))); internal override PipeT PairEachAwaitWithYield(Func> producer) => new PipeTLazyAsync(() => Acquire().Map(t => t.PairEachAwaitWithYield(producer))); internal override PipeT PairEachYieldWithAwait(Func> consumer) => new PipeTLazyAsync(() => Acquire().Map(t => t.PairEachYieldWithAwait(consumer))); internal override K Run() => IO.liftVAsync(Acquire) >> (p => p.Run()); } record PipeTLiftM(K> Value) : PipeT where M : MonadIO { public override PipeT Map(Func f) => new PipeTLiftM(Value.Map(px => px.Map(f))); public override PipeT MapM(Func, K> f) => new PipeTLiftM(Value.Map(px => px.MapM(f))); public override PipeT ApplyBack(PipeT> ff) => new PipeTLiftM(Value.Map(px => px.ApplyBack(ff))); public override PipeT Action(PipeT fb) => new PipeTLiftM(Value.Map(px => px.Action(fb))); public override PipeT Bind(Func> f) => new PipeTLiftM(Value.Map(px => px.Bind(f))); internal override PipeT ReplaceAwait(Func> producer) => new PipeTLiftM(Value.Map(px => px.ReplaceAwait(producer))); internal override PipeT ReplaceYield(Func> consumer) => new PipeTLiftM(Value.Map(px => px.ReplaceYield(consumer))); internal override PipeT PairEachAwaitWithYield(Func> producer) => new PipeTLiftM(Value.Map(px => px.PairEachAwaitWithYield(producer))); internal override PipeT PairEachYieldWithAwait(Func> consumer) => new PipeTLiftM(Value.Map(px => px.PairEachYieldWithAwait(consumer))); internal override K Run() => Value.Bind(p => p.Run()); } record PipeTYield(OUT Value, Func> Next) : PipeT where M : MonadIO { public override PipeT Map(Func f) => new PipeTYield(Value, _ => Next(default).Map(f)); public override PipeT MapM(Func, K> f) => new PipeTYield(Value, _ => Next(default).MapM(f)); public override PipeT ApplyBack(PipeT> ff) => new PipeTYield(Value, _ => Next(default).ApplyBack(ff)); public override PipeT Action(PipeT fb) => new PipeTYield(Value, _ => Next(default).Action(fb)); public override PipeT Bind(Func> f) => new PipeTYield(Value, _ => Next(default).Bind(f)); internal override PipeT ReplaceAwait(Func> producer) => new PipeTYield(Value, _ => Next(default).ReplaceAwait(producer)); internal override PipeT ReplaceYield(Func> consumer) => consumer(Value).Bind(_ => Next(default).ReplaceYield(consumer)); internal override PipeT PairEachAwaitWithYield(Func> producer) => new PipeTYield(Value, _ => Next(default).PairEachAwaitWithYield(producer)); internal override PipeT PairEachYieldWithAwait(Func> consumer) => consumer(Value).PairEachAwaitWithYield(_ => Next(default)); internal override K Run() => throw new InvalidOperationException("closed"); } record PipeTAwait(Func> Await) : PipeT where M : MonadIO { public override PipeT Map(Func f) => new PipeTAwait(x => Await(x).Map(f)); public override PipeT MapM(Func, K> f) => new PipeTAwait(x => Await(x).MapM(f)); public override PipeT ApplyBack(PipeT> ff) => new PipeTAwait(x => Await(x).ApplyBack(ff)); public override PipeT Action(PipeT fb) => new PipeTAwait(x => Await(x).Action(fb)); public override PipeT Bind(Func> f) => new PipeTAwait(x => Await(x).Bind(f)); internal override PipeT ReplaceAwait(Func> producer) => producer().Bind(x => Await(x).ReplaceAwait(producer)); internal override PipeT ReplaceYield(Func> consumer) => new PipeTAwait(x => Await(x).ReplaceYield(consumer)); internal override PipeT PairEachAwaitWithYield(Func> producer) => producer(default).PairEachYieldWithAwait(Await); internal override PipeT PairEachYieldWithAwait(Func> consumer) => new PipeTAwait(x => Await(x).PairEachYieldWithAwait(consumer)); internal override K Run() => throw new InvalidOperationException("closed"); } record PipeTYieldAll(IterableNE> Yields, Func> Next) : PipeT where M : MonadIO { public override PipeT Map(Func f) => new PipeTYieldAll(Yields, x => Next(x).Map(f)); public override PipeT MapM(Func, K> f) => new PipeTYieldAll(Yields, x => Next(x).MapM(f)); public override PipeT ApplyBack(PipeT> ff) => new PipeTYieldAll(Yields, x => Next(x).ApplyBack(ff)); public override PipeT Action(PipeT fb) => new PipeTYieldAll(Yields, x => Next(x).Action(fb)); public override PipeT Bind(Func> f) => new PipeTYieldAll(Yields, x => Next(x).Bind(f)); internal override PipeT ReplaceAwait(Func> producer) => new PipeTYieldAll(Yields.Select(x => x.ReplaceAwait(producer)), x => Next(x).ReplaceAwait(producer)); internal override PipeT ReplaceYield(Func> consumer) => new PipeTYieldAll(Yields.Select(x => x.ReplaceYield(consumer)), x => Next(x).ReplaceYield(consumer)); internal override PipeT PairEachAwaitWithYield(Func> producer) => new PipeTYieldAll( Yields.Select(x => x.PairEachAwaitWithYield(_ => producer(default).Map(_ => Unit.Default))), x => Next(x).PairEachAwaitWithYield(producer)); internal override PipeT PairEachYieldWithAwait(Func> consumer) => new PipeTYieldAll( Yields.Select(x => x.PairEachYieldWithAwait(o => consumer(o).Map(_ => Unit.Default))), x => Next(x).PairEachYieldWithAwait(consumer)); internal override K Run() => Yields.Map(y => y.Kind()) .Actions() .Bind(_ => Next(default)) .As() .Run(); } record PipeTYieldAllSource(Source Yields, Func> F, Func> Next) : PipeT where M : MonadIO { public override PipeT Map(Func f) => new PipeTYieldAllSource(Yields, F, x => Next(x).Map(f)); public override PipeT MapM(Func, K> f) => new PipeTYieldAllSource(Yields, F, x => Next(x).MapM(f)); public override PipeT ApplyBack(PipeT> ff) => new PipeTYieldAllSource(Yields, F, x => Next(x).ApplyBack(ff)); public override PipeT Action(PipeT fb) => new PipeTYieldAllSource(Yields, F, x => Next(x).Action(fb)); public override PipeT Bind(Func> f) => new PipeTYieldAllSource(Yields, F, x => Next(x).Bind(f)); internal override PipeT ReplaceAwait(Func> producer) => new PipeTYieldAllSource(Yields, x => F(x).ReplaceAwait(producer), x => Next(x).ReplaceAwait(producer)); internal override PipeT ReplaceYield(Func> consumer) => new PipeTYieldAllSource(Yields, x => F(x).ReplaceYield(consumer), x => Next(x).ReplaceYield(consumer)); internal override PipeT PairEachAwaitWithYield(Func> producer) => new PipeTYieldAllSource( Yields, x => F(x).PairEachAwaitWithYield(_ => producer(default).Map(_ => Unit.Default)), x => Next(x).PairEachAwaitWithYield(producer)); internal override PipeT PairEachYieldWithAwait(Func> consumer) => new PipeTYieldAllSource( Yields, x => F(x).PairEachYieldWithAwait(o => consumer(o).Map(_ => Unit.Default)), x => Next(x).PairEachYieldWithAwait(consumer)); internal override K Run() { var comp = Yields.ReduceIO( PipeT.pure(unit), (ms, x) => Reduced.ContinueIO(ms.Bind(_ => F(x)))); var next = comp >> Next(unit) >> lower; return next.Run(); } } record PipeTYieldAllSourceT(SourceT Yields, Func> F, Func> Next) : PipeT where M : MonadIO { public override PipeT Map(Func f) => new PipeTYieldAllSourceT(Yields, F, x => Next(x).Map(f)); public override PipeT MapM(Func, K> f) => new PipeTYieldAllSourceT(Yields, F, x => Next(x).MapM(f)); public override PipeT ApplyBack(PipeT> ff) => new PipeTYieldAllSourceT(Yields, F, x => Next(x).ApplyBack(ff)); public override PipeT Action(PipeT fb) => new PipeTYieldAllSourceT(Yields, F, x => Next(x).Action(fb)); public override PipeT Bind(Func> f) => new PipeTYieldAllSourceT(Yields, F, x => Next(x).Bind(f)); internal override PipeT ReplaceAwait(Func> producer) => new PipeTYieldAllSourceT(Yields, x => F(x).ReplaceAwait(producer), x => Next(x).ReplaceAwait(producer)); internal override PipeT ReplaceYield(Func> consumer) => new PipeTYieldAllSourceT(Yields, x => F(x).ReplaceYield(consumer), x => Next(x).ReplaceYield(consumer)); internal override PipeT PairEachAwaitWithYield(Func> producer) => new PipeTYieldAllSourceT( Yields, x => F(x).PairEachAwaitWithYield(_ => producer(default).Map(_ => Unit.Default)), x => Next(x).PairEachAwaitWithYield(producer)); internal override PipeT PairEachYieldWithAwait(Func> consumer) => new PipeTYieldAllSourceT( Yields, x => F(x).PairEachYieldWithAwait(o => consumer(o).Map(_ => Unit.Default)), x => Next(x).PairEachYieldWithAwait(consumer)); internal override K Run() { var comp = Yields.ReduceM(PipeT.pure(unit), (ms, x) => M.Pure(Reduced.Continue(ms.Bind(_ => F(x))))) .Bind(pipe => pipe.Bind(x => Next(x))); return comp.Run(); } } ================================================ FILE: LanguageExt.Streaming/Pipes/PipeT/PipeT.Extensions.cs ================================================ using System; using System.Diagnostics.Contracts; using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt.Pipes; public static partial class PipeTExtensions { /// /// Downcast /// [Pure] public static PipeT As(this K, A> ma) where M : MonadIO => (PipeT)ma; /// /// Convert to the `Eff` version of `Pipe` /// [Pure] public static Pipe ToEff(this K>, A> ma) => ma.As(); /// /// Monad bind /// [Pure] public static PipeT SelectMany( this K ma, Func> f, Func g) where M : MonadIO => PipeT.liftM(ma).SelectMany(f, g); /// /// Monad bind /// [Pure] public static PipeT SelectMany( this IO ma, Func> f, Func g) where M : MonadIO => PipeT.liftIO(ma).SelectMany(f, g); /// /// Monad bind /// [Pure] public static PipeT SelectMany( this Pure ma, Func> f, Func g) where M : MonadIO => PipeT.pure(ma.Value).SelectMany(f, g); /// /// Monad bind /// [Pure] public static PipeT SelectMany( this Lift ff, Func> f, Func g) where M : MonadIO => PipeT.lift(ff.Function).SelectMany(f, g); /// /// Monad bind /// [Pure] public static PipeT Bind( this K ma, Func> f) where M : MonadIO => PipeT.liftM(ma).Bind(f); /// /// Monad bind /// [Pure] public static PipeT Bind( this IO ma, Func> f) where M : MonadIO => PipeT.liftIO(ma).Bind(f); /// /// Monad bind /// [Pure] public static PipeT Bind( this Pure ma, Func> f) where M : MonadIO => PipeT.pure(ma.Value).Bind(f); /// /// Monad bind /// [Pure] public static PipeT Bind( this Lift ff, Func> f) where M : MonadIO => PipeT.lift(ff.Function).Bind(f); /// /// Monad bind operation /// [Pure] public static PipeT SelectMany( this K, A> ma, Func> bind, Func project) where M : MonadIO, Fallible => ma.Bind(a => bind(a) switch { { Flag: true } => PipeT.pure(project(a, default)), var guard => PipeT.fail(guard.OnFalse()) }).As(); /// /// Monad bind operation /// [Pure] public static PipeT SelectMany( this Guard ma, Func, B>> bind, Func project) where M : MonadIO, Fallible => ma switch { { Flag: true } => bind(default).Map(b => project(default, b)).As(), var guard => PipeT.fail(guard.OnFalse()) }; [Pure] public static PipeT MapIO( this K, A> ma, Func, IO> f) where M : MonadUnliftIO => ma.As().MapM(ma => M.MapIOMaybe(ma, f)); } ================================================ FILE: LanguageExt.Streaming/Pipes/PipeT/PipeT.Module.cs ================================================ using System; using System.Collections.Generic; using System.Threading.Tasks; using LanguageExt.Common; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt.Pipes; /// /// `PipeT` streaming producer monad-transformer /// public static class PipeT { /// /// Yield a value downstream /// /// Stream value to consume /// Stream value to produce /// Lifted monad type /// public static PipeT yield(OUT value) where M : MonadIO => new PipeTYield(value, _ => pure(default)); /// /// Yield all values downstream /// /// Stream value to consume /// Stream value to produce /// Lifted monad type /// public static PipeT yieldAll(IterableNE values) where M : MonadIO => new PipeTYieldAll(values.Select(yield), pure); /// /// Yield all values downstream /// /// Stream value to consume /// Stream value to produce /// Lifted monad type /// public static PipeT yieldAll(Iterable values) where M : MonadIO => liftIO>>(IterableNE.createRange(values).Map(Some)) >> (opt => opt switch { { IsSome: true, Case: IterableNE xs } => yieldAll(xs), _ => pure(unit) }) >> lower; /// /// Yield all values downstream /// /// Stream value to consume /// Stream value to produce /// Lifted monad type /// public static PipeT yieldAll(IEnumerable values) where M : MonadIO => IterableNE.createRange(values) switch { { IsSome: true, Case: IterableNE xs } => yieldAll(xs), _ => pure(unit) }; /// /// Yield all values downstream /// /// Stream value to consume /// Stream value to produce /// Lifted monad type /// public static PipeT yieldAll(IAsyncEnumerable values) where M : MonadIO => from opt in liftIO>>(IterableNE.createRange(values).Map(Some)) from res in opt switch { { IsSome: true, Case: IterableNE xs } => yieldAll(xs), _ => pure(unit) } select res; /// /// Yield all values downstream /// /// Stream value to consume /// Stream value to produce /// Lifted monad type /// public static PipeT yieldAll(Source values) where M : MonadIO => new PipeTYieldAllSource(values, yield, pure); /// /// Yield all values downstream /// /// Stream value to consume /// Stream value to produce /// Lifted monad type /// public static PipeT yieldAll(SourceT values) where M : MonadIO => new PipeTYieldAllSourceT(values, yield, pure); /// /// Evaluate the `M` monad repeatedly, yielding its bound values downstream /// /// Stream value to consume /// Stream value to produce /// Lifted monad type /// public static PipeT yieldRepeat(K ma) where M : MonadIO => new PipeTYieldAll(Units.Select(_ => ma.Bind(yield)), pure); /// /// Evaluate the `IO` monad repeatedly, yielding its bound values downstream /// /// Stream value to consume /// Stream value to produce /// Lifted monad type /// public static PipeT yieldRepeatIO(IO ma) where M : MonadIO => new PipeTYieldAll(Units.Select(_ => ma.Bind(yield).As()), pure); /// /// Await a value from upstream /// /// Lifted monad type /// Stream value to consume /// Stream value to produce /// Pipe public static PipeT awaiting() where M : MonadIO => new PipeTAwait(pure); /// /// Create a pipe that filters out values that return `false` when applied to a predicate function /// /// Predicate function /// Lifted monad type /// Stream value to consume and produce /// Pipe public static PipeT filter(Func f) where M : MonadIO => awaiting().Bind(x => f(x) ? yield(x) : pure(default)); /// /// Create a pipe from a mapping function /// /// Mapping function /// Lifted monad type /// Stream value to consume /// Stream value to produce /// Pipe public static PipeT map(Func f) where M : MonadIO => awaiting().Bind(x => yield(f(x))); /// /// Create a pipe from a mapping function /// /// Mapping function /// Lifted monad type /// Stream value to consume /// Stream value to produce /// Pipe public static PipeT mapM(Func> f) where M : MonadIO => awaiting().Bind(x => liftM(f(x)).Bind(yield)); /// /// Create a pipe that simply returns a bound value without yielding anything /// /// Stream value to consume /// Stream value to produce /// Lifted monad type /// Bound value type /// public static PipeT pure(A value) where M : MonadIO => new PipeTPure(value); /// /// Create a pipe that always fails /// /// Stream value to consume /// Stream value to produce /// Failure type /// Lifted monad type /// Bound value type /// public static PipeT fail(E value) where M : MonadIO, Fallible => new PipeTFail(value); /// /// Create a pipe that always fails /// /// Stream value to consume /// Stream value to produce /// Lifted monad type /// Bound value type /// public static PipeT error(Error value) where M : MonadIO, Fallible => new PipeTFail(value); /// /// Create a pipe that yields nothing at all /// /// Stream value to consume /// Stream value to produce /// Lifted monad type /// Bound value type /// public static PipeT empty() where M : MonadIO, MonoidK => PipeTEmpty.Default; /// /// Create a pipe that simply returns a bound value without yielding anything /// /// Stream value to consume /// Stream value to produce /// Lifted monad type /// Bound value type /// public static PipeT lift(Func f) where M : MonadIO => new PipeTLazy(() => pure(f())); /// /// Create a pipe that simply returns a bound value without yielding anything /// /// Stream value to consume /// Stream value to produce /// Lifted monad type /// Bound value type /// public static PipeT lift(Memo f) where M : MonadIO => new PipeTLazy(() => pure(f.Value)); /// /// Create a lazy pipe /// /// Stream value to consume /// Stream value to produce /// Lifted monad type /// Bound value type /// public static PipeT liftT(Func> f) where M : MonadIO => new PipeTLazy(f); /// /// Create a memoised pipe /// /// Stream value to consume /// Stream value to produce /// Lifted monad type /// Bound value type /// public static PipeT liftT(Memo, A> f) where M : MonadIO => new PipeTMemo(f); /// /// Create an asynchronous lazy pipe /// /// Stream value to consume /// Stream value to produce /// Lifted monad type /// Bound value type /// public static PipeT liftT(Func>> f) where M : MonadIO => new PipeTLazyAsync(f); /// /// Create an asynchronous pipe /// /// Stream value to consume /// Stream value to produce /// Lifted monad type /// Bound value type /// public static PipeT liftT(ValueTask> task) where M : MonadIO => new PipeTLazyAsync(() => task); /// /// Create a pipe that simply returns the bound value of the lifted monad without yielding anything /// /// Stream value to consume /// Stream value to produce /// Lifted monad type /// Bound value type /// public static PipeT liftM(K ma) where M : MonadIO => new PipeTLiftM(ma.Map(pure)); /// /// Create a pipe that simply returns the bound value of the lifted monad without yielding anything /// /// Stream value to consume /// Stream value to produce /// Lifted monad type /// Bound value type /// public static PipeT liftM(ValueTask> ma) where M : MonadIO => new PipeTLazyAsync(() => ma.Map(liftM)); /// /// Create a pipe that simply returns the bound value of the lifted monad without yielding anything /// /// Stream value to consume /// Stream value to produce /// Lifted monad type /// Bound value type /// public static PipeT liftIO(IO ma) where M : MonadIO => liftM(M.LiftIOMaybe(ma)); /// /// Continually repeat the provided operation /// /// Stream value to consume /// Stream value to produce /// Lifted monad type /// Bound value type /// public static PipeT repeat(PipeT ma) where M : MonadIO => ma.Bind(_ => repeat(ma)); /// /// Repeat the provided operation based on the schedule provided /// /// Stream value to consume /// Stream value to produce /// Lifted monad type /// Bound value type /// public static PipeT repeat(Schedule schedule, PipeT ma) where M : MonadIO { return from s in pure>(schedule.Run().GetIterator()) from r in ma from t in go(s, ma, r) select t; static PipeT go(Iterator schedule, PipeT ma, A latest) => schedule.IsEmpty ? pure(latest) : liftIO(IO.yieldFor(schedule.Head)) .Bind(_ => ma.Bind(x => go(schedule.Tail, ma, x))); } /// /// Continually lift and repeat the provided operation /// /// Stream value to consume /// Stream value to produce /// Lifted monad type /// Bound value type /// public static PipeT repeatM(K ma) where M : MonadIO => repeat(liftM(ma)); /// /// Repeat the provided operation based on the schedule provided /// /// Stream value to consume /// Stream value to produce /// Lifted monad type /// Bound value type /// public static PipeT repeatM(Schedule schedule, K ma) where M : MonadIO => repeat(schedule, liftM(ma)); /// /// Fold the given pipe until the `Schedule` completes. /// Once complete, the pipe yields the aggregated value downstream. /// /// Schedule to run each item /// Fold function /// Initial state /// Pipe to fold /// Stream value to consume /// Stream value to produce /// Lifted monad type /// Bound value type /// public static PipeT fold( Schedule Time, Func Fold, OUT Init, PipeT Item) where M : MonadIO { var state = Init; var sch = Time.Run().GetIterator(); return Item.Bind( x => { if (sch.IsEmpty) { sch.Dispose(); sch = Time.Run().GetIterator(); var nstate = state; state = Init; return yield(nstate); } else { state = Fold(state, x); var delay = sch.Head; sch = sch.Tail; return liftIO(IO.yieldFor(delay)); } }); } /// /// Fold the given pipe until the predicate is `true`. Once `true` the pipe yields the /// aggregated value downstream. /// /// Fold function /// Until predicate /// Initial state /// Pipe to fold /// Stream value to consume /// Stream value to produce /// Lifted monad type /// Bound value type /// public static PipeT foldUntil( Func Fold, Func<(OUT State, A Value), bool> Pred, OUT Init, PipeT Item) where M : MonadIO { var state = Init; return Item.Bind( x => { if (Pred((state, x))) { var nstate = state; state = Init; return yield(nstate); } else { state = Fold(state, x); return PipeTCached.unitP; } }); } /// /// Fold the given pipe until the predicate is `true` or the `Schedule` completes. /// Once `true`, or completed, the pipe yields the aggregated value downstream. /// /// Schedule to run each item /// Fold function /// Until predicate /// Initial state /// Pipe to fold /// Stream value to consume /// Stream value to produce /// Lifted monad type /// Bound value type /// public static PipeT foldUntil( Schedule Time, Func Fold, Func<(OUT State, A Value), bool> Pred, OUT Init, PipeT Item) where M : MonadIO { var state = Init; var sch = Time.Run().GetIterator(); return Item.Bind( x => { if (sch.IsEmpty || Pred((state, x))) { sch.Dispose(); sch = Time.Run().GetIterator(); var nstate = state; state = Init; return yield(nstate); } else { state = Fold(state, x); var delay = sch.Head; sch = sch.Tail; return liftIO(IO.yieldFor(delay)); } }); } /// /// Fold the given pipe while the predicate is `true`. Once `false` the pipe yields the /// aggregated value downstream. /// /// Fold function /// Until predicate /// Initial state /// Pipe to fold /// Stream value to consume /// Stream value to produce /// Lifted monad type /// Bound value type /// public static PipeT foldWhile( Func Fold, Func<(OUT State, A Value), bool> Pred, OUT Init, PipeT Item) where M : MonadIO { var state = Init; return Item.Bind( x => { if (Pred((state, x))) { state = Fold(state, x); return PipeTCached.unitP; } else { var nstate = state; state = Init; return yield(nstate); } }); } /// /// Fold the given pipe while the predicate is `true` or the `Schedule` completes. /// Once `false`, or completed, the pipe yields the aggregated value downstream. /// /// Schedule to run each item /// Fold function /// Until predicate /// Initial state /// Pipe to fold /// Stream value to consume /// Stream value to produce /// Lifted monad type /// Bound value type /// public static PipeT foldWhile( Schedule Time, Func Fold, Func<(OUT State, A Value), bool> Pred, OUT Init, PipeT Item) where M : MonadIO { var state = Init; var sch = Time.Run().GetIterator(); return Item.Bind( x => { if (sch.IsEmpty || !Pred((state, x))) { sch.Dispose(); sch = Time.Run().GetIterator(); var nstate = state; state = Init; return yield(nstate); } else { state = Fold(state, x); var delay = sch.Head; sch = sch.Tail; return liftIO(IO.yieldFor(delay)); } }); } /// /// Fold the given pipe until the `Schedule` completes. /// Once complete, the pipe yields the aggregated value downstream. /// /// Schedule to run each item /// Fold function /// Initial state /// Stream value to consume /// Stream value to produce /// Lifted monad type /// public static PipeT fold( Schedule Time, Func Fold, OUT Init) where M : MonadIO => awaiting().Fold(Time, Fold, Init); /// /// Fold the given pipe until the predicate is `true`. Once `true` the pipe yields the /// aggregated value downstream. /// /// Fold function /// Until predicate /// Initial state /// Stream value to consume /// Stream value to produce /// Lifted monad type /// public static PipeT foldUntil( Func Fold, Func<(OUT State, IN Value), bool> Pred, OUT Init) where M : MonadIO => awaiting().FoldUntil(Fold, Pred, Init); /// /// Fold the given pipe until the predicate is `true` or the `Schedule` completes. /// Once `true`, or completed, the pipe yields the aggregated value downstream. /// /// Schedule to run each item /// Fold function /// Until predicate /// Initial state /// Stream value to consume /// Stream value to produce /// Lifted monad type /// public static PipeT foldUntil( Schedule Time, Func Fold, Func<(OUT State, IN Value), bool> Pred, OUT Init) where M : MonadIO => awaiting().FoldUntil(Time, Fold, Pred, Init); /// /// Fold the given pipe while the predicate is `true`. Once `false` the pipe yields the /// aggregated value downstream. /// /// Fold function /// Until predicate /// Initial state /// Stream value to consume /// Stream value to produce /// Lifted monad type /// public static PipeT foldWhile( Func Fold, Func<(OUT State, IN Value), bool> Pred, OUT Init) where M : MonadIO => awaiting().FoldWhile(Fold, Pred, Init); /// /// Fold the given pipe while the predicate is `true` or the `Schedule` completes. /// Once `false`, or completed, the pipe yields the aggregated value downstream. /// /// Schedule to run each item /// Fold function /// Until predicate /// Initial state /// Stream value to consume /// Stream value to produce /// Lifted monad type /// public static PipeT foldWhile( Schedule Time, Func Fold, Func<(OUT State, IN Value), bool> Pred, OUT Init) where M : MonadIO => awaiting().FoldWhile(Time, Fold, Pred, Init); } ================================================ FILE: LanguageExt.Streaming/Pipes/PipeT/PipeT.Monad.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using LanguageExt.Async.Linq; using LanguageExt.Traits; namespace LanguageExt.Pipes; public class PipeT : MonadT, M>, MonadUnliftIO> where M : MonadIO, Monad { static K, B> Monad>.Bind( K, A> ma, Func, B>> f) => ma.As().Bind(x => f(x).As()); static K, B> Monad>.Recur(A value, Func, Next>> f) => Monad.unsafeRecur(value, f); static K, B> Functor>.Map( Func f, K, A> ma) => ma.As().Map(f); static K, A> Applicative>.Pure(A value) => PipeT.pure(value); static K, B> Applicative>.Apply( K, Func> mf, K, A> ma) => ma.As().ApplyBack(mf.As()); static K, B> Applicative>.Apply( K, Func> mf, Memo, A> ma) => new PipeTMemo(ma).ApplyBack(mf.As()); static K, B> Applicative>.Action( K, A> ma, K, B> mb) => PipeT.liftM(ma.As().Run().Action(mb.As().Run())); static K, A> Applicative>.Actions(IterableNE, A>> fas) => PipeT.liftM(fas.Select(fa => fa.As().Run().Kind()).Actions()); static K, A> MonadT, M>.Lift(K ma) => PipeT.liftM(ma); static K, A> MonadIO>.LiftIO(IO ma) => PipeT.liftIO(ma); static K, B> MonadUnliftIO>.MapIO(K, A> ma, Func, IO> f) => ma.As().MapM(m => M.MapIOMaybe(m, f)); static K, IO> MonadUnliftIO>.ToIO(K, A> ma) => ma.As().MapM(M.ToIOMaybe); static K, ForkIO> MonadUnliftIO>.ForkIO( K, A> ma, Option timeout) => MonadT.lift, M, ForkIO>(ma.As().Run().ForkIOMaybe(timeout)); } ================================================ FILE: LanguageExt.Streaming/Pipes/PipeT/PipeT.Operators.cs ================================================ using LanguageExt.Traits; namespace LanguageExt.Pipes; public static partial class PipeTExtensions { extension(K, A>) where M : MonadIO { /// /// Downcast /// public static PipeT operator +(K, A> ma) => (PipeT) ma; /// /// Downcast /// public static PipeT operator >>(K, A> ma, Lower lower) => +ma; } } ================================================ FILE: LanguageExt.Streaming/Pipes/PipeT/PipeT.cs ================================================ using System; using System.Diagnostics.Contracts; using System.Threading; using System.Threading.Tasks; using LanguageExt.Traits; namespace LanguageExt.Pipes; /// /// `PipeT` monad-transformer base-type for Pipes streaming components: /// /// * `ProducerT` is a `PipeT` with the `IN` 'closed off' with `Unit` /// * `ConsumerT` is a `PipeT` with the `OUT` 'closed off' with `Void` /// * `EffectT` is a `PipeT` with the `IN` and `OUT` 'closed off' with `Unit` upstream and `Void` downstream /// /// /// Type of values to be consumed /// Type of values to be produced /// Lifted monad type /// Bound value type public abstract record PipeT : K, A> where M : MonadIO { [Pure] public PipeT Compose(PipeT rhs) => rhs.PairEachAwaitWithYield(_ => this); [Pure] public ConsumerT Compose(ConsumerT rhs) => rhs.Proxy.PairEachAwaitWithYield(_ => this); [Pure] public static PipeT operator | (PipeT lhs, PipeT rhs) => lhs.Compose(rhs); [Pure] public static ProducerT operator | (ProducerT lhs, PipeT rhs) => lhs.Compose(rhs); [Pure] public static ConsumerT operator | (PipeT lhs, ConsumerT rhs) => lhs.Compose(rhs.Proxy); [Pure] public static implicit operator PipeT(Pure rhs) => PipeT.pure(rhs.Value); [Pure] public abstract PipeT Map(Func f); [Pure] public abstract PipeT MapM(Func, K> f); [Pure] public abstract PipeT ApplyBack(PipeT> ff); [Pure] public abstract PipeT Action(PipeT fb); [Pure] public abstract PipeT Bind(Func> f); [Pure] public PipeT Bind(Func> f) => Bind(x => PipeT.liftIO(f(x))); [Pure] public PipeT Bind(Func> f) => Bind(x => PipeT.liftM(f(x))); [Pure] public PipeT Bind(Func> f) => Map(x => f(x).Value); [Pure] public PipeT Bind(Func> f) => Map(x => f(x).Function()); [Pure] public PipeT BindAsync(Func>> f) => Bind(x => PipeT.liftT(f(x))); [Pure] public PipeT Select(Func f) => Map(f); [Pure] public PipeT SelectMany(Func> f, Func g) => Bind(x => f(x).Map(y => g(x, y))); [Pure] public PipeT SelectMany(Func> f, Func g) => Bind(x => f(x).Map(y => g(x, y))); [Pure] public PipeT SelectMany(Func> f, Func g) => Bind(x => f(x).Map(y => g(x, y))); [Pure] public PipeT SelectMany(Func> f, Func g) => Map(x => g(x, f(x).Value)); [Pure] public PipeT SelectMany(Func> f, Func g) => Map(x => g(x, f(x).Function())); [Pure] internal abstract PipeT ReplaceAwait(Func> producer); [Pure] internal abstract PipeT ReplaceYield(Func> consumer); [Pure] internal abstract PipeT PairEachAwaitWithYield(Func> producer); [Pure] internal abstract PipeT PairEachYieldWithAwait(Func> consumer); /// /// Fold the given pipe until the `Schedule` completes. /// Once complete, the pipe yields the aggregated value downstream. /// /// Schedule to run each item /// Fold function /// Initial state /// Stream value to consume /// Stream value to produce /// Lifted monad type /// Bound value type /// public PipeT Fold( Schedule Time, Func Fold, OUT Init) => PipeT.fold(Time, Fold, Init, this); /// /// Fold the given pipe until the predicate is `true`. Once `true` the pipe yields the /// aggregated value downstream. /// /// Fold function /// Until predicate /// Initial state /// Stream value to consume /// Stream value to produce /// Lifted monad type /// Bound value type /// public PipeT FoldUntil( Func Fold, Func<(OUT State, A Value), bool> Pred, OUT Init) => PipeT.foldUntil(Fold, Pred, Init, this); /// /// Fold the given pipe until the predicate is `true` or the `Schedule` completes. /// Once `true`, or completed, the pipe yields the aggregated value downstream. /// /// Schedule to run each item /// Fold function /// Until predicate /// Initial state /// Stream value to consume /// Stream value to produce /// Lifted monad type /// Bound value type /// public PipeT FoldUntil( Schedule Time, Func Fold, Func<(OUT State, A Value), bool> Pred, OUT Init) => PipeT.foldUntil(Time, Fold, Pred, Init, this); /// /// Fold the given pipe while the predicate is `true`. Once `false` the pipe yields the /// aggregated value downstream. /// /// Fold function /// Until predicate /// Initial state /// Stream value to consume /// Stream value to produce /// Lifted monad type /// Bound value type /// public PipeT FoldWhile( Func Fold, Func<(OUT State, A Value), bool> Pred, OUT Init) => PipeT.foldWhile(Fold, Pred, Init, this); /// /// Fold the given pipe while the predicate is `true` or the `Schedule` completes. /// Once `false`, or completed, the pipe yields the aggregated value downstream. /// /// Schedule to run each item /// Fold function /// Until predicate /// Initial state /// Stream value to consume /// Stream value to produce /// Lifted monad type /// Bound value type /// public PipeT FoldWhile( Schedule Time, Func Fold, Func<(OUT State, A Value), bool> Pred, OUT Init) => PipeT.foldWhile(Time, Fold, Pred, Init, this); [Pure] internal abstract K Run(); } ================================================ FILE: LanguageExt.Streaming/Pipes/Producer/Producer.Extensions.cs ================================================ using System; using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt.Pipes; public static partial class ProducerExtensions { /// /// Transformation from `PipeT` to `ProducerT`. /// public static Producer ToProducer(this K>, A> pipe) => new(pipe.As()); /// /// Transformation from `PipeT` to `ProducerT`. /// public static Producer ToProducer(this K, A> pipe) => new(pipe.As().Proxy); /// /// Downcast /// public static Producer As(this K, A> ma) => (Producer)ma; /// /// Monad bind /// public static Producer SelectMany( this K, A> ma, Func> f, Func g) => Producer.liftM(ma).SelectMany(f, g); /// /// Monad bind /// public static Producer SelectMany( this IO ma, Func> f, Func g) => Producer.liftIO(ma).SelectMany(f, g); /// /// Monad bind /// public static Producer SelectMany( this Pure ma, Func> f, Func g) => Producer.pure(ma.Value).SelectMany(f, g); /// /// Monad bind /// public static Producer SelectMany( this Lift ff, Func> f, Func g) => Producer.lift(ff.Function).SelectMany(f, g); /// /// Monad bind /// public static Producer Bind( this K, A> ma, Func> f) => Producer.liftM(ma).Bind(f); /// /// Monad bind /// public static Producer Bind( this IO ma, Func> f) => Producer.liftIO(ma).Bind(f); /// /// Monad bind /// public static Producer Bind( this Pure ma, Func> f) => Producer.pure(ma.Value).Bind(f); /// /// Monad bind /// public static Producer Bind( this Lift ff, Func> f) => Producer.lift(ff.Function).Bind(f); /// /// Monad bind operation /// public static Producer SelectMany( this K, A> ma, Func> bind, Func project) => ma.Bind(a => bind(a) switch { { Flag: true } => Producer.pure(project(a, default)), var guard => Producer.error(guard.OnFalse()) }).As(); /// /// Monad bind operation /// public static Producer SelectMany( this Guard ma, Func, B>> bind, Func project) => ma switch { { Flag: true } => bind(default).Map(b => project(default, b)).As(), var guard => Producer.error(guard.OnFalse()) }; } ================================================ FILE: LanguageExt.Streaming/Pipes/Producer/Producer.Module.cs ================================================ using System; using System.Collections.Generic; using System.Threading.Tasks; using LanguageExt.Common; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt.Pipes; public static class Producer { /// /// Yield a value downstream /// /// Effect runtime type /// Stream value to produce /// public static Producer yield(OUT value) => PipeT.yield, Unit, OUT>(value); /// /// Yield all values downstream /// /// Effect runtime type /// Stream value to produce /// public static Producer yieldAll(IEnumerable values) => PipeT.yieldAll, Unit, OUT>(values); /// /// Yield all values downstream /// /// Effect runtime type /// Stream value to produce /// public static Producer yieldAll(IAsyncEnumerable values) => PipeT.yieldAll, Unit, OUT>(values); /// /// Evaluate the `M` monad repeatedly, yielding its bound values downstream /// /// Effect runtime type /// Stream value to consume /// Stream value to produce /// public static Producer yieldRepeat(K, OUT> ma) => PipeT.yieldRepeat, Unit, OUT>(ma); /// /// Evaluate the `IO` monad repeatedly, yielding its bound values downstream /// /// Effect runtime type /// Stream value to consume /// Stream value to produce /// public static Producer yieldRepeatIO(IO ma) => PipeT.yieldRepeatIO, Unit, OUT>(ma); /// /// Create a producer that simply returns a bound value without yielding anything /// /// Effect runtime type /// Stream value to produce /// Bound value type /// public static Producer pure(A value) => PipeT.pure, A>(value); /// /// Create a producer that always fails /// /// Effect runtime type /// Stream value to produce /// Bound value type /// public static Producer error(Error value) => PipeT.error, A>(value); /// /// Create a producer that yields nothing at all /// /// Effect runtime type /// Stream value to produce /// Bound value type /// public static Producer empty() => PipeT.empty, A>(); /// /// Create a producer that lazily returns a bound value without yielding anything /// /// Effect runtime type /// Stream value to produce /// Bound value type /// public static Producer lift(Func f) => PipeT.lift, A>(f); /// /// Create a producer that simply returns the bound value of the lifted monad without yielding anything /// /// Effect runtime type /// Stream value to produce /// Bound value type /// public static Producer liftM(K, A> ma) => PipeT.liftM, A>(ma); /// /// Create a producer that simply returns the bound value of the lifted monad without yielding anything /// /// Effect runtime type /// Stream value to produce /// Bound value type /// public static Producer liftIO(IO ma) => PipeT.liftIO, A>(ma); /// /// Create a lazy proxy /// /// Stream value to produce /// Bound value type /// public static Producer liftT(Func> f) => PipeT.liftT(() => f().Proxy); /// /// Create an asynchronous lazy proxy /// /// Effect runtime type /// Stream value to produce /// Bound value type /// public static Producer liftT(Func>> f) => PipeT.liftT(() => f().Map(p => p.Proxy)); /// /// Create an asynchronous proxy /// /// Effect runtime type /// Stream value to produce /// Bound value type /// public static Producer liftT(ValueTask> f) => PipeT.liftT(f.Map(p => p.Proxy)); /// /// Continually repeat the provided operation /// /// Effect runtime type /// Stream value to produce /// Bound value type /// public static Producer repeat(Producer ma) => PipeT.repeat(ma.Proxy); /// /// Repeat the provided operation based on the schedule provided /// /// Effect runtime type /// Stream value to produce /// Bound value type /// public static Producer repeat(Schedule schedule, Producer ma) => PipeT.repeat(schedule, ma.Proxy); /// /// Continually lift and repeat the provided operation /// /// Stream value to produce /// Bound value type /// public static Producer repeatM(K, A> ma) => PipeT.repeatM, A>(ma); /// /// Repeat the provided operation based on the schedule provided /// /// Effect runtime type /// Stream value to produce /// Bound value type /// public static Producer repeatM(Schedule schedule, K, A> ma) => PipeT.repeatM, A>(schedule, ma); /// /// Fold the given pipe until the `Schedule` completes. /// Once complete, the pipe yields the aggregated value downstream. /// /// Schedule to run each item /// Fold function /// Initial state /// Pipe to fold /// Effect runtime type /// Stream value to produce /// Bound value type /// public static Producer fold( Schedule Time, Func Fold, OUT Init, Producer Item) => PipeT.fold(Time, Fold, Init, Item.Proxy); /// /// Fold the given pipe until the predicate is `true`. Once `true` the pipe yields the /// aggregated value downstream. /// /// Fold function /// Until predicate /// Initial state /// Pipe to fold /// Effect runtime type /// Stream value to produce /// Bound value type /// public static Producer foldUntil( Func Fold, Func<(OUT State, A Value), bool> Pred, OUT Init, Producer Item) => PipeT.foldUntil(Fold, Pred, Init, Item.Proxy); /// /// Fold the given pipe until the predicate is `true` or the `Schedule` completes. /// Once `true`, or completed, the pipe yields the aggregated value downstream. /// /// Schedule to run each item /// Fold function /// Until predicate /// Initial state /// Pipe to fold /// Effect runtime type /// Stream value to produce /// Bound value type /// public static Producer foldUntil( Schedule Time, Func Fold, Func<(OUT State, A Value), bool> Pred, OUT Init, Producer Item) => PipeT.foldUntil(Time, Fold, Pred, Init, Item.Proxy); /// /// Fold the given pipe while the predicate is `true`. Once `false` the pipe yields the /// aggregated value downstream. /// /// Fold function /// Until predicate /// Initial state /// Pipe to fold /// Effect runtime type /// Stream value to produce /// Bound value type /// public static Producer foldWhile( Func Fold, Func<(OUT State, A Value), bool> Pred, OUT Init, Producer Item) => PipeT.foldWhile(Fold, Pred, Init, Item.Proxy); /// /// Fold the given pipe while the predicate is `true` or the `Schedule` completes. /// Once `false`, or completed, the pipe yields the aggregated value downstream. /// /// Schedule to run each item /// Fold function /// Until predicate /// Initial state /// Pipe to fold /// Effect runtime type /// Stream value to produce /// Bound value type /// public static Producer foldWhile( Schedule Time, Func Fold, Func<(OUT State, A Value), bool> Pred, OUT Init, Producer Item) => PipeT.foldWhile(Time, Fold, Pred, Init, Item.Proxy); /// /// Merge multiple producers /// /// Producers to merge /// Buffer settings /// Effect runtime type /// Stream value to produce /// Merged producer public static Producer merge( params Producer[] producers) => merge(toSeq(producers)); /// /// Merge multiple producers /// /// Producers to merge /// Buffer settings /// Effect runtime type /// Stream value to produce /// Merged producer public static Producer merge( Seq> producers, Buffer? settings = null) => ProducerT.merge(producers.Map(p => new ProducerT, Unit>(p.Proxy)), settings); } ================================================ FILE: LanguageExt.Streaming/Pipes/Producer/Producer.Monad.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using LanguageExt.Async.Linq; using LanguageExt.Traits; namespace LanguageExt.Pipes; public class Producer : MonadT, Eff>, MonadUnliftIO> { static K, B> Monad>.Bind( K, A> ma, Func, B>> f) => ma.As().Bind(x => f(x).As()); static K, B> Monad>.Recur(A value, Func, Next>> f) => Monad.unsafeRecur(value, f); static K, B> Functor>.Map( Func f, K, A> ma) => ma.As().Map(f); static K, A> Applicative>.Pure(A value) => Producer.pure(value); static K, B> Applicative>.Apply( K, Func> mf, K, A> ma) => ma.As().ApplyBack(mf.As()); static K, B> Applicative>.Apply( K, Func> mf, Memo, A> ma) => new PipeTMemo, A>(ma.Lower().Map(ma => ma.As().Proxy.Kind()).Lift()) .ApplyBack(mf.As().Proxy) .ToProducer(); static K, A> MonadT, Eff>.Lift(K, A> ma) => Producer.liftM(ma); static K, A> MonadIO>.LiftIO(IO ma) => Producer.liftIO(ma); static K, B> MonadUnliftIO>.MapIO(K, A> ma, Func, IO> f) => ma.As().MapIO(f); static K, IO> MonadUnliftIO>.ToIO(K, A> ma) => ma.MapIO(IO.pure); static K, B> Applicative>.Action( K, A> ma, K, B> mb) => Producer.liftM(ma.As().Run().Action(mb.As().Run())); static K, A> Applicative>.Actions( IterableNE, A>> fas) => fas.Select(fa => fa.As().Proxy.Kind()) .Actions() .ToProducer(); } ================================================ FILE: LanguageExt.Streaming/Pipes/Producer/Producer.Operators.cs ================================================ using LanguageExt.Traits; namespace LanguageExt.Pipes; public static partial class ProducerExtensions { extension(K, A>) { /// /// Downcast /// public static Producer operator +(K, A> ma) => (Producer) ma; /// /// Downcast /// public static Producer operator >>(K, A> ma, Lower lower) => +ma; } } ================================================ FILE: LanguageExt.Streaming/Pipes/Producer/Producer.cs ================================================ using System; using System.Diagnostics.Contracts; using LanguageExt.Traits; namespace LanguageExt.Pipes; /// /// `Producer` streaming producer monad-transformer instance /// public readonly record struct Producer(PipeT, A> Proxy) : K, A> { [Pure] public Producer Compose(PipeT, A> rhs) => Proxy.Compose(rhs).ToProducer(); [Pure] public Producer Compose(Pipe rhs) => Proxy.Compose(rhs.Proxy); [Pure] public Effect Compose(ConsumerT, A> rhs) => Proxy.Compose(rhs.Proxy).ToEffect(); [Pure] public Effect Compose(Consumer rhs) => Proxy.Compose(rhs.Proxy).ToEffect(); [Pure] public static Effect operator | (Producer lhs, ConsumerT, A> rhs) => lhs.Proxy.Compose(rhs.Proxy).ToEffect(); [Pure] public static Effect operator | (Producer lhs, Consumer rhs) => lhs.Proxy.Compose(rhs.Proxy).ToEffect(); [Pure] public Producer Map(Func f) => Proxy.Map(f); [Pure] public Producer MapM(Func, Eff> f) => Proxy.MapM(mx => f(mx.As())); [Pure] public Producer MapIO(Func, IO> f) => Proxy.MapIO(f); [Pure] public Producer ApplyBack(Producer> ff) => Proxy.ApplyBack(ff.Proxy); [Pure] public Producer Action(Producer fb) => Proxy.Action(fb.Proxy); [Pure] public Producer Bind(Func> f) => Proxy.Bind(x => f(x).Proxy); [Pure] public Producer Bind(Func, B>> f) => Proxy.Bind(f); [Pure] public Producer Bind(Func> f) => Proxy.Bind(f); [Pure] public Producer Bind(Func> f) => Proxy.Bind(f); [Pure] public Producer Bind(Func> f) => Proxy.Bind(f); [Pure] public Producer Select(Func f) => Map(f); [Pure] public Producer SelectMany(Func> f, Func g) => Proxy.SelectMany(x => f(x).Proxy, g); [Pure] public Producer SelectMany(Func, B>> f, Func g) => Proxy.SelectMany(f, g); [Pure] public Producer SelectMany(Func> f, Func g) => Proxy.SelectMany(f, g); [Pure] public Producer SelectMany(Func> f, Func g) => Proxy.SelectMany(f, g); [Pure] public Producer SelectMany(Func> f, Func g) => Proxy.SelectMany(f, g); /// /// Fold the given pipe until the `Schedule` completes. /// Once complete, the pipe yields the aggregated value downstream. /// /// Schedule to run each item /// Fold function /// Initial state /// Stream value to produce /// Lifted monad type /// Bound value type /// public Producer Fold( Schedule Time, Func Fold, OUT Init) => PipeT.fold(Time, Fold, Init, Proxy); /// /// Fold the given pipe until the predicate is `true`. Once `true` the pipe yields the /// aggregated value downstream. /// /// Fold function /// Until predicate /// Initial state /// Stream value to produce /// Lifted monad type /// Bound value type /// public Producer FoldUntil( Func Fold, Func<(OUT State, A Value), bool> Pred, OUT Init) => PipeT.foldUntil(Fold, Pred, Init, Proxy); /// /// Fold the given pipe until the predicate is `true` or the `Schedule` completes. /// Once `true`, or completed, the pipe yields the aggregated value downstream. /// /// Schedule to run each item /// Fold function /// Until predicate /// Initial state /// Stream value to produce /// Lifted monad type /// Bound value type /// public Producer FoldUntil( Schedule Time, Func Fold, Func<(OUT State, A Value), bool> Pred, OUT Init) => PipeT.foldUntil(Time, Fold, Pred, Init, Proxy); /// /// Fold the given pipe while the predicate is `true`. Once `false` the pipe yields the /// aggregated value downstream. /// /// Fold function /// Until predicate /// Initial state /// Stream value to produce /// Lifted monad type /// Bound value type /// public Producer FoldWhile( Func Fold, Func<(OUT State, A Value), bool> Pred, OUT Init) => PipeT.foldWhile(Fold, Pred, Init, Proxy); /// /// Fold the given pipe while the predicate is `true` or the `Schedule` completes. /// Once `false`, or completed, the pipe yields the aggregated value downstream. /// /// Schedule to run each item /// Fold function /// Until predicate /// Initial state /// Stream value to produce /// Lifted monad type /// Bound value type /// public Producer FoldWhile( Schedule Time, Func Fold, Func<(OUT State, A Value), bool> Pred, OUT Init) => PipeT.foldWhile(Time, Fold, Pred, Init, Proxy); [Pure] public static implicit operator Producer(PipeT, A> pipe) => pipe.ToProducer(); [Pure] public static implicit operator Producer(Pipe pipe) => pipe.ToProducer(); [Pure] public static implicit operator Producer(ProducerT, A> pipe) => pipe.Proxy.ToProducer(); [Pure] public static implicit operator Producer(Pure rhs) => Producer.pure(rhs.Value); [Pure] internal Eff Run() => Proxy.Run().As(); } ================================================ FILE: LanguageExt.Streaming/Pipes/ProducerT/ProducerT.Extensions.cs ================================================ using System; using System.Diagnostics.Contracts; using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt.Pipes; public static partial class ProducerTExtensions { /// /// Transformation from `PipeT` to `ProducerT`. /// [Pure] public static ProducerT ToProducer(this K, A> pipe) where M : MonadIO => new(pipe.As()); /// /// Downcast /// [Pure] public static ProducerT As(this K, A> ma) where M : MonadIO => (ProducerT)ma; /// /// Convert to the `Eff` version of `Producer` /// [Pure] public static Producer ToEff(this K>, A> ma) => ma.As(); /// /// Monad bind /// [Pure] public static ProducerT SelectMany( this K ma, Func> f, Func g) where M : MonadIO => ProducerT.liftM(ma).SelectMany(f, g); /// /// Monad bind /// [Pure] public static ProducerT SelectMany( this IO ma, Func> f, Func g) where M : MonadIO => ProducerT.liftIO(ma).SelectMany(f, g); /// /// Monad bind /// [Pure] public static ProducerT SelectMany( this Pure ma, Func> f, Func g) where M : MonadIO => ProducerT.pure(ma.Value).SelectMany(f, g); /// /// Monad bind /// [Pure] public static ProducerT SelectMany( this Lift ff, Func> f, Func g) where M : MonadIO => ProducerT.lift(ff.Function).SelectMany(f, g); /// /// Monad bind /// [Pure] public static ProducerT Bind( this K ma, Func> f) where M : MonadIO => ProducerT.liftM(ma).Bind(f); /// /// Monad bind /// [Pure] public static ProducerT Bind( this IO ma, Func> f) where M : MonadIO => ProducerT.liftIO(ma).Bind(f); /// /// Monad bind /// [Pure] public static ProducerT Bind( this Pure ma, Func> f) where M : MonadIO => ProducerT.pure(ma.Value).Bind(f); /// /// Monad bind /// [Pure] public static ProducerT Bind( this Lift ff, Func> f) where M : MonadIO => ProducerT.lift(ff.Function).Bind(f); /// /// Monad bind operation /// [Pure] public static ProducerT SelectMany( this K, A> ma, Func> bind, Func project) where M : MonadIO, Fallible => ma.Bind(a => bind(a) switch { { Flag: true } => ProducerT.pure(project(a, default)), var guard => ProducerT.fail(guard.OnFalse()) }).As(); /// /// Monad bind operation /// [Pure] public static ProducerT SelectMany( this Guard ma, Func, B>> bind, Func project) where M : MonadIO, Fallible => ma switch { { Flag: true } => bind(default).Map(b => project(default, b)).As(), var guard => ProducerT.fail(guard.OnFalse()) }; [Pure] public static ProducerT MapIO( this K, A> ma, Func, IO> f) where M : MonadUnliftIO => ma.As().MapM(m => M.MapIOMaybe(m, f)); } ================================================ FILE: LanguageExt.Streaming/Pipes/ProducerT/ProducerT.Module.cs ================================================ using System; using System.Collections.Generic; using System.Threading.Tasks; using LanguageExt.Common; using LanguageExt.Pipes; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt.Pipes; public static class ProducerT { /// /// Yield a value downstream /// /// Stream value to produce /// Lifted monad type /// public static ProducerT yield(OUT value) where M : MonadIO => PipeT.yield(value); /// /// Yield all values downstream /// /// Stream value to produce /// Lifted monad type /// public static ProducerT yieldAll(IEnumerable values) where M : MonadIO => PipeT.yieldAll(values); /// /// Yield all values downstream /// /// Stream value to produce /// Lifted monad type /// public static ProducerT yieldAll(IAsyncEnumerable values) where M : MonadIO => PipeT.yieldAll(values); /// /// Yield all values downstream /// /// Stream value to produce /// Lifted monad type /// public static ProducerT yieldAll(Source values) where M : MonadIO => PipeT.yieldAll(values); /// /// Yield all values downstream /// /// Stream value to produce /// Lifted monad type /// public static ProducerT yieldAll(SourceT values) where M : MonadIO => PipeT.yieldAll(values); /// /// Evaluate the `M` monad repeatedly, yielding its bound values downstream /// /// Stream value to consume /// Stream value to produce /// Lifted monad type /// public static ProducerT yieldRepeat(K ma) where M : MonadIO => PipeT.yieldRepeat(ma); /// /// Evaluate the `IO` monad repeatedly, yielding its bound values downstream /// /// Stream value to consume /// Stream value to produce /// Lifted monad type /// public static ProducerT yieldRepeatIO(IO ma) where M : MonadIO => PipeT.yieldRepeatIO(ma); /// /// Create a producer that simply returns a bound value without yielding anything /// /// Stream value to produce /// Lifted monad type /// Bound value type /// public static ProducerT pure(A value) where M : MonadIO => PipeT.pure(value); /// /// Create a producer that always fails /// /// Stream value to produce /// Failure type /// Lifted monad type /// Bound value type /// public static ProducerT fail(E value) where M : MonadIO, Fallible => PipeT.fail(value); /// /// Create a producer that always fails /// /// Stream value to produce /// Lifted monad type /// Bound value type /// public static ProducerT error(Error value) where M : MonadIO, Fallible => PipeT.error(value); /// /// Create a producer that yields nothing at all /// /// Stream value to produce /// Lifted monad type /// Bound value type /// public static ProducerT empty() where M : MonadIO, MonoidK => PipeT.empty(); /// /// Create a producer that lazily returns a bound value without yielding anything /// /// Stream value to produce /// Lifted monad type /// Bound value type /// public static ProducerT lift(Func f) where M : MonadIO => PipeT.lift(f); /// /// Create a producer that simply returns the bound value of the lifted monad without yielding anything /// /// Stream value to produce /// Lifted monad type /// Bound value type /// public static ProducerT liftM(K ma) where M : MonadIO => PipeT.liftM(ma); /// /// Create a producer that simply returns the bound value of the lifted monad without yielding anything /// /// Stream value to produce /// Lifted monad type /// Bound value type /// public static ProducerT liftIO(IO ma) where M : MonadIO => PipeT.liftIO(ma); /// /// Create a lazy proxy /// /// Stream value to produce /// Lifted monad type /// Bound value type /// public static ProducerT liftT(Func> f) where M : MonadIO => PipeT.liftT(() => f().Proxy); /// /// Create an asynchronous lazy proxy /// /// Stream value to produce /// Lifted monad type /// Bound value type /// public static ProducerT liftT(Func>> f) where M : MonadIO => PipeT.liftT(() => f().Map(p => p.Proxy)); /// /// Create an asynchronous proxy /// /// Stream value to produce /// Lifted monad type /// Bound value type /// public static ProducerT liftT(ValueTask> f) where M : MonadIO => PipeT.liftT(f.Map(p => p.Proxy)); /// /// Continually repeat the provided operation /// /// Stream value to produce /// Lifted monad type /// Bound value type /// public static ProducerT repeat(ProducerT ma) where M : MonadIO => PipeT.repeat(ma.Proxy).ToProducer(); /// /// Repeat the provided operation based on the schedule provided /// /// Stream value to produce /// Lifted monad type /// Bound value type /// public static ProducerT repeat(Schedule schedule, ProducerT ma) where M : MonadIO => PipeT.repeat(schedule, ma.Proxy).ToProducer(); /// /// Continually lift and repeat the provided operation /// /// Stream value to produce /// Lifted monad type /// Bound value type /// public static ProducerT repeatM(K ma) where M : MonadIO => PipeT.repeatM(ma).ToProducer(); /// /// Repeat the provided operation based on the schedule provided /// /// Stream value to produce /// Lifted monad type /// Bound value type /// public static ProducerT repeatM(Schedule schedule, K ma) where M : MonadIO => PipeT.repeatM(schedule, ma).ToProducer(); /// /// Fold the given pipe until the `Schedule` completes. /// Once complete, the pipe yields the aggregated value downstream. /// /// Schedule to run each item /// Fold function /// Initial state /// Pipe to fold /// Stream value to produce /// Lifted monad type /// Bound value type /// public static ProducerT fold( Schedule Time, Func Fold, OUT Init, ProducerT Item) where M : MonadIO => PipeT.fold(Time, Fold, Init, Item.Proxy); /// /// Fold the given pipe until the predicate is `true`. Once `true` the pipe yields the /// aggregated value downstream. /// /// Fold function /// Until predicate /// Initial state /// Pipe to fold /// Stream value to produce /// Lifted monad type /// Bound value type /// public static ProducerT foldUntil( Func Fold, Func<(OUT State, A Value), bool> Pred, OUT Init, ProducerT Item) where M : MonadIO => PipeT.foldUntil(Fold, Pred, Init, Item.Proxy); /// /// Fold the given pipe until the predicate is `true` or the `Schedule` completes. /// Once `true`, or completed, the pipe yields the aggregated value downstream. /// /// Schedule to run each item /// Fold function /// Until predicate /// Initial state /// Pipe to fold /// Stream value to produce /// Lifted monad type /// Bound value type /// public static ProducerT foldUntil( Schedule Time, Func Fold, Func<(OUT State, A Value), bool> Pred, OUT Init, ProducerT Item) where M : MonadIO => PipeT.foldUntil(Time, Fold, Pred, Init, Item.Proxy); /// /// Fold the given pipe while the predicate is `true`. Once `false` the pipe yields the /// aggregated value downstream. /// /// Fold function /// Until predicate /// Initial state /// Pipe to fold /// Stream value to produce /// Lifted monad type /// Bound value type /// public static ProducerT foldWhile( Func Fold, Func<(OUT State, A Value), bool> Pred, OUT Init, ProducerT Item) where M : MonadIO => PipeT.foldWhile(Fold, Pred, Init, Item.Proxy); /// /// Fold the given pipe while the predicate is `true` or the `Schedule` completes. /// Once `false`, or completed, the pipe yields the aggregated value downstream. /// /// Schedule to run each item /// Fold function /// Until predicate /// Initial state /// Pipe to fold /// Stream value to produce /// Lifted monad type /// Bound value type /// public static ProducerT foldWhile( Schedule Time, Func Fold, Func<(OUT State, A Value), bool> Pred, OUT Init, ProducerT Item) where M : MonadIO => PipeT.foldWhile(Time, Fold, Pred, Init, Item.Proxy); /// /// Merge multiple producers /// /// Producers to merge /// Buffer settings /// Stream value to produce /// Lifted monad type /// Merged producer public static ProducerT merge( params ProducerT[] producers) where M : MonadUnliftIO => merge(toSeq(producers)); /// /// Merge multiple producers /// /// Producers to merge /// Buffer settings /// Stream value to produce /// Lifted monad type /// Merged producer public static ProducerT merge( Seq> producers, Buffer? settings = null) where M : MonadUnliftIO { if (producers.Count == 0) return pure(default); return from conduit in Pure(Conduit.make(settings ?? Buffer.Unbounded)) from signal in Signal.countdown(producers.Count) from forks in forkEffects(producers, signal, conduit) from _ in conduit.ToProducerT() from x in forks.Traverse(f => f.Cancel).As() select unit; } static K>> forkEffects( Seq> producers, CountdownSignal signal, Conduit conduit) where M : MonadUnliftIO => producers.Map(p => (p | conduit.ToConsumerT()).Run()) .Traverse(ma => ma.Bind(_ => trigger(signal, conduit)) .ForkIO()); static K trigger(CountdownSignal signal, Conduit conduit) where M : MonadIO => signal.Trigger().Bind(f => when(f, conduit.Complete()).As()); } ================================================ FILE: LanguageExt.Streaming/Pipes/ProducerT/ProducerT.Monad.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using LanguageExt.Async.Linq; using LanguageExt.Traits; namespace LanguageExt.Pipes; public class ProducerT : MonadT, M>, MonadUnliftIO> where M : MonadIO { static K, B> Monad>.Bind( K, A> ma, Func, B>> f) => ma.As().Bind(x => f(x).As()); static K, B> Monad>.Recur(A value, Func, Next>> f) => Monad.unsafeRecur(value, f); static K, B> Functor>.Map( Func f, K, A> ma) => ma.As().Map(f); static K, A> Applicative>.Pure(A value) => ProducerT.pure(value); static K, B> Applicative>.Apply( K, Func> mf, K, A> ma) => ma.As().ApplyBack(mf.As()); static K, B> Applicative>.Apply( K, Func> mf, Memo, A> ma) => new PipeTMemo(ma.Lower().Map(ma => ma.As().Proxy.Kind()).Lift()) .ApplyBack(mf.As().Proxy) .ToProducer(); static K, A> MonadT, M>.Lift(K ma) => ProducerT.liftM(ma); static K, A> MonadIO>.LiftIO(IO ma) => ProducerT.liftIO(ma); static K, B> MonadUnliftIO>.MapIO(K, A> ma, Func, IO> f) => ma.As().MapM(m => M.MapIOMaybe(m, f)); static K, IO> MonadUnliftIO>.ToIO(K, A> ma) => ma.As().MapM(M.ToIOMaybe); static K, B> Applicative>.Action( K, A> ma, K, B> mb) => ProducerT.liftM(ma.As().Run().Action(mb.As().Run())); static K, A> Applicative>.Actions( IterableNE, A>> fas) => fas.Select(fa => fa.As().Proxy.Kind()) .Actions() .ToProducer(); } ================================================ FILE: LanguageExt.Streaming/Pipes/ProducerT/ProducerT.Operators.cs ================================================ using LanguageExt.Traits; namespace LanguageExt.Pipes; public static partial class ProducerTExtensions { extension(K, A>) where M : MonadIO { /// /// Downcast /// public static ProducerT operator +(K, A> ma) => (ProducerT) ma; /// /// Downcast /// public static ProducerT operator >>(K, A> ma, Lower lower) => +ma; } } ================================================ FILE: LanguageExt.Streaming/Pipes/ProducerT/ProducerT.cs ================================================ using System; using System.Diagnostics.Contracts; using LanguageExt.Traits; namespace LanguageExt.Pipes; /// /// `ProducerT` streaming producer monad-transformer instance /// public readonly record struct ProducerT(PipeT Proxy) : K, A> where M : MonadIO { [Pure] public ProducerT Compose(PipeT rhs) => Proxy.Compose(rhs).ToProducer(); [Pure] public EffectT Compose(ConsumerT rhs) => Proxy.Compose(rhs.Proxy).ToEffect(); [Pure] public static EffectT operator | (ProducerT lhs, ConsumerT rhs) => lhs.Proxy.Compose(rhs.Proxy).ToEffect(); [Pure] public ProducerT Map(Func f) => Proxy.Map(f); [Pure] public ProducerT MapM(Func, K> f) => Proxy.MapM(f); [Pure] public ProducerT ApplyBack(ProducerT> ff) => Proxy.ApplyBack(ff.Proxy); [Pure] public ProducerT Action(ProducerT fb) => Proxy.Action(fb.Proxy); [Pure] public ProducerT Bind(Func> f) => Proxy.Bind(x => f(x).Proxy); [Pure] public ProducerT Bind(Func> f) => Proxy.Bind(f); [Pure] public ProducerT Bind(Func> f) => Proxy.Bind(f); [Pure] public ProducerT Bind(Func> f) => Proxy.Bind(f); [Pure] public ProducerT Bind(Func> f) => Proxy.Bind(f); [Pure] public ProducerT Select(Func f) => Map(f); [Pure] public ProducerT SelectMany(Func> f, Func g) => Proxy.SelectMany(x => f(x).Proxy, g); [Pure] public ProducerT SelectMany(Func> f, Func g) => Proxy.SelectMany(f, g); [Pure] public ProducerT SelectMany(Func> f, Func g) => Proxy.SelectMany(f, g); [Pure] public ProducerT SelectMany(Func> f, Func g) => Proxy.SelectMany(f, g); [Pure] public ProducerT SelectMany(Func> f, Func g) => Proxy.SelectMany(f, g); /// /// Fold the given pipe until the `Schedule` completes. /// Once complete, the pipe yields the aggregated value downstream. /// /// Schedule to run each item /// Fold function /// Initial state /// Stream value to produce /// Lifted monad type /// Bound value type /// public ProducerT Fold( Schedule Time, Func Fold, OUT Init) => PipeT.fold(Time, Fold, Init, Proxy); /// /// Fold the given pipe until the predicate is `true`. Once `true` the pipe yields the /// aggregated value downstream. /// /// Fold function /// Until predicate /// Initial state /// Stream value to produce /// Lifted monad type /// Bound value type /// public ProducerT FoldUntil( Func Fold, Func<(OUT State, A Value), bool> Pred, OUT Init) => PipeT.foldUntil(Fold, Pred, Init, Proxy); /// /// Fold the given pipe until the predicate is `true` or the `Schedule` completes. /// Once `true`, or completed, the pipe yields the aggregated value downstream. /// /// Schedule to run each item /// Fold function /// Until predicate /// Initial state /// Stream value to produce /// Lifted monad type /// Bound value type /// public ProducerT FoldUntil( Schedule Time, Func Fold, Func<(OUT State, A Value), bool> Pred, OUT Init) => PipeT.foldUntil(Time, Fold, Pred, Init, Proxy); /// /// Fold the given pipe while the predicate is `true`. Once `false` the pipe yields the /// aggregated value downstream. /// /// Fold function /// Until predicate /// Initial state /// Stream value to produce /// Lifted monad type /// Bound value type /// public ProducerT FoldWhile( Func Fold, Func<(OUT State, A Value), bool> Pred, OUT Init) => PipeT.foldWhile(Fold, Pred, Init, Proxy); /// /// Fold the given pipe while the predicate is `true` or the `Schedule` completes. /// Once `false`, or completed, the pipe yields the aggregated value downstream. /// /// Schedule to run each item /// Fold function /// Until predicate /// Initial state /// Stream value to produce /// Lifted monad type /// Bound value type /// public ProducerT FoldWhile( Schedule Time, Func Fold, Func<(OUT State, A Value), bool> Pred, OUT Init) => PipeT.foldWhile(Time, Fold, Pred, Init, Proxy); [Pure] public static implicit operator ProducerT(PipeT pipe) => pipe.ToProducer(); [Pure] public static implicit operator ProducerT(Pure rhs) => ProducerT.pure(rhs.Value); [Pure] internal K Run() => Proxy.Run(); } ================================================ FILE: LanguageExt.Streaming/Pipes/README.md ================================================ > If you find this feature confusing at first, and it wouldn't be surprising as it's quite a complex idea, there are some examples in the [EffectsExample sample in the repo](https://github.com/louthy/language-ext/blob/main/Samples/EffectsExamples/Examples/TextFileChunkStreamExample.cs) Conventional stream programming forces you to choose only two of the following three features: 1. **Effects** 2. **Streaming** 3. **Composability** If you sacrifice _Effects_ you get `IEnumerable`, which you can transform using composable functions in constant space, but without interleaving effects (other than of the _imperative kind_). If you sacrifice _Streaming_ you get `Traverse` and `Sequence`, which are composable and effectful, but do not return a single result until the whole list has first been processed and loaded into memory. If you sacrifice _Composability_ you write a tightly coupled for loops, and fire off imperative side-effects like they're going out of style. Which is streaming and effectful, but is not modular or separable. `Pipes` gives you all three features: effectful, streaming, and composable programming. `Pipes` also provides a wide variety of stream programming abstractions which are all subsets of a single unified machinery: * Effectful [`Producer`](Producer) and [`ProducerT`](ProducerT), * Effectful [`Consumer`](Consumer) and [`ConsumerT`](ConsumerT), * Effectful [`Pipe`](Pipe) and [`PipeT`](PipeT) (like Unix pipes) * Effectful [`Effect`](Effect) and [`EffectT`](EffectT) > The `T` suffix types (`ProducerT`, `ConsumerT`, `PipeT`, and `EffectT`) are the > more generalist monad-transformers. They can lift any monad `M` you like into them, > supplementing the behaviour of `Pipes` with the behaviour of `M`. The non-`T` > suffix types (`Producer`, `Consumer`, `Pipe`, and `Effect`) only support the lifting > of the `Eff` type. They're slightly easier to use, just less flexible. All of these are connectable and you can combine them together in clever and unexpected ways because they all share the same underlying type: [`PipeT`](PipeT). The pipes ecosystem decouples stream processing stages from each other so that you can mix and match diverse stages to produce useful streaming programs. If you are a library writer, pipes lets you package up streaming components into a reusable interface. If you are an application writer, pipes lets you connect pre-made streaming components with minimal effort to produce a highly-efficient program that streams data in constant memory. To enforce loose coupling, components can only communicate using two commands: * `yield`: Send output data * `awaiting`: Receive input data Pipes has four types of components built around these two commands: * [`Producer`](Producer) and [`ProducerT`](ProducerT) yield values downstream and can only do so using: `Producer.yield` and `ProducerT.yield`. * [`Consumer`](Consumer) and [`ConsumerT`](ConsumerT) await values from upstream and can only do so using: `Consumer.awaiting` and `ConsumerT.awaiting`. * [`Pipe`](Pipe) and [`PipeT`](PipeT) can both await and yield, using: `Pipe.awaiting`, `PipeT.awaiting`, `Pipe.yield`, and `PipeT.yield`. * [`Effect`](Effect) and [`EffectT`](EffectT) can neither yield nor await and they model non-streaming components. Pipes uses parametric polymorphism (i.e. generics) to overload all operations. The operator `|` connects `Producer`, `Consumer`, and `Pipe` by 'fusing' them together. Eventually they will 'fuse' together into an `Effect` or `EffectT`. This final state can be `.Run()`, you must fuse to an `Effect` or `EffectT` to be able to invoke any of the pipelines. `Producer`, `ProducerT`, `Consumer`, `ConsumerT`, `Pipe`, `Effect`, and `EffectT` are all special cases of a single underlying type: [`PipeT`](PipeT). You can think of it as having the following shape: PipeT Upstream | Downstream +---------+ | | IN --► --► OUT -- Information flowing downstream | | | +----|----+ | A Pipes uses type synonyms to hide unused inputs or outputs and clean up type signatures. These type synonyms come in two flavors: * Concrete type synonyms that explicitly close unused inputs and outputs of the `Proxy` type. * Polymorphic type synonyms that don't explicitly close unused inputs or outputs. The concrete type synonyms use `Unit` to close unused inputs and `Void` (the uninhabited type) to close unused outputs: * `EffectT`: explicitly closes both ends, forbidding `awaiting` and `yield`: EffectT = PipeT Upstream | Downstream +---------+ | | Unit --► --► Void | | | +----|----+ | A * `ProducerT`: explicitly closes the upstream end, forbidding `awaiting`: ProducerT = PipeT Upstream | Downstream +---------+ | | Unit --► --► OUT | | | +----|----+ | A * `ConsumerT`: explicitly closes the downstream end, forbidding `yield`: ConsumerT = PipeT Upstream | Downstream +---------+ | | IN --► --► Void | | | +----|----+ | A When you compose `PipeT` using `|` all you are doing is placing them side by side and fusing them laterally. For example, when you compose a `ProducerT`, `PipeT`, and a `ConsumerT`, you can think of information flowing like this: ProducerT PipeT ConsumerT +-------------+ +------------+ +-------------+ | | | | | | Unit --► readLine --► string --► parseInt --► int --► writeLine --► Void | | | | | | | | | +------|------+ +------|-----+ +------|------+ | | | A A A Composition fuses away the intermediate interfaces, leaving behind an `EffectT`: EffectT +-------------------------------------+ | | Unit --► readLine | parseInt | writeLine --► Void | | +------------------|------------------+ | A This `EffectT` can be `Run()` which will return the composed underlying `M` type. Or, if it's an `Effect` will return the composed underlying `Eff`. ================================================ FILE: LanguageExt.Streaming/README.md ================================================ The `Streaming` library of language-ext is all about compositional streams. There are two key types of streaming functionality: **closed-streams** and **open-streams**... ## Closed streams Closed streams are facilitated by the [`Pipes`](Pipes) system. The types in the `Pipes` system are _compositional monad-transformers_ that 'fuse' together to produce an [`EffectT`](Pipes/EffectT). This effect is a _closed system_, meaning that there is no way (from the API) to directly interact with the effect from the outside: it can be executed and will return a result if it terminates. The pipeline components are: * [`ProducerT`](Pipes/ProducerT) * [`PipeT`](Pipes/PipeT) * [`ConsumerT`](Pipes/ConsumerT) These are the components that fuse together (using the `|` operator) to make an [`EffectT`](Pipes/EffectT). The types are _monad-transformers_ that support lifting monads with the `MonadIO` trait only (which constrains `M`). This makes sense, otherwise the closed-system would have no effect other than heating up the CPU. There are also more specialised versions of the above that only support the lifting of the `Eff` effect-monad: * [`Producer`](Pipes/Producer) * [`Pipe`](Pipes/Pipe) * [`Consumer`](Pipes/Consumer) They all fuse together into an [`Effect`](Pipes/Effect). Pipes are especially useful if you want to build reusable streaming components that you can glue together ad infinitum. Pipes are, arguably, less useful for day-to-day stream processing, like handling events, but your mileage may vary. _More details on the [`Pipes page`](Pipes)._ ## Open streams Open streams are closer to what most C# devs have used classically. They are like events or `IObservable` streams. They yield values and (under certain circumstances) accept inputs. * [`Source`](Source) and [`SourceT`](SourceT) yield values synchronously or asynchronously depending on their construction. * [`Sink`](Sink) and [`SinkT`](SinkT) receives values and propagates them through the channel they're attached to. * [`Conduit`](Conduit) and [`ConduitT`](ConduitT) provides and input transducer (acts like a `Sink`), an internal buffer, and an output transducer (acts like a `Source`). > I'm calling these 'open streams' because we can `Post` values to a `Sink`/`SinkT` and we can `Reduce` values yielded by > `Source`/`SourceT`. So, they are 'open' for public manipulation, unlike `Pipes` which fuse the public access away. ### [`Source`](Source) [`Source`](Source) is the 'classic stream': you can lift any of the following types into it: `System.Threading.Channels.Channel`, `IEnumerable`, `IAsyncEnumerable`, or singleton values. To process a stream, you need to use one of the `Reduce` or `ReduceAsync` variants. These take `Reducer` delegates as arguments. They are essentially a fold over the stream of values, which results in an aggregated state once the stream has completed. These reducers can be seen to play a similar role to `Subscribe` in `IObservable` streams, but are more principled because they return a value (which we can leverage to carry state for the duration of the stream). `Source` also supports some built-in reducers: * `Last` - aggregates no state, simply returns the last item yielded * `Iter` - this forces evaluation of the stream, aggregating no state, and ignoring all yielded values. * `Collect` - adds all yielded values to a `Seq`, which is then returned upon stream completion. ### [`SourceT`](SourceT) [`SourceT`](SourceT) is the classic-stream _embellished_ - it turns the stream into a monad-transformer that can lift any `MonadIO`-enabled monad (`M`), allowing side effects to be embedded into the stream in a principled way. So, for example, to use the `IO` monad with `SourceT`, simply use: `SourceT`. Then you can use one of the following `static` methods on the `SourceT` type to lift `IO` effects into a stream: * `SourceT.liftM(IO effect)` creates a singleton-stream * `SourceT.foreverM(IO effect)` creates an infinite stream, repeating the same effect over and over * `SourceT.liftM(Channel> channel)` lifts a `System.Threading.Channels.Channel` of effects * `SourceT.liftM(IEnumerable> effects)` lifts an `IEnumerable` of effects * `SourceT.liftM(IAsyncEnumerable> effects)` lifts an `IAsyncEnumerable` of effects > Obviously, when lifting non-`IO` monads, the types above change. `SourceT` also supports the same built-in convenience reducers as `Source` (`Last`, `Iter`, `Collect`). ### [`Sink`](Sink) [`Sink`](Sink) provides a way to accept many input values. The values are buffered until consumed. The sink can be thought of as a `System.Threading.Channels.Channel` (which is the buffer that collects the values) that happens to manipulate the values being posted to the buffer just before they are stored. > This manipulation is possible because the `Sink` is a `CoFunctor` (contravariant functor). This is the dual of `Functor`: we can think of `Functor.Map` as converting a value from `A -> B`. Whereas `CoFunctor.Comap` converts from `B -> A`. So, to manipulate values coming into the `Sink`, use `Comap`. It will give you a new `Sink` with the manipulation 'built-in'. ### [`SinkT`](SinkT) [`SinkT`](SinkT) provides a way to accept many input values. The values are buffered until consumed. The sink can be thought of as a `System.Threading.Channels.Channel` (which is the buffer that collects the values) that happens to manipulate the values being posted to the buffer just before they are stored. > This manipulation is possible because the `SinkT` is a `CoFunctor` (contravariant functor). This is the dual of `Functor`: we can think of `Functor.Map` as converting a value from `A -> B`. Whereas `CoFunctor.Comap` converts from `B -> A`. So, to manipulate values coming into the `SinkT`, use `Comap`. It will give you a new `SinkT` with the manipulation 'built-in'. `SinkT` is also a transformer that lifts types of `K`. ### [`Conduit`](Conduit) `Conduit` can be pictured as so: +----------------------------------------------------------------+ | | | A --> Transducer --> X --> Buffer --> X --> Transducer --> B | | | +----------------------------------------------------------------+ * A value of `A` is posted to the `Conduit` (via `Post`) * It flows through an input `Transducer`, mapping the `A` value to `X` (an internal type you can't see) * The `X` value is then stored in the conduit's internal buffer (a `System.Threading.Channels.Channel`) * Any invocation of `Reduce` will force the consumption of the values in the buffer * Flowing each value `X` through the output `Transducer` So the input and output transducers allow for pre and post-processing of values as they flow through the conduit. `Conduit` is a `CoFunctor`, call `Comap` to manipulate the pre-processing transducer. `Conduit` is also a `Functor`, call `Map` to manipulate the post-processing transducer. There are other non-trait, but common behaviours, like `FoldWhile`, `Filter`, `Skip`, `Take`, etc. > `Conduit` supports access to a `Sink` and a `Source` for more advanced processing. ### [`ConduitT`](Conduit) `ConduitT` can be pictured as so: +------------------------------------------------------------------------------------------+ | | | K --> TransducerM --> K --> Buffer --> K --> TransducerM --> K | | | +------------------------------------------------------------------------------------------+ * A value of `K` is posted to the `Conduit` (via `Post`) * It flows through an input `TransducerM`, mapping the `K` value to `K` (an internal type you can't see) * The `K` value is then stored in the conduit's internal buffer (a `System.Threading.Channels.Channel`) * Any invocation of `Reduce` will force the consumption of the values in the buffer * Flowing each value `K` through the output `TransducerM` So the input and output transducers allow for pre and post-processing of values as they flow through the conduit. `ConduitT` is a `CoFunctor`, call `Comap` to manipulate the pre-processing transducer. `Conduit` is also a `Functor`, call `Map` to manipulate the post-processing transducer. There are other non-trait, but common behaviours, like `FoldWhile`, `Filter`, `Skip`, `Take`, etc. > `ConduitT` supports access to a `SinkT` and a `SourceT` for more advanced processing. ## Open to closed streams Clearly, even for 'closed systems' like the [`Pipes`](Pipes) system, it would be beneficial to be able to post values into the streams from the outside. And so, the _open-stream components_ can all be converted into `Pipes` components like `ProducerT` and `ConsumerT`. * `Conduit` and `ConduitT` support `ToProducer`, `ToProducerT`, `ToConsumer`, and `ToConsumerT`. * `Sink` and `SinkT` supports `ToConsumer`, and `ToConsumerT`. * `Source` and `SourceT` supports `ToProducer`, and `ToProducerT`. This allows for the ultimate flexibility in your choice of streaming effect. It also allows for efficient concurrency in the more abstract and compositional world of the pipes. In fact `ProducerT.merge`, which merges many streams into one, uses `ConduitT` internally to collect the values and to merge them into a single `ProducerT`. ================================================ FILE: LanguageExt.Streaming/README.nuget.md ================================================ The `Streaming` library of language-ext is all about compositional streams. There are two key types of streaming functionality: **closed-streams** and **open-streams**... ## Closed streams Closed streams are facilitated by the [`Pipes`](Pipes) system. The types in the `Pipes` system are _compositional monad-transformers_ that 'fuse' together to produce an [`EffectT`](Pipes/EffectT). This effect is a _closed system_, meaning that there is no way (from the API) to directly interact with the effect from the outside: it can be executed and will return a result if it terminates. The pipeline components are: * [`ProducerT`](Pipes/ProducerT) * [`PipeT`](Pipes/PipeT) * [`ConsumerT`](Pipes/ConsumerT) These are the components that fuse together (using the `|` operator) to make an [`EffectT`](Pipes/EffectT). The types are _monad-transformers_ that support lifting monads with the `MonadIO` trait only (which constrains `M`). This makes sense, otherwise the closed-system would have no effect other than heating up the CPU. There are also more specialised versions of the above that only support the lifting of the `Eff` effect-monad: * [`Producer`](Pipes/Producer) * [`Pipe`](Pipes/Pipe) * [`Consumer`](Pipes/Consumer) They all fuse together into an [`Effect`](Pipes/Effect). Pipes are especially useful if you want to build reusable streaming components that you can glue together ad infinitum. Pipes are, arguably, less useful for day-to-day stream processing, like handling events, but your mileage may vary. _More details on the [`Pipes page`](Pipes)._ ## Open streams Open streams are closer to what most C# devs have used classically. They are like events or `IObservable` streams. They yield values and (under certain circumstances) accept inputs. * [`Source`](Source) and [`SourceT`](SourceT) yield values synchronously or asynchronously depending on their construction. * [`Sink`](Sink) and [`SinkT`](SinkT) receives values and propagates them through the channel they're attached to. * [`Conduit`](Conduit) and [`ConduitT`](ConduitT) provides and input transducer (acts like a `Sink`), an internal buffer, and an output transducer (acts like a `Source`). > I'm calling these 'open streams' because we can `Post` values to a `Sink`/`SinkT` and we can `Reduce` values yielded by > `Source`/`SourceT`. So, they are 'open' for public manipulation, unlike `Pipes` which fuse the public access away. ### [`Source`](Source) [`Source`](Source) is the 'classic stream': you can lift any of the following types into it: `System.Threading.Channels.Channel`, `IEnumerable`, `IAsyncEnumerable`, or singleton values. To process a stream, you need to use one of the `Reduce` or `ReduceAsync` variants. These take `Reducer` delegates as arguments. They are essentially a fold over the stream of values, which results in an aggregated state once the stream has completed. These reducers can be seen to play a similar role to `Subscribe` in `IObservable` streams, but are more principled because they return a value (which we can leverage to carry state for the duration of the stream). `Source` also supports some built-in reducers: * `Last` - aggregates no state, simply returns the last item yielded * `Iter` - this forces evaluation of the stream, aggregating no state, and ignoring all yielded values. * `Collect` - adds all yielded values to a `Seq`, which is then returned upon stream completion. ### [`SourceT`](SourceT) [`SourceT`](SourceT) is the classic-stream _embellished_ - it turns the stream into a monad-transformer that can lift any `MonadIO`-enabled monad (`M`), allowing side effects to be embedded into the stream in a principled way. So, for example, to use the `IO` monad with `SourceT`, simply use: `SourceT`. Then you can use one of the following `static` methods on the `SourceT` type to lift `IO` effects into a stream: * `SourceT.liftM(IO effect)` creates a singleton-stream * `SourceT.foreverM(IO effect)` creates an infinite stream, repeating the same effect over and over * `SourceT.liftM(Channel> channel)` lifts a `System.Threading.Channels.Channel` of effects * `SourceT.liftM(IEnumerable> effects)` lifts an `IEnumerable` of effects * `SourceT.liftM(IAsyncEnumerable> effects)` lifts an `IAsyncEnumerable` of effects > Obviously, when lifting non-`IO` monads, the types above change. `SourceT` also supports the same built-in convenience reducers as `Source` (`Last`, `Iter`, `Collect`). ### [`Sink`](Sink) [`Sink`](Sink) provides a way to accept many input values. The values are buffered until consumed. The sink can be thought of as a `System.Threading.Channels.Channel` (which is the buffer that collects the values) that happens to manipulate the values being posted to the buffer just before they are stored. > This manipulation is possible because the `Sink` is a `CoFunctor` (contravariant functor). This is the dual of `Functor`: we can think of `Functor.Map` as converting a value from `A -> B`. Whereas `CoFunctor.Comap` converts from `B -> A`. So, to manipulate values coming into the `Sink`, use `Comap`. It will give you a new `Sink` with the manipulation 'built-in'. ### [`SinkT`](SinkT) [`SinkT`](SinkT) provides a way to accept many input values. The values are buffered until consumed. The sink can be thought of as a `System.Threading.Channels.Channel` (which is the buffer that collects the values) that happens to manipulate the values being posted to the buffer just before they are stored. > This manipulation is possible because the `SinkT` is a `CoFunctor` (contravariant functor). This is the dual of `Functor`: we can think of `Functor.Map` as converting a value from `A -> B`. Whereas `CoFunctor.Comap` converts from `B -> A`. So, to manipulate values coming into the `SinkT`, use `Comap`. It will give you a new `SinkT` with the manipulation 'built-in'. `SinkT` is also a transformer that lifts types of `K`. ### [`Conduit`](Conduit) `Conduit` can be pictured as so: +----------------------------------------------------------------+ | | | A --> Transducer --> X --> Buffer --> X --> Transducer --> B | | | +----------------------------------------------------------------+ * A value of `A` is posted to the `Conduit` (via `Post`) * It flows through an input `Transducer`, mapping the `A` value to `X` (an internal type you can't see) * The `X` value is then stored in the conduit's internal buffer (a `System.Threading.Channels.Channel`) * Any invocation of `Reduce` will force the consumption of the values in the buffer * Flowing each value `X` through the output `Transducer` So the input and output transducers allow for pre and post-processing of values as they flow through the conduit. `Conduit` is a `CoFunctor`, call `Comap` to manipulate the pre-processing transducer. `Conduit` is also a `Functor`, call `Map` to manipulate the post-processing transducer. There are other non-trait, but common behaviours, like `FoldWhile`, `Filter`, `Skip`, `Take`, etc. > `Conduit` supports access to a `Sink` and a `Source` for more advanced processing. ### [`ConduitT`](Conduit) `ConduitT` can be pictured as so: +------------------------------------------------------------------------------------------+ | | | K --> TransducerM --> K --> Buffer --> K --> TransducerM --> K | | | +------------------------------------------------------------------------------------------+ * A value of `K` is posted to the `Conduit` (via `Post`) * It flows through an input `TransducerM`, mapping the `K` value to `K` (an internal type you can't see) * The `K` value is then stored in the conduit's internal buffer (a `System.Threading.Channels.Channel`) * Any invocation of `Reduce` will force the consumption of the values in the buffer * Flowing each value `K` through the output `TransducerM` So the input and output transducers allow for pre and post-processing of values as they flow through the conduit. `ConduitT` is a `CoFunctor`, call `Comap` to manipulate the pre-processing transducer. `Conduit` is also a `Functor`, call `Map` to manipulate the post-processing transducer. There are other non-trait, but common behaviours, like `FoldWhile`, `Filter`, `Skip`, `Take`, etc. > `ConduitT` supports access to a `SinkT` and a `SourceT` for more advanced processing. ## Open to closed streams Clearly, even for 'closed systems' like the [`Pipes`](Pipes) system, it would be beneficial to be able to post values into the streams from the outside. And so, the _open-stream components_ can all be converted into `Pipes` components like `ProducerT` and `ConsumerT`. * `Conduit` and `ConduitT` support `ToProducer`, `ToProducerT`, `ToConsumer`, and `ToConsumerT`. * `Sink` and `SinkT` supports `ToConsumer`, and `ToConsumerT`. * `Source` and `SourceT` supports `ToProducer`, and `ToProducerT`. This allows for the ultimate flexibility in your choice of streaming effect. It also allows for efficient concurrency in the more abstract and compositional world of the pipes. In fact `ProducerT.merge`, which merges many streams into one, uses `ConduitT` internally to collect the values and to merge them into a single `ProducerT`. ================================================ FILE: LanguageExt.Streaming/Sink/DSL/SinkChoose.cs ================================================ using System; using LanguageExt.Common; namespace LanguageExt; record SinkChoose(Func> F, Sink Left, Sink Right) : Sink { public override Sink Comap(Func f) => new SinkContraMap(f, this); public override IO Post(A value) => F(value) switch { Either.Left(var left) => Left.Post(left), Either.Right(var right) => Right.Post(right), _ => IO.fail(Errors.SinkFull) }; public override IO Complete() => Left.Complete().Bind(_ => Right.Complete()); public override IO Fail(Error error) => Left.Fail(error).Bind(_ => Right.Fail(error)); } ================================================ FILE: LanguageExt.Streaming/Sink/DSL/SinkCombine.cs ================================================ using System; using LanguageExt.Common; using static LanguageExt.Prelude; namespace LanguageExt; record SinkCombine(Func F, Sink Left, Sink Right) : Sink { public override Sink Comap(Func f) => new SinkContraMap(f, this); public override IO Post(A value) => F(value) switch { var (b, c) => awaitAll(Left.Post(b).Map(Seq()).Catch(_ => true, e => IO.pure(Seq(e))), Right.Post(c).Map(Seq()).Catch(_ => true, e => IO.pure(Seq(e)))) .Map(es => es.Flatten()) .Bind(es => es switch { [] => unitIO, _ => IO.fail(Error.Many(es)) }).As() }; public override IO Complete() => Left.Complete().Bind(_ => Right.Complete()); public override IO Fail(Error error) => Left.Fail(error).Bind(_ => Right.Fail(error)); } ================================================ FILE: LanguageExt.Streaming/Sink/DSL/SinkContraMap.cs ================================================ using System; using LanguageExt.Common; namespace LanguageExt; record SinkContraMap(Func F, Sink Sink) : Sink { public override Sink Comap(Func f) => new SinkContraMap(x => F(f(x)), Sink); public override IO Post(B value) => Sink.Post(F(value)); public override IO Complete() => Sink.Complete(); public override IO Fail(Error Error) => Sink.Fail(Error); } ================================================ FILE: LanguageExt.Streaming/Sink/DSL/SinkContraMapT.cs ================================================ using System; using System.Threading.Channels; using LanguageExt.Common; using static LanguageExt.Prelude; namespace LanguageExt; record SinkContraMapT(Transducer F, Sink Sink) : Sink { public override Sink Comap(Func f) => new SinkContraMapT(Transducer.map(f).Compose(F), Sink); public override Sink Comap(Transducer f) => new SinkContraMapT(f.Compose(F), Sink); public override IO Post(B value) => F.Reduce((_, a) => Sink.Post(a).Map(Reduced.Continue))(unit, value) .Map(x => x.Value); public override IO Complete() => Sink.Complete(); public override IO Fail(Error Error) => Sink.Fail(Error); } ================================================ FILE: LanguageExt.Streaming/Sink/DSL/SinkEmpty.cs ================================================ using System; using LanguageExt.Common; using static LanguageExt.Prelude; namespace LanguageExt; record SinkEmpty : Sink { public static readonly Sink Default = new SinkEmpty(); public override Sink Comap(Func f) => new SinkEmpty(); public override IO Post(A value) => IO.fail(Errors.SinkFull); public override IO Complete() => unitIO; public override IO Fail(Error error) => unitIO; } ================================================ FILE: LanguageExt.Streaming/Sink/DSL/SinkVoid.cs ================================================ using System; using LanguageExt.Common; using static LanguageExt.Prelude; namespace LanguageExt; record SinkVoid : Sink { public static readonly Sink Default = new SinkVoid(); public override Sink Comap(Func f) => new SinkVoid(); public override IO Post(A value) => unitIO; public override IO Complete() => unitIO; public override IO Fail(Error error) => unitIO; } ================================================ FILE: LanguageExt.Streaming/Sink/DSL/SinkWriter.cs ================================================ using System; using System.Threading.Channels; using LanguageExt.Common; namespace LanguageExt; record SinkWriter(Channel Channel) : Sink { public override Sink Comap(Func f) => new SinkContraMap(f, this); public override IO Post(A value) => from f in IO.liftVAsync(e => Channel.Writer.WaitToWriteAsync(e.Token)) from r in f ? IO.liftVAsync(() => Channel.Writer.WriteAsync(value).ToUnit()) : IO.fail(Errors.SinkFull) select r; public override IO Complete() => IO.lift(() => Channel.Writer.Complete()); public override IO Fail(Error error) => IO.lift(() => Channel.Writer.Complete(error.ToException())); } ================================================ FILE: LanguageExt.Streaming/Sink/Sink.CoFunctor.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public partial class Sink : Decidable { static K Cofunctor.Comap(Func f, K fb) => fb.As().Comap(f); static K Divisible.Divide(Func f, K fb, K fc) => new SinkCombine(f, fb.As(), fc.As()); static K Divisible.Conquer() => Sink.Empty; static K Decidable.Lose(Func f) => Sink.Void; static K Decidable.Route(Func> f, K fb, K fc) => new SinkChoose(f, fb.As(), fc.As()); } ================================================ FILE: LanguageExt.Streaming/Sink/Sink.Extensions.cs ================================================ using LanguageExt.Traits; namespace LanguageExt; public static class SinkExtensions { /// /// Downcast /// public static Sink As(this K ma) => (Sink)ma; } ================================================ FILE: LanguageExt.Streaming/Sink/Sink.Module.cs ================================================ using System.Threading.Channels; using LanguageExt.Traits; namespace LanguageExt; public partial class Sink { /// /// Create a sink from a `System.Threading.Channels.Channel`. /// /// Channel to lift /// Bound value type /// Constructed sink public static Sink lift(Channel channel) => new SinkWriter(channel); } ================================================ FILE: LanguageExt.Streaming/Sink/Sink.cs ================================================ using System; using LanguageExt.Common; using LanguageExt.Pipes; using LanguageExt.Traits; namespace LanguageExt; /// /// Entry point to a channel. Sinks receive values and propagate them through the /// channel they're attached to. The behaviour depends on the `Buffer` type they /// were created with. /// /// Value type public abstract record Sink : K, Monoid> { /// /// Post a value to the Sink /// /// /// Raises `Errors.NoSpaceInSink` if the Sink is full or closed. /// /// Value to post /// IO computation that represents the posting public abstract IO Post(A value); /// /// Complete and close the Sink /// public abstract IO Complete(); /// /// Complete and close the Sink with an `Error` /// public abstract IO Fail(Error Error); /// /// Contravariant functor map /// public abstract Sink Comap(Func f); /// /// Contravariant functor map /// public virtual Sink Comap(Transducer f) => new SinkContraMapT(f, this); /// /// Combine two Sinks: `lhs` and `rhs` into a single Sink that takes incoming /// values and then posts to the `lhs` and `rhs` Sinks. /// public Sink Combine(Sink rhs) => new SinkCombine(x => (x, x), this, rhs); /// /// Combine two Sinks: `lhs` and `rhs` into a single Sink that takes incoming /// values, maps them to an `(A, B)` tuple, and then posts the first and second /// elements to the `lhs` and `rhs` Sinks. /// public Sink Combine(Func f, Sink rhs) => new SinkCombine(f, this, rhs); /// /// Combine two Sinks into a single Source. The values are both /// merged into a new Sink. /// /// Left-hand side /// Right-hand side /// Merged stream of values public static Sink operator +(Sink lhs, Sink rhs) => lhs.Combine(rhs); /// /// Sink that is closed and can't be posted to without an error being raised /// public static Sink Empty => SinkEmpty.Default; /// /// Sink that swallows everything silently /// public static Sink Void => SinkVoid.Default; /// /// Convert the `Sink` to a `ConsumerT` pipe component /// /// Monad to lift (must support `IO`) /// `ConsumerT` public ConsumerT ToConsumerT() where M : MonadIO => ConsumerT.repeat(ConsumerT.awaiting().Bind(Post)); /// /// Convert the `Sink` to a `Consumer` pipe component /// /// `Consumer` public Consumer ToConsumer() => ToConsumerT>(); } ================================================ FILE: LanguageExt.Streaming/SinkT/DSL/SinkChoose.cs ================================================ using System; using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt; record SinkTChoose(Func> F, SinkT Left, SinkT Right) : SinkT where M : MonadIO { public override SinkT Comap(Func f) => new SinkTContraMap(f, this); public override K PostM(K ma) => ma.Bind(value => F(value) switch { Either.Left(var left) => Left.PostM(M.Pure(left)), Either.Right(var right) => Right.PostM(M.Pure(right)), _ => M.LiftIOMaybe(IO.fail(Errors.SinkFull)) }); public override K Complete() => Left.Complete().Bind(_ => Right.Complete()); public override K Fail(Error error) => Left.Fail(error).Bind(_ => Right.Fail(error)); } ================================================ FILE: LanguageExt.Streaming/SinkT/DSL/SinkCombine.cs ================================================ using System; using LanguageExt.Common; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; record SinkTCombine(Func F, SinkT Left, SinkT Right) : SinkT where M : MonadIO { public override SinkT Comap(Func f) => new SinkTContraMap(f, this); public override K PostM(K ma) => ma.Bind(value => F(value) switch { var (b, c) => awaitAll(Left.PostM(M.Pure(b)).ConstMap(Seq()), Right.PostM(M.Pure(c)).ConstMap(Seq())) .Map(es => es.Flatten()) .Bind(es => es switch { [] => unitIO, _ => IO.fail(Error.Many(es)) }) }); static K> awaitAll(params K[] ms) => toSeq(ms) .Traverse(M.ToIOMaybe) .Bind(Prelude.awaitAll); public override K Complete() => Left.Complete().Bind(_ => Right.Complete()); public override K Fail(Error error) => Left.Fail(error).Bind(_ => Right.Fail(error)); } ================================================ FILE: LanguageExt.Streaming/SinkT/DSL/SinkContraMap.cs ================================================ using System; using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt; record SinkTContraMap(Func F, SinkT Sink) : SinkT where M : MonadIO { public override SinkT Comap(Func f) => new SinkTContraMap(x => F(f(x)), Sink); public override K PostM(K ma) => Sink.PostM(ma.Map(F)); public override K Complete() => Sink.Complete(); public override K Fail(Error Error) => Sink.Fail(Error); } ================================================ FILE: LanguageExt.Streaming/SinkT/DSL/SinkContraMapT.cs ================================================ using System; using LanguageExt.Common; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; record SinkTContraMapT(TransducerM F, SinkT Sink) : SinkT where M : MonadIO { public override SinkT Comap(Func f) => new SinkTContraMapT(TransducerM.map(f).Compose(F), Sink); public override SinkT Comap(TransducerM f) => new SinkTContraMapT(f.Compose(F), Sink); public override K PostM(K mb) => mb.Bind(b => F.Reduce((_, a) => Sink.PostM(M.Pure(a)) * Reduced.Continue)(unit, b)) .Map(r => r.Value); public override K Complete() => Sink.Complete(); public override K Fail(Error Error) => Sink.Fail(Error); } ================================================ FILE: LanguageExt.Streaming/SinkT/DSL/SinkEmpty.cs ================================================ using System; using LanguageExt.Common; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; record SinkTEmpty : SinkT where M : MonadIO { public static readonly SinkT Default = new SinkTEmpty(); public override SinkT Comap(Func f) => SinkTEmpty.Default; public override K PostM(K ma) => M.LiftIOMaybe(IO.fail(Errors.SinkFull)); public override K Complete() => M.Pure(unit); public override K Fail(Error error) => M.Pure(unit); } ================================================ FILE: LanguageExt.Streaming/SinkT/DSL/SinkVoid.cs ================================================ using System; using LanguageExt.Common; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; record SinkTVoid : SinkT where M : MonadIO { public static readonly SinkT Default = new SinkTVoid(); public override SinkT Comap(Func f) => SinkTVoid.Default; public override K PostM(K ma) => M.Pure(unit); public override K Complete() => M.Pure(unit); public override K Fail(Error error) => M.Pure(unit); } ================================================ FILE: LanguageExt.Streaming/SinkT/DSL/SinkWriter.cs ================================================ using System; using System.Threading.Channels; using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt; record SinkTWriter(Channel> Channel) : SinkT where M : MonadIO { public override SinkT Comap(Func f) => new SinkTContraMap(f, this); public override K PostM(K ma) => M.LiftIOMaybe(from f in IO.liftVAsync(e => Channel.Writer.WaitToWriteAsync(e.Token)) from r in f ? IO.liftVAsync(() => Channel.Writer.WriteAsync(ma).ToUnit()) : IO.fail(Errors.SinkFull) select r); public override K Complete() => M.LiftIOMaybe(IO.lift(() => Channel.Writer.Complete())); public override K Fail(Error error) => M.LiftIOMaybe(IO.lift(() => Channel.Writer.Complete(error.ToException()))); } ================================================ FILE: LanguageExt.Streaming/SinkT/SinkT.CoFunctor.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public partial class SinkT : Decidable> where M : MonadIO { static K, A> Cofunctor>.Comap(Func f, K, B> fb) => fb.As().Comap(f); static K, A> Divisible>.Divide(Func f, K, B> fb, K, C> fc) => new SinkTCombine(f, fb.As(), fc.As()); static K, A> Divisible>.Conquer() => SinkT.Empty; static K, A> Decidable>.Lose(Func f) => SinkT.Void; static K, A> Decidable>.Route(Func> f, K, B> fb, K, C> fc) => new SinkTChoose(f, fb.As(), fc.As()); } ================================================ FILE: LanguageExt.Streaming/SinkT/SinkT.Extensions.cs ================================================ using System; using LanguageExt.Pipes; using LanguageExt.Traits; namespace LanguageExt; public static class SinkTExtensions { /// /// Downcast /// public static SinkT As(this K, A> ma) where M : MonadIO => (SinkT)ma; /// /// Convert the `Sink` to a `Consumer` pipe component /// /// `Consumer` public static Consumer ToConsumer(this SinkT, A> ma) => ma.ToConsumerT(); } ================================================ FILE: LanguageExt.Streaming/SinkT/SinkT.Module.cs ================================================ using System.Threading.Channels; using LanguageExt.Traits; namespace LanguageExt; public partial class SinkT { /// /// Create a sink from a `System.Threading.Channels.Channel`. /// /// Channel to lift /// Bound value type /// Constructed sink public static SinkT lift(Channel> channel) where M : MonadIO => new SinkTWriter(channel); } ================================================ FILE: LanguageExt.Streaming/SinkT/SinkT.cs ================================================ using System; using LanguageExt.Common; using LanguageExt.Pipes; using LanguageExt.Traits; namespace LanguageExt; /// /// Entry point to a channel. Sinks receive values and propagate them through the /// channel they're attached to. The behaviour depends on the `Buffer` type they /// were created with. /// /// Lifted type /// Value type public abstract record SinkT : K, A>, Monoid> where M : MonadIO { /// /// Post a value to the sink /// /// /// Raises `Errors.NoSpaceInSink` if the sink is full or closed. /// /// Value to post /// IO computation that represents the posting public abstract K PostM(K ma); /// /// Post a value to the sink /// /// /// Raises `Errors.NoSpaceInSink` if the sink is full or closed. /// /// Value to post /// IO computation that represents the posting public K Post(A value) => PostM(M.Pure(value)); /// /// Complete and close the sink /// public abstract K Complete(); /// /// Complete and close the sink with an `Error` /// public abstract K Fail(Error Error); /// /// Contravariant functor map /// public abstract SinkT Comap(Func f); /// /// Contravariant functor map /// public virtual SinkT Comap(TransducerM f) => new SinkTContraMapT(f, this); /// /// Combine two Sinks: `lhs` and `rhs` into a single Sink that takes incoming /// values and then posts to the `lhs` and `rhs` Sinks. /// public SinkT Combine(SinkT rhs) => new SinkTCombine(x => (x, x), this, rhs); /// /// Combine two Sinks: `lhs` and `rhs` into a single Sink that takes incoming /// values, maps them to an `(A, B)` tuple, and then posts the first and second /// elements to the `lhs` and `rhs` Sinks. /// public SinkT Combine(SinkT rhs, Func f) => new SinkTCombine(f, this, rhs); /// /// Combine two Sinks into a single Source. The values are both /// merged into a new Sink. /// /// Left-hand side /// Right-hand side /// Merged stream of values public static SinkT operator +(SinkT lhs, SinkT rhs) => lhs.Combine(rhs); /// /// Sink that is closed and can't be posted to without an error being raised /// public static SinkT Empty => SinkTEmpty.Default; /// /// Sink that swallows everything silently /// public static SinkT Void => SinkTVoid.Default; /// /// Convert the `Sink` to a `ConsumerT` pipe component /// /// Monad to lift (must support `IO`) /// `ConsumerT` public ConsumerT ToConsumerT() => ConsumerT.repeat(ConsumerT.awaiting().Bind(x => PostM(M.Pure(x)))); } ================================================ FILE: LanguageExt.Streaming/Source/Extensions/Source.Extensions.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics.Contracts; using System.Threading.Channels; using LanguageExt.Traits; namespace LanguageExt; public static partial class SourceExtensions { /// /// Downcast /// public static Source As(this K ma) => (Source)ma; [Pure] public static Source AsSource(this Channel items) => Source.lift(items); [Pure] public static Source AsSource(this IEnumerable items) => Source.lift(items); [Pure] public static Source AsSource(this IAsyncEnumerable items) => Source.lift(items); [Pure] public static Source AsSource(this IObservable items) => Source.lift(items); } ================================================ FILE: LanguageExt.Streaming/Source/Extensions/Source.Reducers.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics.Contracts; using System.Threading.Channels; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; public static partial class SourceExtensions { /// /// Force iteration of the stream, yielding a unit `M` structure. /// /// /// The expectation is that the stream uses `IO` for side effects, so this makes them to happen. /// public static IO Iter(this K ma) => ma.As().ReduceIO(unit, (_, _) => Reduced.ContinueIO(unit)); /// /// Force iteration of the stream, yielding a unit `M` structure. /// /// /// The expectation is that the stream uses `IO` for side effects, so this makes them to happen. /// public static K Iter(this K ma) where M : MonadIO => M.LiftIOMaybe(ma.Iter()); /// /// Force iteration of the stream, yielding the last structure processed /// public static IO Last(this K ma) => ma.As() .ReduceIO(Option.None, (_, x) => Reduced.ContinueIO(Some(x))) .Bind(ma => ma switch { { IsSome: true, Case: A value } => IO.pure(value), _ => IO.empty() }); /// /// Force iteration of the stream, yielding the last structure processed /// public static K Last(this K ma) where M : MonadIO => M.LiftIOMaybe(ma.Last()); /// /// Force iteration of the stream and collect all the values into a `Seq`. /// public static IO> Collect(this K ma) => ma.As().ReduceIO>([], (xs, x) => Reduced.ContinueIO(xs.Add(x))); /// /// Force iteration of the stream and collect all the values into a `Seq`. /// public static K> Collect(this K ma) where M : MonadIO => M.LiftIOMaybe(ma.Collect()); } ================================================ FILE: LanguageExt.Streaming/Source/Operators/Source.Operators.Applicative.cs ================================================ using System; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; public static partial class SourceExtensions { extension(K self) { /// /// Applicative sequence operator /// public static Source operator >>> (K ma, K mb) => +ma.Action(mb); /// /// Applicative apply operator /// public static Source operator * (K> mf, K ma) => +mf.Apply(ma); /// /// Applicative apply operator /// public static Source operator * (K ma, K> mf) => +mf.Apply(ma); } extension(K self) { /// /// Applicative apply operator /// public static Source> operator * ( K> mf, K ma) => curry * mf * ma; /// /// Applicative apply operator /// public static Source> operator * ( K ma, K> mf) => curry * mf * ma; } extension(K self) { /// /// Applicative apply operator /// public static Source>> operator * ( K> mf, K ma) => curry * mf * ma; /// /// Applicative apply operator /// public static Source>> operator * ( K ma, K> mf) => curry * mf * ma; } extension(K self) { /// /// Applicative apply operator /// public static Source>>> operator * ( K> mf, K ma) => curry * mf * ma; /// /// Applicative apply operator /// public static Source>>> operator * ( K ma, K> mf) => curry * mf * ma; } extension(K self) { /// /// Applicative apply operator /// public static Source>>>> operator * ( K> mf, K ma) => curry * mf * ma; /// /// Applicative apply operator /// public static Source>>>> operator * ( K ma, K> mf) => curry * mf * ma; } extension(K self) { /// /// Applicative apply operator /// public static Source>>>>> operator * ( K> mf, K ma) => curry * mf * ma; /// /// Applicative apply operator /// public static Source>>>>> operator * ( K ma, K> mf) => curry * mf * ma; } extension(K self) { /// /// Applicative apply operator /// public static Source>>>>>> operator * ( K> mf, K ma) => curry * mf * ma; /// /// Applicative apply operator /// public static Source>>>>>> operator * ( K ma, K> mf) => curry * mf * ma; } extension(K self) { /// /// Applicative apply operator /// public static Source>>>>>>> operator * ( K> mf, K ma) => curry * mf * ma; /// /// Applicative apply operator /// public static Source>>>>>>> operator * ( K ma, K> mf) => curry * mf * ma; } extension(K self) { /// /// Applicative apply operator /// public static Source>>>>>>>> operator * ( K> mf, K ma) => curry * mf * ma; /// /// Applicative apply operator /// public static Source>>>>>>>> operator * ( K ma, K> mf) => curry * mf * ma; } extension(K self) { /// /// Applicative apply operator /// public static Source>>>>>>>>> operator * ( K> mf, K ma) => curry * mf * ma; /// /// Applicative apply operator /// public static Source>>>>>>>>> operator *( K ma, K> mf) => curry * mf * ma; } } ================================================ FILE: LanguageExt.Streaming/Source/Operators/Source.Operators.Choice.cs ================================================ using LanguageExt.Traits; namespace LanguageExt; public static partial class SourceExtensions { extension(K self) { public static Source operator |(K lhs, K rhs) => +lhs.Choose(rhs); public static Source operator |(K lhs, Pure rhs) => +lhs.Choose(Source.pure(rhs.Value)); } } ================================================ FILE: LanguageExt.Streaming/Source/Operators/Source.Operators.Combine.cs ================================================ using LanguageExt.Traits; namespace LanguageExt; public static partial class SourceExtensions { extension(K self) { /// /// Semigroup combine operator: an associative binary operation. /// /// Left-hand side operand /// Right-hand side operand /// public static Source operator +(K lhs, K rhs) => +lhs.Combine(rhs); /// /// Semigroup combine operator: an associative binary operation. /// /// Left-hand side operand /// Right-hand side operand /// public static Source operator +(K lhs, Pure rhs) => +lhs.Combine(Source.pure(rhs.Value)); } } ================================================ FILE: LanguageExt.Streaming/Source/Operators/Source.Operators.Functor.cs ================================================ using System; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; public static partial class SourceExtensions { extension(K _) { /// /// Functor map operator /// public static Source operator *(Func f, K ma) => +ma.Map(f); /// /// Functor map operator /// public static Source operator *(K ma, Func f) => +ma.Map(f); } extension(K _) { /// /// Functor map operator /// public static Source> operator * ( Func f, K ma) => curry(f) * ma; /// /// Functor map operator /// public static Source> operator * ( K ma, Func f) => curry(f) * ma; } extension(K _) { /// /// Functor map operator /// public static Source>> operator * ( Func f, K ma) => curry(f) * ma; /// /// Functor map operator /// public static Source>> operator * ( K ma, Func f) => curry(f) * ma; } extension(K _) { /// /// Functor map operator /// public static Source>>> operator * ( Func f, K ma) => curry(f) * ma; /// /// Functor map operator /// public static Source>>> operator * ( K ma, Func f) => curry(f) * ma; } extension(K _) { /// /// Functor map operator /// public static Source>>>> operator * ( Func f, K ma) => curry(f) * ma; /// /// Functor map operator /// public static Source>>>> operator * ( K ma, Func f) => curry(f) * ma; } extension(K _) { /// /// Functor map operator /// public static Source>>>>> operator * ( Func f, K ma) => curry(f) * ma; /// /// Functor map operator /// public static Source>>>>> operator * ( K ma, Func f) => curry(f) * ma; } extension(K _) { /// /// Functor map operator /// public static Source>>>>>> operator * ( Func f, K ma) => curry(f) * ma; /// /// Functor map operator /// public static Source>>>>>> operator * ( K ma, Func f) => curry(f) * ma; } extension(K _) { /// /// Functor map operator /// public static Source>>>>>>> operator * ( Func f, K ma) => curry(f) * ma; /// /// Functor map operator /// public static Source>>>>>>> operator * ( K ma, Func f) => curry(f) * ma; } extension(K _) { /// /// Functor map operator /// public static Source>>>>>>>> operator * ( Func f, K ma) => curry(f) * ma; /// /// Functor map operator /// public static Source>>>>>>>> operator * ( K ma, Func f) => curry(f) * ma; } extension(K _) { /// /// Functor map operator /// public static Source>>>>>>>>> operator * ( Func f, K ma) => curry(f) * ma; /// /// Functor map operator /// public static Source>>>>>>>>> operator * ( K ma, Func f) => curry(f) * ma; } } ================================================ FILE: LanguageExt.Streaming/Source/Operators/Source.Operators.Monad.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public static partial class SourceExtensions { extension(K self) { /// /// Monad bind operator /// /// Monad to bind /// Binding function /// Mapped monad public static Source operator >> (K ma, Func> f) => +ma.Bind(f); /// /// Sequentially compose two actions, discarding any value produced by the first, like sequencing operators (such /// as the semicolon) in C#. /// /// First action to run /// Second action to run /// Result of the second action public static Source operator >> (K lhs, K rhs) => lhs >> (_ => rhs); } extension(K self) { /// /// Sequentially compose two actions. The second action is a unit-returning action, so the result of the /// first action is propagated. /// /// First action to run /// Second action to run /// Result of the first action public static Source operator >> (K lhs, K rhs) => lhs >> (x => (_ => x) * rhs); } } ================================================ FILE: LanguageExt.Streaming/Source/Operators/Source.Operators.Zip.cs ================================================ using LanguageExt.Traits; namespace LanguageExt; public static partial class SourceExtensions { extension(K) { public static Source<(A First, B Second)> operator &(K lhs, K rhs) => +lhs.Zip(rhs); } extension(K) { public static Source<(A First, B Second, C Third)> operator &(K lhs, K rhs) => +lhs.Zip(rhs).Map(s => (s.First.First, s.First.Second, s.Second)); } extension(K) { public static Source<(A First, B Second, C Third, D Fourth)> operator &(K lhs, K rhs) => +lhs.Zip(rhs).Map(s => (s.First.First, s.First.Second, s.Second.First, s.Second.Second)); } extension(K) { public static Source<(A First, B Second, C Third, D Fourth)> operator &(K lhs, K rhs) => +lhs.Zip(rhs).Map(s => (s.First.First, s.First.Second, s.First.Third, s.Second)); } } ================================================ FILE: LanguageExt.Streaming/Source/Operators/Source.Operators.cs ================================================ using LanguageExt.Traits; namespace LanguageExt; public static partial class SourceExtensions { extension(K _) { /// /// Downcast operator /// public static Source operator +(K ma) => (Source)ma; /// /// Downcast operator /// public static Source operator >> (K ma, Lower lower) => +ma; } } ================================================ FILE: LanguageExt.Streaming/Source/Source.Module.cs ================================================ using System; using System.Collections.Generic; using System.Threading.Channels; using static LanguageExt.Prelude; namespace LanguageExt; public partial class Source { /// /// Empty source /// /// /// This is a 'void' source, it yields zero values. /// /// Bound value type /// Uninhabited source public static Source empty() => Source.Empty; /// /// Lift a pure value into the source /// /// /// This is a singleton/unit source, it yields exactly one value. /// /// Value to lift /// Bound value type /// Singleton source public static Source pure(A value) => new (SourceT.pure(value)); /// /// Lift a pure value into the source and yield it for infinity /// /// /// This is an infinite source, it repeatedly yields a value. /// /// Value to lift /// Bound value type /// Infinite source public static Source forever(A value) => new(SourceT.forever(value)); /// /// Make a `System.Threading.Channels.Channel` into a source of values /// /// Channel to lift /// Value type /// Source of values public static Source lift(Channel channel) => new(SourceT.lift(channel)); /// /// Make an `IEnumerable` into a source of values /// /// `IEnumerable` to lift /// Value type /// Source of values public static Source lift(IEnumerable items) => new(SourceT.lift(items)); /// /// Make an `IAsyncEnumerable` into a source of values /// /// `IAsyncEnumerable` to lift /// Value type /// Source of values public static Source lift(IAsyncEnumerable items) => new(SourceT.lift(items)); /// /// Make an `IObservable` into a source of values /// /// `IObservable` to lift /// Value type /// Source of values public static Source lift(IObservable items) => new(SourceT.lift(items)); /// /// Merge sources into a single source /// /// Sources /// Bound value type /// Source that is the combination of all provided sources public static Source merge(Seq> sources) => new(SourceT.merge(sources.Map(s => s.runSource))); /// /// Merge sources into a single source /// /// Sources /// Bound value type /// Source that is the combination of all provided sources public static Source merge(params Source[] sources) => merge(toSeq(sources)); /// /// Zip two sources into one /// /// Stream to zip with this one /// Bound value-type of the stream to zip with this one /// Stream of values where the items from two streams are paired together public Source<(A First, B Second)> zip(Source first, Source second) => new(SourceT.zip(first.runSource, second.runSource)); /// /// Zip three sources into one /// /// Stream to zip with this one /// Stream to zip with this one /// Bound value-type of the stream to zip with this one /// Stream of values where the items from two streams are paired together public Source<(A First, B Second, C Third)> zip(Source first, Source second, Source third) => new(SourceT.zip(first.runSource, second.runSource, third.runSource)); /// /// Zip three sources into one /// /// Stream to zip with this one /// Stream to zip with this one /// Stream to zip with this one /// Bound value-type of the stream to zip with this one /// Stream of values where the items from two streams are paired together public Source<(A First, B Second, C Third, D Fourth)> zip(Source first, Source second, Source third, Source fourth) => new(SourceT.zip(first.runSource, second.runSource, third.runSource, fourth.runSource)); } ================================================ FILE: LanguageExt.Streaming/Source/Source.cs ================================================ using System; using System.Threading; using System.Threading.Tasks; using LanguageExt.Pipes; using LanguageExt.Traits; namespace LanguageExt; /// /// A source / stream of values /// /// Bound value type public readonly record struct Source(SourceT runSource) : K, Monoid> { /// /// A source that never yields a value /// public static Source Empty => new (SourceT.Empty); /// /// Iterate the stream, flowing values downstream to the reducer, which aggregates a /// result value. /// /// State to reduce /// Reducer /// State type /// Reduced state public IO FoldReduce(S state, Func reducer) => +runSource.FoldReduce(state, reducer); /// /// Iterate the stream, flowing values downstream to the reducer, which aggregates a /// result value. /// /// State to reduce /// Reducer /// State type /// Reduced state public IO Reduce(S state, Reducer reducer) => +runSource.Reduce(state, reducer); /// /// Iterate the stream, flowing values downstream to the reducer, which aggregates a /// result value. /// /// State to reduce /// Reducer /// State type /// Reduced state public IO ReduceIO(S state, ReducerIO reducer) => +runSource.ReduceM(state, (s, v) => reducer(s, v)); /// /// Iterate the stream, flowing values downstream to the reducer, which aggregates a /// result value. /// /// State to reduce /// Reducer /// State type /// Reduced state public IO ReduceM(S state, ReducerIO reducer) => +runSource.ReduceM(state, (s, v) => reducer(s, v)); /// /// Functor map /// public Source Map(Func f) => new(runSource.Map(f)); /// /// Monad bind /// public Source Bind(Func> f) => new(runSource.Bind(x => f(x).runSource)); /// /// Monad bind /// public Source Bind(Func> f) => new(runSource.Bind(x => f(x).As().runSource)); /// /// Filter values. Yielding downstream when `true` /// /// Filter function /// Source where the only values yield are those that pass the predicate public Source Where(Func f) => new(runSource.Where(f)); /// /// Filter values. Yielding downstream when `true` /// /// Filter function /// Source where the only values yield are those that pass the predicate public Source Filter(Func f) => new(runSource.Filter(f)); /// /// Applicative apply /// public Source ApplyBack(Source> ff) => new(runSource.ApplyBack(ff.runSource)); /// /// Concatenate two sources into a single source. /// /// Left-hand side /// Right-hand side /// Concatenated stream of values public Source Combine(Source rhs) => new(runSource.Combine(rhs.runSource)); /// /// The value streams are both merged into a new stream. Values are yielded /// as they become available. /// /// Left-hand side /// Right-hand side /// Merged stream public Source Choose(Source rhs) => new(runSource.Choose(rhs.runSource)); /// /// The value streams are both merged into a new stream. Values are yielded /// as they become available. /// /// Left-hand side /// Right-hand side /// Merged stream public Source Choose(Memo rhs) => new(runSource.Choose(rhs.Lower().Value.As().runSource)); /// /// Zip two sources into one /// /// Stream to zip with this one /// Bound value-type of the stream to zip with this one /// Stream of values where the items from two streams are paired together public Source<(A First, B Second)> Zip(Source second) => new (runSource.Zip(second.runSource)); /// /// Zip three sources into one /// /// Stream to zip with this one /// Stream to zip with this one /// Bound value-type of the stream to zip with this one /// Stream of values where the items from two streams are paired together public Source<(A First, B Second, C Third)> Zip(Source second, Source third) => new (runSource.Zip(second.runSource, third.runSource)); /// /// Zip three sources into one /// /// Stream to zip with this one /// Stream to zip with this one /// Stream to zip with this one /// Bound value-type of the stream to zip with this one /// Stream of values where the items from two streams are paired together public Source<(A First, B Second, C Third, D Fourth)> Zip(Source second, Source third, Source fourth) => new (runSource.Zip(second.runSource, third.runSource, fourth.runSource)); /// /// Skip items in the source /// /// Amount to skip /// Transformed source public Source Skip(int amount) => new (runSource.Skip(amount)); /// /// Limit the number of items processed /// /// Amount to take /// Transformed source public Source Take(int amount) => new (runSource.Take(amount)); /// /// Fold the values flowing through. A value is only yielded downstream upon completion of the stream. /// /// Binary operator /// Initial state /// State type /// Stream of aggregate state public Source Fold(Func Fold, S Init) => new (runSource.Fold(Fold, Init)); /// /// Fold the values flowing through. Values are yielded downstream when either the schedule expires, or the /// source completes. /// /// Schedule to control the rate of processing /// Binary operator /// Initial state /// State type /// Stream of aggregate states public Source Fold(Schedule Time, Func Fold, S Init) => new (runSource.Fold(Time, Fold, Init)); /// /// Fold the values flowing through. Values are yielded downstream when either the predicate returns /// `false`, or the source completes. /// /// Binary operator /// Predicate /// Initial state /// State type /// Stream of aggregate states public Source FoldWhile(Func Fold, Func<(S State, A Value), bool> Pred, S Init) => new (runSource.FoldWhile(Fold, Pred, Init)); /// /// Fold the values flowing through. Values are yielded downstream when either the predicate returns /// `true`, or the source completes. /// /// Binary operator /// Predicate /// Initial state /// State type /// Stream of aggregate states public Source FoldUntil(Func Fold, Func<(S State, A Value), bool> Pred, S Init) => new(runSource.FoldUntil(Fold, Pred, Init)); /// /// Fold the values flowing through. Values are yielded downstream when either the schedule expires, the /// predicate returns `false`, or the source completes. /// /// Schedule to control the rate of processing /// Binary operator /// Predicate /// Initial state /// State type /// Stream of aggregate states public Source FoldWhile( Schedule Time, Func Fold, Func<(S State, A Value), bool> Pred, S Init) => new (runSource.FoldWhile(Time, Fold, Pred, Init)); /// /// Fold the values flowing through. Values are yielded downstream when either the schedule expires, the /// predicate returns `true`, or the source completes. /// /// Schedule to control the rate of processing /// Binary operator /// Predicate /// Initial state /// /// Stream of aggregate states public Source FoldUntil( Schedule Time, Func Fold, Func<(S State, A Value), bool> Pred, S Init) => new (runSource.FoldUntil(Time, Fold, Pred, Init)); /// /// Transform with a transducer /// /// Transducer to use to transform /// Target bound value type /// Transformed source public Source Transform(Transducer transducer) => new(runSource.Transform(transducer)); /// /// Transform with a transducer /// /// Transducer to use to transform /// Target bound value type /// Transformed source public Source Transform(TransducerM transducer) => new(runSource.Transform(transducer)); /// /// Convert `Source` to a `ProducerT` pipe component /// /// Monad to lift (must support `IO`) /// `ProducerT` public ProducerT ToProducerT() where M : MonadIO => ProducerT.yieldAll(this); /// /// Convert `Source` to a `Producer` pipe component /// /// `Producer` public Producer ToProducer() => ToProducerT>(); /// /// Functor map /// public Source Select(Func f) => Map(f); /// /// Monad bind /// public Source SelectMany(Func> bind, Func project) => Bind(a => bind(a).Map(b => project(a, b))); } ================================================ FILE: LanguageExt.Streaming/Source/Trait/Source.TraitImpl.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public partial class Source : Monad, MonoidK, Alternative { static K Monad.Bind(K ma, Func> f) => ma.As().Bind(f); static K Monad.Recur(A value, Func>> f) => Monad.unsafeRecur(value, f); static K Functor.Map(Func f, K ma) => ma.As().Map(f); static K Applicative.Pure(A value) => new Source(SourceT.pure(value)); static K Applicative.Apply(K> mf, K ma) => ma.As().ApplyBack(mf.As()); static K Applicative.Apply(K> mf, Memo ma) => ma.Value.As().ApplyBack(mf.As()); static K SemigroupK.Combine(K fa, K fb) => fa.As().Combine(fb.As()); static K Choice.Choose(K fa, K fb) => fa.As().Choose(fb.As()); public static K Choose(K fa, Memo fb) => fa.As().Choose(fb); static K Alternative.Empty() => Source.Empty; static K MonoidK.Empty() => Source.Empty; } ================================================ FILE: LanguageExt.Streaming/SourceT/DSL/ApplySourceT.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; /* record ApplySourceT(SourceT> FF, SourceT FA) : SourceT where M : MonadIO, Fallible { public override K> ReduceInternalM(S state, ReducerM, S> reducer) => new Zip2SourceT, A>(FF, FA).Map(p => p.First(p.Second)).ReduceInternalM(state, reducer); } record ApplySourceT2(SourceT> FF, Memo, A> FA) : SourceT where M : MonadIO, Fallible { public override K> ReduceInternalM(S state, ReducerM, S> reducer) => new Zip2SourceT, A>(FF, FA.Value.As()).Map(p => p.First(p.Second)).ReduceInternalM(state, reducer); } */ ================================================ FILE: LanguageExt.Streaming/SourceT/DSL/BindSourceT.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; record BindSourceT(SourceT Source, Func> F) : SourceT where M : MonadIO { internal override K> ReduceInternalM(S state, ReducerM, S> reducer) => Source.ReduceInternalM(state, (s, mx) => mx.Bind(x => F(x).ReduceInternalM(s, reducer))); } ================================================ FILE: LanguageExt.Streaming/SourceT/DSL/ChooseSourceT.cs ================================================ using System; using System.Threading; using LanguageExt.Traits; using System.Threading.Channels; using LanguageExt.Common; using static LanguageExt.Prelude; namespace LanguageExt; record ChooseSourceT(Seq> Sources) : SourceT where M : MonadIO, Fallible { internal override K> ReduceInternalM(S state, ReducerM, S> reducer) => M.BracketIOMaybe( // Get our environment from envIO in IO.token // Create a channel for the merged streams from channel in M.Pure(Channel.CreateUnbounded>()) let writer = channel.Writer // For each stream, reduce it (which writes to the merged stream), fork it, and return // This gives us K that we can't run directly, so we must bind it... from signal in useMaybe(Signal.countdown(Sources.Count)) let trigger = trigger(signal, writer) let error = error(signal, writer) from forks in useMaybe(Sources.Map(s => s.ReduceInternalM(unit, (_, ma) => M.Pure(writeAsync(writer, ma))) .Bind(_ => trigger) .Catch(error) .ForkIOMaybe()) .Sequence() .Map(ForkRelease.New)) // Reduce the merged stream from result in SourceT.liftM(channel).ReduceInternalM(state, reducer) // Make sure the forks are shutdown from _ in forks.Cancel() select result); static Func> error(CountdownSignal signal, ChannelWriter> writer) => err => signal.Count .Bind(signal.Trigger) .Map(_ => writer.TryComplete()) .Bind(_ => M.Fail(err)); static K trigger(CountdownSignal signal, ChannelWriter> writer) => // Mark channel completed if all sources are complete (f => ignore(f && writer.TryComplete())) * signal.Trigger(); static Reduced writeAsync(ChannelWriter> writer, K value) => writer.TryWrite(value) ? Reduced.Unit : Reduced.Done(unit); class ForkRelease(K> Forks) : IDisposable { int disposed; public static ForkRelease New(K> forks) => new (forks); public IO Cancel() => IO.lift(e => { if (Interlocked.CompareExchange(ref disposed, 1, 0) == 0) { foreach (var fork in +Forks) { fork.Cancel.Run(e); } } return unit; }); public void Dispose() => Cancel().Run(); } } ================================================ FILE: LanguageExt.Streaming/SourceT/DSL/CombineSourceT.cs ================================================ using LanguageExt.Traits; namespace LanguageExt; record CombineSourceT(Seq> Sources) : SourceT where M : MonadIO { internal override K> ReduceInternalM(S state, ReducerM, S> reducer) { return go(state, Sources); K> go(S state, Seq> sources) => sources.Tail.IsEmpty ? sources[0].ReduceInternalM(state, reducer) : sources[0].ReduceInternalM(state, reducer) .Bind(ns => ns.Continue ? go(ns.Value, sources.Tail) : M.Pure(ns)); } } ================================================ FILE: LanguageExt.Streaming/SourceT/DSL/DoneSourceT.cs ================================================ using LanguageExt.Traits; namespace LanguageExt; record DoneSourceT : SourceT where M : MonadIO { internal override K> ReduceInternalM(S state, ReducerM, S> reducer) => M.Pure(Reduced.Done(state)); } ================================================ FILE: LanguageExt.Streaming/SourceT/DSL/EmptySourceT.cs ================================================ using LanguageExt.Traits; namespace LanguageExt; record EmptySourceT : SourceT where M : MonadIO { public static readonly SourceT Default = new EmptySourceT(); internal override K> ReduceInternalM(S state, ReducerM, S> reducer) => M.Pure(Reduced.Done(state)); } ================================================ FILE: LanguageExt.Streaming/SourceT/DSL/FilterSourceT.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; record FilterSourceT(SourceT Source, Func Predicate) : SourceT where M : MonadIO { internal override K> ReduceInternalM(S state, ReducerM, S> reducer) => Source.ReduceInternal(state, (s, x) => Predicate(x) ? reducer(s, M.Pure(x)) : M.Pure(Reduced.Continue(s))); } ================================================ FILE: LanguageExt.Streaming/SourceT/DSL/FoldUntilSourceT.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; record FoldUntilSourceT( SourceT Source, Schedule Schedule, Func Folder, Func<(S State, A Value), bool> Pred, S State) : SourceT where M : MonadIO { // TODO: Schedule internal override K> ReduceInternalM(S1 state, ReducerM, S1> reducer) => Source.ReduceInternalM((FState: State, IState: state), (rs, ma) => from a in ma let ns = Folder(rs.FState, a) from r in Pred((ns, a)) ? reducer(rs.IState, M.Pure(ns)).Map(s1 => s1.Map(s2 => (ns, s2))) : M.Pure(Reduced.Continue((ns, rs.IState))) select r) .Map(s => s.Map(s1 => s1.IState)); } ================================================ FILE: LanguageExt.Streaming/SourceT/DSL/FoldWhileSourceT.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; record FoldWhileSourceT( SourceT Source, Schedule Schedule, Func Folder, Func<(S State, A Value), bool> Pred, S State) : SourceT where M : MonadIO { // TODO: Schedule internal override K> ReduceInternalM(S1 state, ReducerM, S1> reducer) => Source.ReduceInternalM((FState: State, IState: state), (s, ma) => ma >> (a => Pred((s.FState, a)) ? M.Pure(Reduced.Continue((Folder(s.FState, a), s.IState))) : reducer(s.IState, M.Pure(s.FState)) .Map(s1 => s1.Map(s2 => (s.FState, s2))))) .Map(s => s.Map(s1 => s1.IState)); } ================================================ FILE: LanguageExt.Streaming/SourceT/DSL/FoldablePureSourceT.cs ================================================ using LanguageExt.Traits; namespace LanguageExt; record FoldablePureSourceT(K Items) : SourceT where M : MonadIO where F : Foldable { internal override K> ReduceInternalM(S state, ReducerM, S> reducer) { return steps() >> (f => Monad.recur(f, go)); IO> steps() => // Needs to remain lazy IO.lift(e => Items.FoldStep(state)); K, Reduced>> go(Fold fold) => IO.token.Bind(t => t.IsCancellationRequested ? done(state) : fold switch { Fold.Done(var s) => done(s), Fold.Loop(var s, var a, var n) => reducer(s, M.Pure(a)) * (ns => ns.Continue ? next(n(ns.Value)) : reduced(ns)) }); K, Reduced>> done(S state) => M.Pure(reduced(Reduced.Done(state))); Next, Reduced> reduced(Reduced reduced) => Next.Done, Reduced>(reduced); Next, Reduced> next(Fold tail) => Next.Loop, Reduced>(tail); } } ================================================ FILE: LanguageExt.Streaming/SourceT/DSL/FoldableSourceT.cs ================================================ using LanguageExt.Traits; namespace LanguageExt; record FoldableSourceT(K> Items) : SourceT where M : MonadIO where F : Foldable { internal override K> ReduceInternalM(S state, ReducerM, S> reducer) { return steps() >> (f => Monad.recur(f, go)); IO, S>> steps() => // Needs to remain lazy IO.lift(e => Items.FoldStep(state)); K, S>, Reduced>> go(Fold, S> fold) => IO.token.Bind(t => t.IsCancellationRequested ? done(state) : fold switch { Fold, S>.Done(var s) => done(s), Fold, S>.Loop(var s, var ma, var n) => reducer(s, ma) * (ns => ns.Continue ? next(n(ns.Value)) : reduced(ns)) }); K, S>, Reduced>> done(S state) => M.Pure(reduced(Reduced.Done(state))); Next, S>, Reduced> reduced(Reduced reduced) => Next.Done, S>, Reduced>(reduced); Next, S>, Reduced> next(Fold, S> tail) => Next.Loop, S>, Reduced>(tail); } } ================================================ FILE: LanguageExt.Streaming/SourceT/DSL/ForeverSourceT.cs ================================================ using LanguageExt.Traits; namespace LanguageExt; record ForeverSourceT(K Value) : SourceT where M : MonadIO { internal override K> ReduceInternalM(S initialState, ReducerM, S> reducer) { return Monad.recur(initialState, go); K>> go(S state) => reducer(state, Value) * (rs => rs switch { { Continue: true, Value: var s } => Next.Loop>(s), _ => Next.Done>(rs) }); } } ================================================ FILE: LanguageExt.Streaming/SourceT/DSL/IteratorAsyncSourceT.cs ================================================ using System.Collections.Generic; using LanguageExt.Traits; namespace LanguageExt; record IteratorAsyncSourceT(IAsyncEnumerable> Items) : SourceT where M : MonadIO { internal override K> ReduceInternalM(S state, ReducerM, S> reducer) { return Monad.recur((state, Items.GetIteratorAsync()), go); K> iter), Reduced>> go((S state, IteratorAsync> iter) self) => isDone(self) >> (d => d ? done(self.state) : reduce(self.iter) >> (ns => ns.Continue ? next(ns.Value, tail(self.iter)) : reduced(ns))); K> reduce(IteratorAsync> iter) => IO.liftVAsync(_ => iter.Head).Bind(h => reducer(state, h)); IO isDone((S state, IteratorAsync> iter) self) => IO.liftVAsync(async e => e.Token.IsCancellationRequested || await self.iter.IsEmpty); K> iter), Reduced>> done(S state) => reduced(Reduced.Done(state)); K> iter), Reduced>> reduced(Reduced reduced) => M.Pure(Next.Done<(S state, IteratorAsync> iter), Reduced>(reduced)); K>> tail(IteratorAsync> iter) => M.LiftIO(IO.liftVAsync(_ => iter.Tail).Map(i => i.Split())); K> iter), Reduced>> next(S state, K>> tail) => tail.Map(t => Next.Loop<(S state, IteratorAsync> iter), Reduced>((state, t))); } } ================================================ FILE: LanguageExt.Streaming/SourceT/DSL/IteratorSyncSourceT.cs ================================================ using System.Collections.Generic; using LanguageExt.Traits; namespace LanguageExt; record IteratorSyncSourceT(IEnumerable> Items) : SourceT where M : MonadIO { internal override K> ReduceInternalM(S state, ReducerM, S> reducer) { return Monad.recur((state, Items.GetIterator()), go); K> iter), Reduced>> go((S state, Iterator> iter) self) => isDone(self) >> (d => d ? done(self.state) : reducer(state, self.iter.Head) >> (ns => ns.Continue ? next(ns.Value, self.iter.Tail) : reduced(ns))); IO isDone((S state, Iterator> iter) self) => IO.lift(e => e.Token.IsCancellationRequested || self.iter.IsEmpty); K> iter), Reduced>> done(S state) => reduced(Reduced.Done(state)); K> iter), Reduced>> reduced(Reduced reduced) => M.Pure(Next.Done<(S state, Iterator> iter), Reduced>(reduced)); K> iter), Reduced>> next(S state, Iterator> tail) => M.Pure(Next.Loop<(S state, Iterator> iter), Reduced>((state, tail.Split()))); } } ================================================ FILE: LanguageExt.Streaming/SourceT/DSL/LiftSourceT.cs ================================================ using LanguageExt.Traits; namespace LanguageExt; record LiftSourceT(K Value) : SourceT where M : MonadIO { internal override K> ReduceInternalM(S state, ReducerM, S> reducer) => reducer(state, Value); } ================================================ FILE: LanguageExt.Streaming/SourceT/DSL/MapIOSourceT.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; record MapIOSourceT(SourceT Source, Func, IO> F) : SourceT where M : MonadIO { internal override K> ReduceInternalM(S state, ReducerM, S> reducer) => Source.ReduceInternalM(state, (s, ma) => reducer(s, M.MapIOMaybe(ma, F))); } ================================================ FILE: LanguageExt.Streaming/SourceT/DSL/MapSourceT.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; record MapSourceT(SourceT Source, Func F) : SourceT where M : MonadIO { internal override K> ReduceInternalM(S state, ReducerM, S> reducer) => Source.ReduceInternalM(state, (s, mx) => reducer(s, mx.Map(F))); } ================================================ FILE: LanguageExt.Streaming/SourceT/DSL/MultiListenerPureSourceT.cs ================================================ using System; using System.Threading; using LanguageExt.Common; using LanguageExt.Traits; using System.Threading.Tasks; using System.Threading.Channels; using static LanguageExt.Prelude; using System.Collections.Concurrent; namespace LanguageExt; record MultiListenerPureSourceT(Channel Source) : SourceT where M : MonadIO, Fallible { volatile int count; readonly ConcurrentDictionary>, Unit> listeners = new(); readonly CancellationTokenSource tokenSource = new(); internal override K> ReduceInternalM(S state, ReducerM, S> reducer) { return from channel in mkChannel let final = final(channel) from result in body(channel, state) .Bind(final.ConstMap) // Run final if we succeed .Catch(error(channel)) // Run final if we fail and keep the original state select result; K> body(Channel> channel, S state) => from _ in addListener(channel) >> startup from st in readAll(channel, reducer, state) >> final(channel) select st; } IO>> mkChannel => IO.lift(_ => Channel.CreateUnbounded>()); IO addListener(Channel> channel) => IO.lift(_ => ignore(listeners.TryAdd(channel, unit))); IO startup => IO.lift(_ => { if (Interlocked.Increment(ref count) == 1) { // We let the Task run without awaiting, because we don't want to block ignore(startupAsync()); } return unit; }); async Task startupAsync() { try { var token = tokenSource.Token; while (await Source.Reader.WaitToReadAsync(token)) { if (token.IsCancellationRequested) { return; } var x = await Source.Reader.ReadAsync(token); foreach (var listener in listeners.Keys) { if (await listener.Writer.WaitToWriteAsync(token)) { await listener.Writer.WriteAsync(M.Pure(x), token); } } } } catch (Exception e) { foreach (var listener in listeners.Keys) { listener.Writer.TryComplete(e); } } finally { foreach (var listener in listeners.Keys) { listener.Writer.TryComplete(); } } } K> readAll(Channel> channel, ReducerM, S> reducer, S initialState) { return Monad.recur(initialState, go); K>> go(S state) => IO.token >> (t => t.IsCancellationRequested ? complete(state) : waitToRead() >> (available => available ? reduce(state) : complete(state))); K>> reduce(S state) => read() >> (head => reducer(state, head) >> next); K>> next(Reduced reduced) => reduced.Continue ? M.Pure(Next.Loop>(reduced.Value)) : M.Pure(Next.Done>(reduced)); IO waitToRead() => IO.liftVAsync(e => channel.Reader.WaitToReadAsync(e.Token)); IO> read() => IO.liftVAsync(e => channel.Reader.ReadAsync(e.Token)); K>> complete(S state) => M.Pure(Next.Done>(Reduced.Done(state))); } K final(Channel> channel) => M.LiftIO( IO.liftVAsync(async e => { listeners.TryRemove(channel, out _); if (Interlocked.Decrement(ref count) == 0) { await shutdown(); } return unit; })); Func>> error(Channel> channel) => err => final(channel) >> M.Fail>(err); Task shutdown() => tokenSource.CancelAsync(); } ================================================ FILE: LanguageExt.Streaming/SourceT/DSL/MultiListenerSourceT.cs ================================================ using System; using System.Collections.Concurrent; using System.Threading; using System.Threading.Channels; using System.Threading.Tasks; using LanguageExt.Common; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; record MultiListenerSourceT(Channel> Source) : SourceT where M : MonadIO, Fallible { volatile int count; readonly ConcurrentDictionary>, Unit> listeners = new(); readonly CancellationTokenSource tokenSource = new(); internal override K> ReduceInternalM(S state, ReducerM, S> reducer) { return from channel in mkChannel let final = final(channel) from result in body(channel, state) .Bind(final.ConstMap) // Run final if we succeed .Catch(error(channel)) // Run final if we fail and rethrow select result; K> body(Channel> channel, S state) => from _ in addListener(channel) >> startup from st in readAll(channel, reducer, state) >> final(channel) select st; } IO>> mkChannel => IO.lift(_ => Channel.CreateUnbounded>()); IO addListener(Channel> channel) => IO.lift(_ => ignore(listeners.TryAdd(channel, unit))); IO startup => IO.lift(_ => { if (Interlocked.Increment(ref count) == 1) { // We let the Task run without awaiting, because we don't want to block ignore(startupAsync()); } return unit; }); async Task startupAsync() { try { var token = tokenSource.Token; while (await Source.Reader.WaitToReadAsync(token)) { if (token.IsCancellationRequested) { return; } var x = await Source.Reader.ReadAsync(token); foreach (var listener in listeners.Keys) { if (await listener.Writer.WaitToWriteAsync(token)) { await listener.Writer.WriteAsync(x, token); } } } } catch (Exception e) { foreach (var listener in listeners.Keys) { listener.Writer.TryComplete(e); } } finally { foreach (var listener in listeners.Keys) { listener.Writer.TryComplete(); } } } K> readAll(Channel> channel, ReducerM, S> reducer, S initialState) { return Monad.recur(initialState, go); K>> go(S state) => IO.token >> (t => t.IsCancellationRequested ? complete(state) : waitToRead() >> (available => available ? reduce(state) : complete(state))); K>> reduce(S state) => read() >> (head => reducer(state, head) * next); Next> next(Reduced reduced) => reduced.Continue ? Next.Loop>(reduced.Value) : Next.Done>(reduced); IO waitToRead() => IO.liftVAsync(e => channel.Reader.WaitToReadAsync(e.Token)); IO> read() => IO.liftVAsync(e => channel.Reader.ReadAsync(e.Token)); K>> complete(S state) => M.Pure(Next.Done>(Reduced.Done(state))); } K final(Channel> channel) => M.LiftIO( IO.liftVAsync(async e => { listeners.TryRemove(channel, out _); if (Interlocked.Decrement(ref count) == 0) { await shutdown(); } return unit; })); Func>> error(Channel> channel) => err => final(channel) >> M.Fail>(err); Task shutdown() => tokenSource.CancelAsync(); } ================================================ FILE: LanguageExt.Streaming/SourceT/DSL/ObservablePureSourceT.cs ================================================ using System; using System.Threading.Tasks; using LanguageExt.Traits; using System.Threading; namespace LanguageExt; record ObservablePureSourceT(IObservable Items) : SourceT where M : MonadIO { internal override K> ReduceInternalM(S state, ReducerM, S> reducer) { return M.LiftIO(IO.liftVAsync(e => go(state, Items.ToAsyncEnumerable(e.Token).GetIteratorAsync(), e.Token))).Flatten(); async ValueTask>> go(S state, IteratorAsync iter, CancellationToken token) { if(token.IsCancellationRequested) return M.Pure(Reduced.Done(state)); if (await iter.IsEmpty) return M.Pure(Reduced.Done(state)); var head = await iter.Head; var tail = (await iter.Tail).Split(); return reducer(state, M.Pure(head)) >> (ns => ns.Continue ? go(ns.Value, tail, token).GetAwaiter().GetResult() : M.Pure(ns)); } } } ================================================ FILE: LanguageExt.Streaming/SourceT/DSL/ObservableSourceT.cs ================================================ using System; using System.Threading.Tasks; using LanguageExt.Traits; using System.Threading; namespace LanguageExt; record ObservableSourceT(IObservable> Items) : SourceT where M : MonadIO { internal override K> ReduceInternalM(S state, ReducerM, S> reducer) { return M.LiftIO(IO.liftVAsync(e => go(state, Items.ToAsyncEnumerable(e.Token).GetIteratorAsync(), e.Token))).Flatten(); async ValueTask>> go(S state, IteratorAsync> iter, CancellationToken token) { if(token.IsCancellationRequested) return M.Pure(Reduced.Done(state)); if (await iter.IsEmpty) return M.Pure(Reduced.Done(state)); var head = await iter.Head; var tail = (await iter.Tail).Split(); return reducer(state, head) >> (ns => ns.Continue ? go(ns.Value, tail, token).GetAwaiter().GetResult() : M.Pure(ns)); } } } ================================================ FILE: LanguageExt.Streaming/SourceT/DSL/PureSourceT.cs ================================================ using LanguageExt.Traits; namespace LanguageExt; record PureSourceT(A Value) : SourceT where M : MonadIO { internal override K> ReduceInternalM(S state, ReducerM, S> reducer) => reducer(state, M.Pure(Value)); } ================================================ FILE: LanguageExt.Streaming/SourceT/DSL/Reader2SourceT.cs ================================================ using System.Threading; using System.Threading.Channels; using System.Threading.Tasks; using LanguageExt.Traits; namespace LanguageExt; record Reader2SourceT(Channel> ChannelA, Channel> ChannelB) : SourceT where M : MonadIO { internal override K> ReduceInternalM(S state, ReducerM, S> reducer) { return M.LiftIO(IO.liftVAsync(e => go(state, e.Token))).Flatten(); async ValueTask>> go(S state, CancellationToken token) { if(token.IsCancellationRequested) return M.Pure(Reduced.Done(state)); var fa = ChannelA.Reader.WaitToReadAsync(token).AsTask(); var fb = ChannelB.Reader.WaitToReadAsync(token).AsTask(); await Task.WhenAll(fa, fb); if (!fa.Result || !fb.Result) return M.Pure(Reduced.Done(state)); var ta = ChannelA.Reader.ReadAsync(token).AsTask(); var tb = ChannelB.Reader.ReadAsync(token).AsTask(); await Task.WhenAll(ta, tb); return reducer(state, ta.Result.Zip(tb.Result)) >> (ns => ns.Continue ? go(ns.Value, token).GetAwaiter().GetResult() : M.Pure(ns)); } } } ================================================ FILE: LanguageExt.Streaming/SourceT/DSL/Reader3SourceT.cs ================================================ using System.Threading; using System.Threading.Channels; using System.Threading.Tasks; using LanguageExt.Traits; namespace LanguageExt; record Reader3SourceT( Channel> ChannelA, Channel> ChannelB, Channel> ChannelC) : SourceT where M : MonadIO { internal override K> ReduceInternalM(S state, ReducerM, S> reducer) { return M.LiftIO(IO.liftVAsync(e => go(state, e.Token))).Flatten(); async ValueTask>> go(S state, CancellationToken token) { if(token.IsCancellationRequested) return M.Pure(Reduced.Done(state)); var fa = ChannelA.Reader.WaitToReadAsync(token).AsTask(); var fb = ChannelB.Reader.WaitToReadAsync(token).AsTask(); var fc = ChannelC.Reader.WaitToReadAsync(token).AsTask(); await Task.WhenAll(fa, fb, fc); if (!fa.Result || !fb.Result || !fc.Result) return M.Pure(Reduced.Done(state)); var ta = ChannelA.Reader.ReadAsync(token).AsTask(); var tb = ChannelB.Reader.ReadAsync(token).AsTask(); var tc = ChannelC.Reader.ReadAsync(token).AsTask(); await Task.WhenAll(ta, tb, tc); return reducer(state, ta.Result.Zip(tb.Result, tc.Result)) >> (ns => ns.Continue ? go(ns.Value, token).GetAwaiter().GetResult() : M.Pure(ns)); } } } ================================================ FILE: LanguageExt.Streaming/SourceT/DSL/Reader4SourceT.cs ================================================ using System.Threading; using System.Threading.Channels; using System.Threading.Tasks; using LanguageExt.Traits; namespace LanguageExt; record Reader4SourceT( Channel> ChannelA, Channel> ChannelB, Channel> ChannelC, Channel> ChannelD) : SourceT where M : MonadIO { internal override K> ReduceInternalM(S state, ReducerM, S> reducer) { return M.LiftIO(IO.liftVAsync(e => go(state, e.Token))).Flatten(); async ValueTask>> go(S state, CancellationToken token) { if(token.IsCancellationRequested) return M.Pure(Reduced.Done(state)); var fa = ChannelA.Reader.WaitToReadAsync(token).AsTask(); var fb = ChannelB.Reader.WaitToReadAsync(token).AsTask(); var fc = ChannelC.Reader.WaitToReadAsync(token).AsTask(); var fd = ChannelD.Reader.WaitToReadAsync(token).AsTask(); await Task.WhenAll(fa, fb, fc, fd); if (!fa.Result || !fb.Result || !fc.Result || !fd.Result) return M.Pure(Reduced.Done(state)); var ta = ChannelA.Reader.ReadAsync(token).AsTask(); var tb = ChannelB.Reader.ReadAsync(token).AsTask(); var tc = ChannelC.Reader.ReadAsync(token).AsTask(); var td = ChannelD.Reader.ReadAsync(token).AsTask(); await Task.WhenAll(ta, tb, tc, td); return reducer(state, ta.Result.Zip(tb.Result, tc.Result, td.Result)) >> (ns => ns.Continue ? go(ns.Value, token).GetAwaiter().GetResult() : M.Pure(ns)); } } } ================================================ FILE: LanguageExt.Streaming/SourceT/DSL/SourcePureSourceT.cs ================================================ using LanguageExt.Traits; namespace LanguageExt; record SourcePureSourceT(Source Source) : SourceT where M : MonadIO { internal override K> ReduceInternalM(S state, ReducerM, S> reducer) => M.LiftIO(Source.Reduce( M.Pure(Reduced.Continue(state)), (ms, a) => Reduced.Continue(ms >> (ns => ns.Continue ? reducer(ns.Value, M.Pure(a)) : M.Pure(ns))))) .Flatten(); } ================================================ FILE: LanguageExt.Streaming/SourceT/DSL/SourceSourceT.cs ================================================ using LanguageExt.Traits; namespace LanguageExt; record SourceSourceT(Source> Source) : SourceT where M : MonadIO { internal override K> ReduceInternalM(S state, ReducerM, S> reducer) => M.LiftIO(Source.Reduce( M.Pure(Reduced.Continue(state)), (ms, ma) => Reduced.Continue(ms >> (ns => ns.Continue ? reducer(ns.Value, ma) : M.Pure(ns))))) .Flatten(); } ================================================ FILE: LanguageExt.Streaming/SourceT/DSL/TakeForSourceT.cs ================================================ using System; using LanguageExt.Common; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; record TakeForSourceT(SourceT Source, TimeSpan Duration) : SourceT where M : MonadIO, Fallible { internal override K> ReduceInternalM(S state, ReducerM, S> reducer) => from sofar in atomIO(state) from value in M.TimeoutIOMaybe(reduce(state, reducer, sofar), Duration) | @catch(ErrorCodes.TimedOut, timedOut(sofar)) | @catch(ErrorCodes.Cancelled, timedOut(sofar)) select value; K> timedOut(Atom state) => M.LiftIO(state.ValueIO).Map(Reduced.Done); K> reduce(S state, ReducerM, S> reducer, Atom sofar) => Source.ReduceInternalM( state, (s, ma) => from ns in reducer(s, ma) from _ in sofar.SwapIO(_ => ns.Value) select ns); } ================================================ FILE: LanguageExt.Streaming/SourceT/DSL/TakeSourceT.cs ================================================ using LanguageExt.Traits; namespace LanguageExt; record TakeSourceT(SourceT Source, int Count) : SourceT where M : MonadIO { internal override K> ReduceInternalM(S state, ReducerM, S> reducer) { var remaining = Count; return Source.ReduceInternalM(state, (s, ma) => { if (remaining < 1) return M.Pure(Reduced.Done(s)); remaining--; return remaining == 0 ? reducer(s, ma).Map(n => Reduced.Done(n.Value)) : reducer(s, ma); }); } } ================================================ FILE: LanguageExt.Streaming/SourceT/DSL/ToIOSourceT.cs ================================================ using LanguageExt.Traits; namespace LanguageExt; record ToIOSourceT(SourceT Source) : SourceT> where M : MonadIO { internal override K> ReduceInternalM(S state, ReducerM>, S> reducer) => Source.ReduceInternalM(state, (s, mx) => reducer(s, M.ToIOMaybe(mx))); } ================================================ FILE: LanguageExt.Streaming/SourceT/DSL/TransformSourceT.cs ================================================ using LanguageExt.Traits; namespace LanguageExt; record TransformSourceT(SourceT Source, TransducerM Transducer) : SourceT where M : MonadIO { internal override K> ReduceInternalM(S state, ReducerM, S> reducer) { var t = Transducer.Reduce((s1, b) => reducer(s1, M.Pure(b))); return Source.ReduceInternalM(state, (s, ma) => ma.Bind(a => t(s, a))); } } record TransformSourceT2(SourceT Source, Transducer Transducer) : SourceT where M : MonadIO { readonly TransducerM TransducerM = Transducer.Lift(); internal override K> ReduceInternalM(S state, ReducerM, S> reducer) { var t = TransducerM.Reduce((S s1, B b) => reducer(s1, M.Pure(b))); return Source.ReduceInternalM(state, (s, ma) => ma.Bind(a => t(s, a))); } } ================================================ FILE: LanguageExt.Streaming/SourceT/DSL/Zip2SourceT.cs ================================================ using System; using LanguageExt.Traits; using System.Threading.Channels; using LanguageExt.Common; using static LanguageExt.Prelude; namespace LanguageExt; record Zip2SourceT(SourceT SourceA, SourceT SourceB) : SourceT where M : MonadIO, Fallible { internal override K> ReduceInternalM(S state, ReducerM, S> reducer) => // Create channels that receive the values yielded by the two sources from channelA in M.Pure(Channel.CreateUnbounded>()) from channelB in M.Pure(Channel.CreateUnbounded>()) let writerA = channelA.Writer let writerB = channelB.Writer // Triggers which signal when a channel has completed let triggerA = trigger(writerA) let triggerB = trigger(writerB) let error = error(writerA, writerB) // Create a forked first channel from forkA in SourceA.ReduceInternalM(unit, (_, ma) => writeAsync(writerA, ma)) .Bind(_ => triggerA) .Catch(error) .ForkIOMaybe() // Create a forked second channel from forkB in SourceB.ReduceInternalM(unit, (_, ma) => writeAsync(writerB, ma)) .Bind(_ => triggerB) .Catch(error) .ForkIOMaybe() let forks = Seq(forkA, forkB) // Then create a reader iterator that will yield the merged values from result in new Reader2SourceT(channelA, channelB).ReduceInternalM(state, reducer) // Make sure the forks are shutdown from _ in M.LiftIO(forks.Traverse(f => f.Cancel)) // Await all of the values - this should not yield anything useful unless an error occurred. from rs in M.LiftIO(forks.Traverse(f => f.Await)) select result; static K trigger(ChannelWriter> writer) => M.LiftIO(IO.lift(() => writer.TryComplete().Ignore())); static Func> error( ChannelWriter> writerA, ChannelWriter> writerB) => err => M.LiftIO( IO.lift(e => { writerA.TryComplete(); writerB.TryComplete(); return unit; })) .Bind(_ => M.Fail(err)); static K> writeAsync(ChannelWriter writer, X value) => M.LiftIO(IO.liftVAsync(async e => { await writer.WriteAsync(value, e.Token); return Reduced.Continue(unit); })); } ================================================ FILE: LanguageExt.Streaming/SourceT/DSL/Zip3SourceT.cs ================================================ using System; using static LanguageExt.Prelude; using System.Threading.Channels; using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt; record Zip3SourceT( SourceT SourceA, SourceT SourceB, SourceT SourceC) : SourceT where M : MonadIO, Fallible { internal override K> ReduceInternalM(S state, ReducerM, S> reducer) => // Create channels that receive the values yielded by the two sources from channelA in M.Pure(Channel.CreateUnbounded>()) from channelB in M.Pure(Channel.CreateUnbounded>()) from channelC in M.Pure(Channel.CreateUnbounded>()) let writerA = channelA.Writer let writerB = channelB.Writer let writerC = channelC.Writer // Triggers which signal when a channel has completed let triggerA = trigger(writerA) let triggerB = trigger(writerB) let triggerC = trigger(writerC) let error = error(writerA, writerB, writerC) // Create a forked first channel from forkA in SourceA.ReduceInternalM(unit, (_, ma) => writeAsync(writerA, ma)) .Bind(_ => triggerA) .Catch(error) .ForkIOMaybe() // Create a forked second channel from forkB in SourceB.ReduceInternalM(unit, (_, ma) => writeAsync(writerB, ma)) .Bind(_ => triggerB) .Catch(error) .ForkIOMaybe() // Create a forked third channel from forkC in SourceC.ReduceInternalM(unit, (_, ma) => writeAsync(writerC, ma)) .Bind(_ => triggerC) .Catch(error) .ForkIOMaybe() let forks = Seq(forkA, forkB, forkC) // Then create a reader iterator that will yield the merged values from result in new Reader3SourceT(channelA, channelB, channelC).ReduceInternalM(state, reducer) // Make sure the forks are shutdown from _ in M.LiftIOMaybe(forks.Traverse(f => f.Cancel)) // Await all of the values - this should not yield anything useful unless an error occurred. from rs in M.LiftIO(forks.Traverse(f => f.Await)) select result; static K trigger(ChannelWriter> writer) => M.LiftIOMaybe(IO.lift(() => writer.TryComplete().Ignore())); static Func> error( ChannelWriter> writerA, ChannelWriter> writerB, ChannelWriter> writerC) => err => M.LiftIO( IO.lift(e => { writerA.TryComplete(); writerB.TryComplete(); writerC.TryComplete(); return unit; })) .Bind(_ => M.Fail(err)); static K> writeAsync(ChannelWriter writer, X value) => M.LiftIO(IO.liftVAsync(async e => { await writer.WriteAsync(value, e.Token); return Reduced.Continue(unit); })); } ================================================ FILE: LanguageExt.Streaming/SourceT/DSL/Zip4SourceT.cs ================================================ using System; using LanguageExt.Traits; using System.Threading.Channels; using LanguageExt.Common; using static LanguageExt.Prelude; namespace LanguageExt; record Zip4SourceT(SourceT SourceA, SourceT SourceB, SourceT SourceC, SourceT SourceD) : SourceT where M : MonadIO, Fallible { internal override K> ReduceInternalM(S state, ReducerM, S> reducer) => // Create channels that receive the values yielded by the two sources from channelA in M.Pure(Channel.CreateUnbounded>()) from channelB in M.Pure(Channel.CreateUnbounded>()) from channelC in M.Pure(Channel.CreateUnbounded>()) from channelD in M.Pure(Channel.CreateUnbounded>()) let writerA = channelA.Writer let writerB = channelB.Writer let writerC = channelC.Writer let writerD = channelD.Writer // Triggers which signal when a channel has completed let triggerA = trigger(writerA) let triggerB = trigger(writerB) let triggerC = trigger(writerC) let triggerD = trigger(writerD) let error = error(writerA, writerB, writerC, writerD) // Create a forked first channel from forkA in SourceA.ReduceInternalM(unit, (_, ma) => writeAsync(writerA, ma)) .Bind(_ => triggerA) .Catch(error) .ForkIOMaybe() // Create a forked second channel from forkB in SourceB.ReduceInternalM(unit, (_, ma) => writeAsync(writerB, ma)) .Bind(_ => triggerB) .Catch(error) .ForkIOMaybe() // Create a forked third channel from forkC in SourceC.ReduceInternalM(unit, (_, ma) => writeAsync(writerC, ma)) .Bind(_ => triggerC) .Catch(error) .ForkIOMaybe() // Create a forked fourth channel from forkD in SourceD.ReduceInternalM(unit, (_, ma) => writeAsync(writerD, ma)) .Bind(_ => triggerD) .Catch(error) .ForkIOMaybe() let forks = Seq(forkA, forkB, forkC, forkD) // Then create a reader iterator that will yield the merged values from result in new Reader4SourceT(channelA, channelB, channelC, channelD).ReduceInternalM(state, reducer) // Make sure the forks are shutdown from _ in M.LiftIOMaybe(forks.Traverse(f => f.Cancel)) // Await all of the values - this should not yield anything useful unless an error occurred. from rs in M.LiftIO(forks.Traverse(f => f.Await)) select result; static K trigger(ChannelWriter> writer) => M.LiftIOMaybe(IO.lift(() => writer.TryComplete().Ignore())); static Func> error( ChannelWriter> writerA, ChannelWriter> writerB, ChannelWriter> writerC, ChannelWriter> writerD) => err => M.LiftIO( IO.lift(e => { writerA.TryComplete(); writerB.TryComplete(); writerC.TryComplete(); writerD.TryComplete(); return unit; })) .Bind(_ => M.Fail(err)); static K> writeAsync(ChannelWriter writer, X value) => M.LiftIO(IO.liftVAsync(async e => { await writer.WriteAsync(value, e.Token); return Reduced.Continue(unit); })); } ================================================ FILE: LanguageExt.Streaming/SourceT/Extensions/SourceT.Combinators.cs ================================================ using System; using LanguageExt.Common; using LanguageExt.Traits; using System.Diagnostics.Contracts; using static LanguageExt.Prelude; namespace LanguageExt; public static partial class SourceTExtensions { /// Stream of values to delay the yielding of /// Lifted monad type /// Bound value type extension(K, A> ma) where M : MonadIO { /// /// Delay the yielding of values by the specified duration /// /// Duration to delay the yielding of values for /// public SourceT Delay(TimeSpan duration) => IO.yieldFor(duration) >> ma >> lower; /// /// Delay the yielding of values by the specified duration /// /// Duration to delay the yielding of values for /// public SourceT Delay(Duration duration) => IO.yieldFor(duration) >> ma >> lower; } /// Source /// Lifted monad trait /// Bound value type extension(K, A> ma) where M : MonadIO, Fallible { /// /// Take values from the source for a period of time /// /// Duration to take values for /// SourceT [Pure] public SourceT TakeFor(Duration duration) => new TakeForSourceT(+ma, (TimeSpan)duration); /// /// Take values from the source for a period of time /// /// Duration to take values for /// SourceT [Pure] public SourceT TakeFor(TimeSpan duration) => new TakeForSourceT(+ma, duration); /// /// Combine two sources into a single source. The value streams are both /// merged into a new stream. Values are yielded as they become available /// regardless of which stream yields it. /// /// Left-hand side /// Right-hand side /// Merged stream of values public SourceT Choose(K, A> rhs) => (ma, rhs) switch { (EmptySourceT, EmptySourceT) => EmptySourceT.Default, (var l, EmptySourceT) => +l, (EmptySourceT, var r) => +r, (ChooseSourceT l, ChooseSourceT r) => new ChooseSourceT(l.Sources + r.Sources), (ChooseSourceT l, var r) => new ChooseSourceT(l.Sources.Add(+r)), (var l, ChooseSourceT r) => new ChooseSourceT(l.As().Cons(r.Sources)), var (l, r) => new ChooseSourceT([+l, +r]) }; /// /// Combine two sources into a single source. The value streams are both /// merged into a new stream. Values are yielded as they become available /// regardless of which stream yields it. /// /// Left-hand side /// Right-hand side /// Merged stream of values public SourceT Choose(Memo, A> rhs) => ma.Choose(rhs.Value.As()); } /// Stream to zip extension(K, A> first) where M : MonadIO, Fallible { /// /// Zip two sources into one /// /// Stream to zip /// Stream of values where the items from two streams are paired together public SourceT Zip(K, B> second) => new Zip2SourceT(first.As(), second.As()); /// /// Zip three sources into one /// /// Stream to zip /// Stream to zip /// Stream of values where the items from two streams are paired together public SourceT Zip( K, B> second, K, C> third) => new Zip3SourceT(first.As(), second.As(), third.As()); /// /// Zip three sources into one /// /// Stream to zip /// Stream to zip /// Stream to zip /// Stream of values where the items from two streams are paired together public SourceT Zip( K, B> second, K, C> third, K, D> fourth) => new Zip4SourceT(first.As(), second.As(), third.As(), fourth.As()); } } ================================================ FILE: LanguageExt.Streaming/SourceT/Extensions/SourceT.Extensions.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics.Contracts; using System.Threading.Channels; using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt; public static partial class SourceTExtensions { /// /// Downcast /// [Pure] public static SourceT As(this K, A> ma) where M : MonadIO => (SourceT)ma; [Pure] public static SourceT AsSourceT(this Channel items) where M : MonadIO, Fallible => SourceT.lift(items); [Pure] public static SourceT AsSourceM(this Channel> items) where M : MonadIO, Fallible => SourceT.liftM(items); [Pure] public static SourceT AsSourceT(this Source items) where M : MonadIO => SourceT.lift(items); [Pure] public static SourceT AsSourceM(this Source> items) where M : MonadIO => SourceT.liftM(items); [Pure] public static SourceT AsSourceT(this IEnumerable items) where M : MonadIO => SourceT.lift(items); [Pure] public static SourceT AsSourceM(this IEnumerable> items) where M : MonadIO => SourceT.liftM(items); [Pure] public static SourceT AsSourceT(this IObservable items) where M : MonadIO => SourceT.lift(items); [Pure] public static SourceT AsSourceM(this IObservable> items) where M : MonadIO => SourceT.liftM(items); [Pure] public static SourceT AsSourceT(this IAsyncEnumerable items) where M : MonadIO => SourceT.lift(items); [Pure] public static SourceT AsSourceM(this IAsyncEnumerable> items) where M : MonadIO => SourceT.liftM(items); /// /// Monad bind /// [Pure] public static SourceT Bind(this IO ma, Func> f) where M : MonadIO => SourceT.liftIO(ma).Bind(f); /// /// Monad bind /// [Pure] public static SourceT Bind(this Pure ma, Func> f) where M : MonadIO => SourceT.pure(ma.Value).Bind(f); /// /// Monad bind /// [Pure] public static SourceT Bind(this K ma, Func> f) where M : MonadIO => SourceT.liftM(ma).Bind(f); /// /// Monad bind /// [Pure] public static SourceT SelectMany(this K ma, Func> bind, Func project) where M : MonadIO => SourceT.liftM(ma).As().SelectMany(bind, project); /// /// Monad bind /// [Pure] public static SourceT SelectMany(this IO ma, Func> bind, Func project) where M : MonadIO => SourceT.liftIO(ma).As().SelectMany(bind, project); /// /// Monad bind /// [Pure] public static SourceT SelectMany(this Pure ma, Func> bind, Func project) where M : MonadIO => bind(ma.Value).Map(y => project(ma.Value, y)); /// /// Access the `Some` values from the asynchronous stream /// /// Stream of optional values /// Transformer monad /// Bound value type /// Stream of values [Pure] public static SourceT SomeSource(this IAsyncEnumerable> stream) where M : MonadIO => from xs in SourceT.lift>(stream) from ox in xs.Run() where ox.IsSome select (A)ox; /// /// Access the `Some` values from the asynchronous stream /// /// Stream of optional values /// Transformer monad /// Bound value type /// Stream of values [Pure] public static SourceT SomeSource(this IAsyncEnumerable> stream) where M : MonadIO => from ox in SourceT.lift>(stream) where ox.IsSome select (A)ox; /// /// Access the `Some` values from the synchronous stream /// /// Stream of optional values /// Transformer monad /// Bound value type /// Stream of values [Pure] public static SourceT SomeSource(this IEnumerable> stream) where M : MonadIO => from xs in SourceT.lift>(stream) from ox in xs.Run() where ox.IsSome select (A)ox; /// /// Access the `Some` values from the synchronous stream /// /// Stream of optional values /// Transformer monad /// Bound value type /// Stream of values [Pure] public static SourceT SomeSource(this IEnumerable> stream) where M : MonadIO => from ox in SourceT.lift>(stream) where ox.IsSome select (A)ox; /// /// Access the `Right` values from the asynchronous stream /// /// Stream of values /// Transformer monad /// Bound value type /// Stream of values [Pure] public static SourceT RightSource(this IAsyncEnumerable> stream) where M : MonadIO => from xs in SourceT.lift>(stream) from ox in xs.Run() where ox.IsRight select (A)ox; /// /// Access the `Right` values from the asynchronous stream /// /// Stream of values /// Transformer monad /// Bound value type /// Stream of values [Pure] public static SourceT RightSource(this IAsyncEnumerable> stream) where M : MonadIO => from ox in SourceT.lift>(stream) where ox.IsRight select (A)ox; /// /// Access the `Right` values from the synchronous stream /// /// Stream of values /// Transformer monad /// Bound value type /// Stream of values [Pure] public static SourceT RightSource(this IEnumerable> stream) where M : MonadIO => from xs in SourceT.lift>(stream) from ox in xs.Run() where ox.IsRight select (A)ox; /// /// Access the `Right` values from the synchronous stream /// /// Stream of values /// Transformer monad /// Bound value type /// Stream of values [Pure] public static SourceT RightSource(this IEnumerable> stream) where M : MonadIO => from ox in SourceT.lift>(stream) where ox.IsRight select (A)ox; /// /// Access the `Left` values from the asynchronous stream /// /// Stream of values /// Transformer monad /// Bound value type /// Stream of values [Pure] public static SourceT LeftSource(this IAsyncEnumerable> stream) where M : MonadIO => from xs in SourceT.lift>(stream) from ox in xs.Run() where ox.IsLeft select (L)ox; /// /// Access the `Left` values from the asynchronous stream /// /// Stream of values /// Transformer monad /// Bound value type /// Stream of values [Pure] public static SourceT LeftSource(this IAsyncEnumerable> stream) where M : MonadIO => from ox in SourceT.lift>(stream) where ox.IsLeft select (L)ox; /// /// Access the `Left` values from the synchronous stream /// /// Stream of values /// Transformer monad /// Bound value type /// Stream of values [Pure] public static SourceT LeftSource(this IEnumerable> stream) where M : MonadIO => from xs in SourceT.lift>(stream) from ox in xs.Run() where ox.IsLeft select (L)ox; /// /// Access the `Succ` values from the synchronous stream /// /// Stream of values /// Transformer monad /// Bound value type /// Stream of values [Pure] public static SourceT LeftSource(this IEnumerable> stream) where M : MonadIO => from ox in SourceT.lift>(stream) where ox.IsLeft select (L)ox; /// /// Access the `Succ` values from the asynchronous stream /// /// Stream of values /// Transformer monad /// Bound value type /// Stream of values [Pure] public static SourceT SuccSource(this IAsyncEnumerable> stream) where M : MonadIO => from xs in SourceT.lift>(stream) from ox in xs.Run() where ox.IsSucc select (A)ox; /// /// Access the `Succ` values from the asynchronous stream /// /// Stream of values /// Transformer monad /// Bound value type /// Stream of values [Pure] public static SourceT SuccSource(this IAsyncEnumerable> stream) where M : MonadIO => from ox in SourceT.lift>(stream) where ox.IsSucc select (A)ox; /// /// Access the `Succ` values from the synchronous stream /// /// Stream of values /// Transformer monad /// Bound value type /// Stream of values [Pure] public static SourceT SuccSource(this IEnumerable> stream) where M : MonadIO => from xs in SourceT.lift>(stream) from ox in xs.Run() where ox.IsSucc select (A)ox; /// /// Access the `Succ` values from the synchronous stream /// /// Stream of values /// Transformer monad /// Bound value type /// Stream of values [Pure] public static SourceT SuccSource(this IEnumerable> stream) where M : MonadIO => from ox in SourceT.lift>(stream) where ox.IsSucc select (A)ox; /// /// Access the `Fail` values from the asynchronous stream /// /// Stream of values /// Transformer monad /// Bound value type /// Stream of values [Pure] public static SourceT FailSource(this IAsyncEnumerable> stream) where M : MonadIO => from xs in SourceT.lift>(stream) from ox in xs.Run() where ox.IsFail select (Error)ox; /// /// Access the `Fail` values from the asynchronous stream /// /// Stream of values /// Transformer monad /// Bound value type /// Stream of values [Pure] public static SourceT FailSource(this IAsyncEnumerable> stream) where M : MonadIO => from ox in SourceT.lift>(stream) where ox.IsFail select (Error)ox; /// /// Access the `Fail` values from the synchronous stream /// /// Stream of values /// Transformer monad /// Bound value type /// Stream of values [Pure] public static SourceT FailSource(this IEnumerable> stream) where M : MonadIO => from xs in SourceT.lift>(stream) from ox in xs.Run() where ox.IsFail select (Error)ox; /// /// Access the `Fail` values from the synchronous stream /// /// Stream of values /// Transformer monad /// Bound value type /// Stream of values [Pure] public static SourceT FailSource(this IEnumerable> stream) where M : MonadIO => from ox in SourceT.lift>(stream) where ox.IsFail select (Error)ox; /// /// Access the `Succ` values from the asynchronous stream /// /// Stream of values /// Transformer monad /// Bound value type /// Stream of values [Pure] public static SourceT SuccSource(this IAsyncEnumerable> stream) where L : Monoid where M : MonadIO => from xs in SourceT.lift>(stream) from ox in xs.Run() where ox.IsSuccess select (A)ox; /// /// Access the `Succ` values from the asynchronous stream /// /// Stream of values /// Transformer monad /// Bound value type /// Stream of values [Pure] public static SourceT SuccSource(this IAsyncEnumerable> stream) where L : Monoid where M : MonadIO => from ox in SourceT.lift>(stream) where ox.IsSuccess select (A)ox; /// /// Access the `Succ` values from the synchronous stream /// /// Stream of values /// Transformer monad /// Bound value type /// Stream of values [Pure] public static SourceT SuccSource(this IEnumerable> stream) where L : Monoid where M : MonadIO => from xs in SourceT.lift>(stream) from ox in xs.Run() where ox.IsSuccess select (A)ox; /// /// Access the `Succ` values from the synchronous stream /// /// Stream of values /// Transformer monad /// Bound value type /// Stream of values [Pure] public static SourceT SuccSource(this IEnumerable> stream) where L : Monoid where M : MonadIO => from ox in SourceT.lift>(stream) where ox.IsSuccess select (A)ox; /// /// Access the `Fail` values from the asynchronous stream /// /// Stream of values /// Transformer monad /// Bound value type /// Stream of values [Pure] public static SourceT FailSource(this IAsyncEnumerable> stream) where L : Monoid where M : MonadIO => from xs in SourceT.lift>(stream) from ox in xs.Run() where ox.IsFail select (L)ox; /// /// Access the `Fail` values from the asynchronous stream /// /// Stream of values /// Transformer monad /// Bound value type /// Stream of values [Pure] public static SourceT FailsStream(this IAsyncEnumerable> stream) where L : Monoid where M : MonadIO => from ox in SourceT.lift>(stream) where ox.IsFail select (L)ox; /// /// Access the `Fail` values from the synchronous stream /// /// Stream of values /// Transformer monad /// Bound value type /// Stream of values [Pure] public static SourceT FailSource(this IEnumerable> stream) where L : Monoid where M : MonadIO => from xs in SourceT.lift>(stream) from ox in xs.Run() where ox.IsFail select (L)ox; /// /// Access the `Fail` values from the synchronous stream /// /// Stream of values /// Transformer monad /// Bound value type /// Stream of values [Pure] public static SourceT FailSource(this IEnumerable> stream) where L : Monoid where M : MonadIO => from ox in SourceT.lift>(stream) where ox.IsFail select (L)ox; } ================================================ FILE: LanguageExt.Streaming/SourceT/Extensions/SourceT.Reducers.cs ================================================ using System; using System.Diagnostics.Contracts; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; public static partial class SourceTExtensions { /// /// /// extension(K, A> ma) where M : MonadIO { /// /// Reduce the stream into a unit value. /// [Pure] public K Iter() => ma.As().FoldReduce(unit, (_, _) => unit); /// /// Reduce the stream by collecting all the values into a `Seq` while the predicate holds. /// [Pure] public K> CollectWhile(Func<(Seq Items, A Item), bool> predicate) => ma.As().Reduce>( [], (xs, x) => predicate((xs, x)) ? Reduced.Continue(xs.Add(x)) : Reduced.Done(xs)); /// /// Reduce the stream by collecting all the values into a `Seq` while the predicate holds. /// [Pure] public K> CollectUntil(Func<(Seq Items, A Item), bool> predicate) => ma.As().Reduce>( [], (xs, x) => predicate((xs, x)) ? Reduced.Done(xs) : Reduced.Continue(xs.Add(x))); /// /// Reduce the stream by collecting all the values into a `Seq`. /// [Pure] public K> Collect() => ma.As().FoldReduce>([], (xs, x) => xs.Add(x)); /// /// Reduce the stream, yielding the last structure processed, or the `None` if the stream is empty /// [Pure] public K> LastOrNone() => ma.As().FoldReduce(Option.None, (_, x) => Some(x)); /// /// Reduce the stream, yielding the first structure processed, or the `None` if the stream is empty /// [Pure] public K> FirstOrNone() => ma.As().Reduce(Option.None, (_, x) => Reduced.Done(Some(x))); /// /// Reduce the stream, yielding the last structure processed, or the default value if the stream is empty /// [Pure] public K Last(A defaultValue) => ma.As().FoldReduce(defaultValue, (_, x) => x); /// /// Reduce the stream, yielding the first structure processed, or the default value if the stream is empty /// [Pure] public K First(A defaultValue) => ma.As().Reduce(defaultValue, (_, x) => Reduced.Done(x)); } /// /// /// extension(K, A> ma) where M : MonadIO, Alternative { /// /// Reduce the stream, yielding the last structure processed, or `M.Empty` if the stream is empty /// [Pure] public K Last() => ma.LastOrNone() .Bind(ma => ma switch { { IsSome: true, Case: A value } => M.Pure(value), _ => M.Empty() }); /// /// Reduce the stream, yielding the first structure processed, or `M.Empty` if the stream is empty /// [Pure] public K First() => ma.FirstOrNone() .Bind(ma => ma switch { { IsSome: true, Case: A value } => M.Pure(value), _ => M.Empty() }); } } ================================================ FILE: LanguageExt.Streaming/SourceT/Operators/SourceT.Operators.Applicative.cs ================================================ using System; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; public static partial class SourceTExtensions { extension(K, A> self) where M : MonadIO { /// /// Applicative sequence operator /// public static SourceT operator >>> (K, A> ma, K, B> mb) => +ma.Action(mb); /// /// Applicative apply operator /// public static SourceT operator * (K, Func> mf, K, A> ma) => +mf.Apply(ma); /// /// Applicative apply operator /// public static SourceT operator * (K, A> ma, K, Func> mf) => +mf.Apply(ma); } extension(K, A> self) where M : MonadIO { /// /// Applicative apply operator /// public static SourceT> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static SourceT> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) where M : MonadIO { /// /// Applicative apply operator /// public static SourceT>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static SourceT>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) where M : MonadIO { /// /// Applicative apply operator /// public static SourceT>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static SourceT>>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) where M : MonadIO { /// /// Applicative apply operator /// public static SourceT>>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static SourceT>>>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) where M : MonadIO { /// /// Applicative apply operator /// public static SourceT>>>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static SourceT>>>>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) where M : MonadIO { /// /// Applicative apply operator /// public static SourceT>>>>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static SourceT>>>>>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) where M : MonadIO { /// /// Applicative apply operator /// public static SourceT>>>>>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static SourceT>>>>>>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) where M : MonadIO { /// /// Applicative apply operator /// public static SourceT>>>>>>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static SourceT>>>>>>>> operator * ( K, A> ma, K, Func> mf) => curry * mf * ma; } extension(K, A> self) where M : MonadIO { /// /// Applicative apply operator /// public static SourceT>>>>>>>>> operator * ( K, Func> mf, K, A> ma) => curry * mf * ma; /// /// Applicative apply operator /// public static SourceT>>>>>>>>> operator *( K, A> ma, K, Func> mf) => curry * mf * ma; } } ================================================ FILE: LanguageExt.Streaming/SourceT/Operators/SourceT.Operators.Choice.cs ================================================ using LanguageExt.Traits; namespace LanguageExt; public static partial class SourceTExtensions { extension(K, A> self) where M : MonadIO, Fallible { public static SourceT operator |(K, A> lhs, K, A> rhs) => +lhs.Choose(rhs); public static SourceT operator |(K, A> lhs, Pure rhs) => +lhs.Choose(SourceT.pure(rhs.Value)); } } ================================================ FILE: LanguageExt.Streaming/SourceT/Operators/SourceT.Operators.Combine.cs ================================================ using LanguageExt.Traits; namespace LanguageExt; public static partial class SourceExtensions { extension(K, A> self) where M : MonadIO, Alternative { /// /// Semigroup combine operator: an associative binary operation. /// /// Left-hand side operand /// Right-hand side operand /// public static SourceT operator +(K, A> lhs, K, A> rhs) => +lhs.Combine(rhs); /// /// Semigroup combine operator: an associative binary operation. /// /// Left-hand side operand /// Right-hand side operand /// public static SourceT operator +(K, A> lhs, Pure rhs) => +lhs.Combine(SourceT.pure(rhs.Value)); } } ================================================ FILE: LanguageExt.Streaming/SourceT/Operators/SourceT.Operators.Functor.cs ================================================ using System; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; public static partial class SourceTExtensions { extension(K, A> _) where M : MonadIO, Alternative { /// /// Functor map operator /// public static SourceT operator *(Func f, K, A> ma) => +ma.Map(f); /// /// Functor map operator /// public static SourceT operator *(K, A> ma, Func f) => +ma.Map(f); } extension(K, A> _) where M : MonadIO, Alternative { /// /// Functor map operator /// public static SourceT> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static SourceT> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) where M : MonadIO, Alternative { /// /// Functor map operator /// public static SourceT>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static SourceT>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) where M : MonadIO, Alternative { /// /// Functor map operator /// public static SourceT>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static SourceT>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) where M : MonadIO, Alternative { /// /// Functor map operator /// public static SourceT>>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static SourceT>>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) where M : MonadIO, Alternative { /// /// Functor map operator /// public static SourceT>>>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static SourceT>>>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) where M : MonadIO, Alternative { /// /// Functor map operator /// public static SourceT>>>>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static SourceT>>>>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) where M : MonadIO, Alternative { /// /// Functor map operator /// public static SourceT>>>>>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static SourceT>>>>>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) where M : MonadIO, Alternative { /// /// Functor map operator /// public static SourceT>>>>>>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static SourceT>>>>>>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } extension(K, A> _) where M : MonadIO, Alternative { /// /// Functor map operator /// public static SourceT>>>>>>>>> operator * ( Func f, K, A> ma) => curry(f) * ma; /// /// Functor map operator /// public static SourceT>>>>>>>>> operator * ( K, A> ma, Func f) => curry(f) * ma; } } ================================================ FILE: LanguageExt.Streaming/SourceT/Operators/SourceT.Operators.Monad.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public static partial class SourceTExtensions { extension(K, A> self) where M : MonadIO, Alternative { /// /// Monad bind operator /// /// Monad to bind /// Binding function /// Mapped monad public static SourceT operator >> (K, A> ma, Func, B>> f) => +ma.Bind(f); /// /// Sequentially compose two actions, discarding any value produced by the first, like sequencing operators (such /// as the semicolon) in C#. /// /// First action to run /// Second action to run /// Result of the second action public static SourceT operator >> (K, A> lhs, K, B> rhs) => lhs >> (_ => rhs); } extension(K, A> self) where M : MonadIO, Alternative { /// /// Sequentially compose two actions. The second action is a unit-returning action, so the result of the /// first action is propagated. /// /// First action to run /// Second action to run /// Result of the first action public static SourceT operator >> (K, A> lhs, K, Unit> rhs) => lhs >> (x => (_ => x) * rhs); } } ================================================ FILE: LanguageExt.Streaming/SourceT/Operators/SourceT.Operators.Zip.cs ================================================ using LanguageExt.Traits; namespace LanguageExt; public static partial class SourceTExtensions { extension(K, A>) where M : MonadIO, Fallible { public static SourceT operator &(K, A> lhs, K, B> rhs) => +lhs.Zip(rhs); } extension(K, (A First, B Second)>) where M : MonadIO, Fallible { public static SourceT operator &(K, (A First, B Second)> lhs, K, C> rhs) => +lhs.Zip(rhs).Map(s => (s.First.First, s.First.Second, s.Second)); } extension(K, (A First, B Second)>) where M : MonadIO, Fallible { public static SourceT operator &(K, (A First, B Second)> lhs, K, (C First, D Second)> rhs) => +lhs.Zip(rhs).Map(s => (s.First.First, s.First.Second, s.Second.First, s.Second.Second)); } extension(K, (A First, B Second, C Third)>) where M : MonadIO, Fallible { public static SourceT operator &(K, (A First, B Second, C Third)> lhs, K, D> rhs) => +lhs.Zip(rhs).Map(s => (s.First.First, s.First.Second, s.First.Third, s.Second)); } } ================================================ FILE: LanguageExt.Streaming/SourceT/Operators/SourceT.Operators.cs ================================================ using LanguageExt.Traits; namespace LanguageExt; public static partial class SourceTExtensions { extension(K, A> _) where M : MonadIO { /// /// Downcast operator /// public static SourceT operator +(K, A> ma) => (SourceT)ma; /// /// Downcast operator /// public static SourceT operator >> (K, A> ma, Lower lower) => +ma; } } ================================================ FILE: LanguageExt.Streaming/SourceT/SourceT.Module.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics.Contracts; using System.Linq; using System.Threading.Channels; using LanguageExt.Async.Linq; using LanguageExt.Common; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; public partial class SourceT { /// /// Empty source /// /// /// This is a 'void' source, it yields zero values. /// /// Bound value type /// Uninhabited source [Pure] public static SourceT empty() where M : MonadIO => EmptySourceT.Default; /// /// Lift a pure value into the source /// /// /// This is a singleton/unit source, it yields exactly one value. /// /// Value to lift /// Bound value type /// Singleton source [Pure] public static SourceT pure(A value) where M : MonadIO => new PureSourceT(value); /// /// Indicate the stream is complete /// /// /// /// /// [Pure] public static SourceT done() where M : MonadIO => new DoneSourceT(); /// /// Lift a foldable of pure values into a `SourceT` /// /// Foldable of pure values /// Foldable trait type /// Monad trait type /// Bound value type /// `SourceT` [Pure] public static SourceT liftFoldable(K fa) where M : MonadIO where F : Foldable => new FoldablePureSourceT(fa); /// /// Lift a foldable of monadic values into a `SourceT` /// /// Foldable of monadic values /// Foldable trait type /// Monad trait type /// Bound value type /// `SourceT` [Pure] public static SourceT liftFoldableM(K> fma) where M : MonadIO where F : Foldable => new FoldableSourceT(fma); /// /// Lift a structure into the source /// /// /// This is a singleton/unit source, it yields exactly one structure. /// /// Value to lift /// Bound value type /// Singleton source [Pure] public static SourceT liftM(K ma) where M : MonadIO => new LiftSourceT(ma); /// /// Lift a structure into the source /// /// /// This is a singleton/unit source, it yields exactly one structure. /// /// Value to lift /// Bound value type /// Singleton source [Pure] public static SourceT liftIO(K ma) where M : MonadIO => new LiftSourceT(M.LiftIO(ma)); /// /// Lift a pure value into the source and yield it for infinity /// /// /// This is an infinite source, it repeatedly yields a value. /// /// Value to lift /// Bound value type /// Infinite source [Pure] public static SourceT forever(A value) where M : MonadIO => new ForeverSourceT(M.Pure(value)); /// /// Lift a structure into the source and yield it for infinity /// /// /// This is an infinite source, it repeatedly yields the provided structure. /// /// Value to lift /// Bound value type /// Infinite source [Pure] public static SourceT foreverM(K ma) where M : MonadIO => new ForeverSourceT(ma); /// /// Make a `System.Threading.Channels.Channel` into a source of values /// /// Channel to lift /// Label to help debugging /// Value type /// Source of values [Pure] public static SourceT lift(Channel channel) where M : MonadIO, Fallible => new MultiListenerPureSourceT(channel); /// /// Make a `System.Threading.Channels.Channel` into a source of values /// /// Channel to lift /// Value type /// Source of values [Pure] public static SourceT liftM(Channel> channel) where M : MonadIO, Fallible => new MultiListenerSourceT(channel); /// /// Make a `Source` into a `SourceT` /// /// Channel to lift /// Value type /// Source of values [Pure] public static SourceT lift(Source channel) where M : MonadIO => new SourcePureSourceT(channel); /// /// Make a `Source` into a `SourceT` /// /// Channel to lift /// Value type /// Source of values [Pure] public static SourceT liftM(Source> channel) where M : MonadIO => new SourceSourceT(channel); /// /// Make an `IEnumerable` into a source of values /// /// Enumerable to lift /// Value type /// Source of values [Pure] public static SourceT lift(IEnumerable items) where M : MonadIO => new IteratorSyncSourceT(items.Select(M.Pure)); /// /// Make an `IEnumerable` into a source of values /// /// Enumerable to lift /// Value type /// Source of values [Pure] public static SourceT liftM(IEnumerable> items) where M : MonadIO => new IteratorSyncSourceT(items); /// /// Make an `IObservable` into a source of values /// /// `IObservable` to lift /// Value type /// Source of values [Pure] public static SourceT lift(IObservable items) where M : MonadIO => new ObservablePureSourceT(items); /// /// Make an `IObservable` into a source of values /// /// `IObservable` to lift /// Value type /// Source of values [Pure] public static SourceT liftM(IObservable> items) where M : MonadIO => new ObservableSourceT(items); /// /// Make an `IAsyncEnumerable` into a source of values /// /// `IAsyncEnumerable` to lift /// Value type /// Source of values [Pure] public static SourceT lift(IAsyncEnumerable items) where M : MonadIO => new IteratorAsyncSourceT(items.Select(M.Pure)); /// /// Make an `IAsyncEnumerable` into a source of values /// /// `IAsyncEnumerable` to lift /// Value type /// Source of values [Pure] public static SourceT liftM(IAsyncEnumerable> items) where M : MonadIO => new IteratorAsyncSourceT(items); /// /// Merge sources into a single source /// /// Sources /// Bound value type /// Source that is the combination of all provided sources [Pure] public static SourceT merge(Seq> sources) where M : MonadIO, Fallible => sources.Fold(empty(), (s, s2) => s.Choose(s2)); /// /// Merge sources into a single source /// /// Sources /// Bound value type /// Source that is the combination of all provided sources [Pure] public static SourceT merge(params SourceT[] sources) where M : MonadIO, Fallible => merge(toSeq(sources)); /// /// Zip two sources into one /// /// Stream to zip with this one /// Bound value-type of the stream to zip with this one /// Stream of values where the items from two streams are paired together [Pure] public static SourceT zip(SourceT first, SourceT second) where M : MonadUnliftIO, Fallible => new Zip2SourceT(first, second); /// /// Zip three sources into one /// /// Stream to zip with this one /// Stream to zip with this one /// Bound value-type of the stream to zip with this one /// Stream of values where the items from two streams are paired together [Pure] public static SourceT zip(SourceT first, SourceT second, SourceT third) where M : MonadUnliftIO, Fallible => new Zip3SourceT(first, second, third); /// /// Zip three sources into one /// /// Stream to zip with this one /// Stream to zip with this one /// Stream to zip with this one /// Bound value-type of the stream to zip with this one /// Stream of values where the items from two streams are paired together [Pure] public static SourceT zip(SourceT first, SourceT second, SourceT third, SourceT fourth) where M : MonadUnliftIO, Fallible => new Zip4SourceT(first, second, third, fourth); /// /// Reduce the stream into a unit value. /// [Pure] public static K iter(K, A> ma) where M : MonadIO => ma.Iter(); /// /// Reduce the stream by collecting all the values into a `Seq` while the predicate holds. /// [Pure] public static K> collectWhile(K, A> ma, Func<(Seq Items, A Item), bool> predicate) where M : MonadIO => ma.CollectWhile(predicate); /// /// Reduce the stream by collecting all the values into a `Seq` while the predicate holds. /// [Pure] public static K> collectUntil(K, A> ma, Func<(Seq Items, A Item), bool> predicate) where M : MonadIO => ma.CollectUntil(predicate); /// /// Reduce the stream by collecting all the values into a `Seq`. /// [Pure] public static K> collect(K, A> ma) where M : MonadIO => ma.Collect(); /// /// Reduce the stream, yielding the last structure processed, or the `None` if the stream is empty /// [Pure] public static K> lastOrNone(K, A> ma) where M : MonadIO => ma.LastOrNone(); /// /// Reduce the stream, yielding the first structure processed, or the `None` if the stream is empty /// [Pure] public static K> firstOrNone(K, A> ma) where M : MonadIO => ma.FirstOrNone(); /// /// Reduce the stream, yielding the last structure processed, or the default value if the stream is empty /// [Pure] public static K last(K, A> ma, A defaultValue) where M : MonadIO => ma.Last(defaultValue); /// /// Reduce the stream, yielding the first structure processed, or the default value if the stream is empty /// [Pure] public static K first(K, A> ma, A defaultValue) where M : MonadIO => ma.First(defaultValue); /// /// Reduce the stream, yielding the last structure processed, or `M.Empty` if the stream is empty /// [Pure] public static K last(K, A> ma) where M : MonadIO, Alternative => ma.Last(); /// /// Reduce the stream, yielding the first structure processed, or `M.Empty` if the stream is empty /// [Pure] public static K first(K, A> ma) where M : MonadIO, Alternative => ma.First(); } ================================================ FILE: LanguageExt.Streaming/SourceT/SourceT.cs ================================================ using System; using LanguageExt.Common; using LanguageExt.Pipes; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; /// /// A source / stream of lifted values /// /// Bound value type public abstract record SourceT : K, A>, Monoid> where M : MonadIO { /// /// Iterate the stream, flowing values downstream to the reducer, which aggregates a /// result value. This is returned lifted. /// /// Note, this is recursive, so `M` needs to be able to support recursion without /// blowing the stack. If you have the `IO` monad in your stack, then this will automatically /// be the case. /// Initial state /// Reducer /// State type /// Lifted aggregate state public K Reduce(S state, Reducer reducer) => ReduceM(state, (s, x) => M.Pure(reducer(s, x))); /// /// Iterate the stream, flowing values downstream to the reducer, which aggregates a /// result value. This is returned lifted. /// /// Note, this is recursive, so `M` needs to be able to support recursion without /// blowing the stack. If you have the `IO` monad in your stack, then this will automatically /// be the case. /// Initial state /// Reducer /// State type /// Lifted aggregate state public K FoldReduce(S state, Func reducer) => ReduceM(state, (s, x) => M.Pure(Reduced.Continue(reducer(s, x)))); /// /// Iterate the stream, flowing values downstream to the reducer, which aggregates a /// result value. This is returned lifted. /// /// Note, this is recursive, so `M` needs to be able to support recursion without /// blowing the stack. If you have the `IO` monad in your stack, then this will automatically /// be the case. /// Initial state /// Reducer /// State type /// Lifted aggregate state public K ReduceM(S state, ReducerM reducer) => M.Token >> (t => M.BracketIOMaybe( ReduceInternalM(state, (s, mx) => t.IsCancellationRequested ? M.Pure(Reduced.Done(s)) : mx.Bind(x => reducer(s, x))) .Map(r => r.Value))); /// /// Iterate the stream, flowing values downstream to the reducer, which aggregates a /// result value. This is returned lifted. /// /// Note, this is recursive, so `M` needs to be able to support recursion without /// blowing the stack. If you have the `IO` monad in your stack, then this will automatically /// be the case. /// Initial state /// Reducer /// State type /// Lifted aggregate state public K FoldReduceM(S state, Func> reducer) => M.Token >> (t => M.BracketIOMaybe( ReduceInternalM( state, (s, mx) => t.IsCancellationRequested ? M.Pure(Reduced.Done(s)) : mx.Bind(x => reducer(s, x).Map(Reduced.Continue))) .Map(r => r.Value))); /// /// A source that never yields a value /// public static SourceT Empty => EmptySourceT.Default; /// /// Transform with a transducer /// /// Transducer to use to transform /// Target bound value type /// Transformed source public SourceT Transform(Transducer transducer) => new TransformSourceT2(this, transducer); /// /// Transform with a transducer /// /// Transducer to use to transform /// Target bound value type /// Transformed source public SourceT Transform(TransducerM transducer) => new TransformSourceT(this, transducer); /// /// Functor map /// public virtual SourceT Map(Func f) => new MapSourceT(this, f); /// /// Monad bind /// public virtual SourceT Bind(Func> f) => new BindSourceT(this, f); /// /// Monad bind /// public SourceT Bind(Func, B>> f) => Bind(x => f(x).As()); /// /// Monad bind /// public virtual SourceT Bind(Func> f) => new BindSourceT(this, x => SourceT.liftIO(f(x))); /// /// Filter values. Yielding downstream when `true` /// /// Filter function /// SourceT where the only values yield are those that pass the predicate public SourceT Where(Func f) => new FilterSourceT(this, f); /// /// Filter values. Yielding downstream when `true` /// /// Filter function /// SourceT where the only values yield are those that pass the predicate public SourceT Filter(Func f) => new FilterSourceT(this, f); /// /// Applicative apply /// public virtual SourceT ApplyBack(SourceT> ff) => ff >> Map >> lower; /// /// Concatenate streams /// /// Left-hand side /// Right-hand side /// A stream that concatenates the input streams public SourceT Combine(SourceT rhs) => (this, rhs) switch { (EmptySourceT, EmptySourceT) => EmptySourceT.Default, (var l, EmptySourceT) => l, (EmptySourceT, var r) => r, (CombineSourceT l, CombineSourceT r) => new CombineSourceT(l.Sources + r.Sources), (CombineSourceT l, var r) => new CombineSourceT(l.Sources.Add(r)), (var l, CombineSourceT r) => new CombineSourceT(l.Cons(r.Sources)), _ => new CombineSourceT([this, rhs]) }; /// /// Skip items in the source /// /// Amount to skip /// Transformed source public SourceT Skip(int amount) => Transform(TransducerM.skip(amount)); /// /// Limit the number of items processed /// /// Amount to take /// Transformed source public SourceT Take(int amount) => new TakeSourceT(this, amount); /// /// Fold the values flowing through. A value is only yielded downstream upon completion of the stream. /// /// Binary operator /// Initial state /// State type /// Stream of aggregate state public SourceT Fold(Func Fold, S Init) => new FoldWhileSourceT(this, Schedule.Forever, Fold, _ => true, Init); /// /// Fold the values flowing through. Values are yielded downstream when either the schedule expires, or the /// source completes. /// /// Schedule to control the rate of processing /// Binary operator /// Initial state /// State type /// Stream of aggregate states public SourceT Fold(Schedule Time, Func Fold, S Init) => new FoldWhileSourceT(this, Time, Fold, _ => true, Init); /// /// Fold the values flowing through. Values are yielded downstream when either the predicate returns /// `false`, or the source completes. /// /// Binary operator /// Predicate /// Initial state /// State type /// Stream of aggregate states public SourceT FoldWhile(Func Fold, Func<(S State, A Value), bool> Pred, S Init) => new FoldWhileSourceT(this, Schedule.Forever, Fold, Pred, Init); /// /// Fold the values flowing through. Values are yielded downstream when either the predicate returns /// `true`, or the source completes. /// /// Binary operator /// Predicate /// Initial state /// State type /// Stream of aggregate states public SourceT FoldUntil(Func Fold, Func<(S State, A Value), bool> Pred, S Init) => new FoldUntilSourceT(this, Schedule.Forever, Fold, Pred, Init); /// /// Fold the values flowing through. Values are yielded downstream when either the schedule expires, the /// predicate returns `false`, or the source completes. /// /// Schedule to control the rate of processing /// Binary operator /// Predicate /// Initial state /// State type /// Stream of aggregate states public SourceT FoldWhile( Schedule Time, Func Fold, Func<(S State, A Value), bool> Pred, S Init) => new FoldWhileSourceT(this, Time, Fold, Pred, Init); /// /// Fold the values flowing through. Values are yielded downstream when either the schedule expires, the /// predicate returns `true`, or the source completes. /// /// Schedule to control the rate of processing /// Binary operator /// Predicate /// Initial state /// /// Stream of aggregate states public SourceT FoldUntil( Schedule Time, Func Fold, Func<(S State, A Value), bool> Pred, S Init) => new FoldUntilSourceT(this, Time, Fold, Pred, Init); /// /// Convert `SourceT` to a `ProducerT` pipe component /// /// Monad to lift (must support `IO`) /// `ProducerT` public ProducerT ToProducerT() => ProducerT.yieldAll(this); /// /// Sequentially compose two actions. The second action is a unit returning action, so the result of the /// first action is propagated. /// /// First action to run /// Second action to run /// Result of the first action public static SourceT operator >> (SourceT lhs, K, Unit> rhs) => lhs.Bind(x => rhs.Map(_ => x)); public static implicit operator SourceT(IO ma) => SourceT.liftIO(ma); public static implicit operator SourceT(Pure ma) => SourceT.liftM(M.Pure(ma.Value)); public static implicit operator SourceT(Fail ma) => IO.fail(ma.Value); /// /// Functor map /// public SourceT Select(Func f) => Map(f); /// /// Monad bind /// public SourceT SelectMany(Func> bind, Func project) => Bind(a => bind(a).Map(b => project(a, b))); /// /// Monad bind /// public SourceT SelectMany(Func> bind, Func project) => SelectMany(x => SourceT.liftM(bind(x)), project); /// /// Monad bind /// public SourceT SelectMany(Func> bind, Func project) => SelectMany(a => SourceT.liftIO(bind(a)), project); /// /// Monad bind /// public SourceT SelectMany(Func> bind, Func project) => Map(a => project(a, bind(a).Value)); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Internal // /// /// Iterate the stream, flowing values downstream to the reducer, which aggregates a /// result value. This is returned lifted. /// /// Note, this is recursive, so `M` needs to be able to support recursion without /// blowing the stack. If you have the `IO` monad in your stack, then this will automatically /// be the case. /// Initial state /// Reducer /// State type /// Lifted aggregate state public K> ReduceInternal(S state, ReducerM reducer) => ReduceInternalM(state, (s, mx) => mx.Bind(x => reducer(s, x))); /// /// Iterate the stream, flowing values downstream to the reducer, which aggregates a /// result value. This is returned lifted. /// /// Note, this is recursive, so `M` needs to be able to support recursion without /// blowing the stack. If you have the `IO` monad in your stack, then this will automatically /// be the case. /// Initial state /// Reducer /// State type /// Lifted aggregate state internal abstract K> ReduceInternalM(S state, ReducerM, S> reducer); } ================================================ FILE: LanguageExt.Streaming/SourceT/Trait/SourceT.TraitImpl.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public partial class SourceT : MonoidK>, MonadUnliftIO> where M : MonadIO { static K, B> Monad>.Bind(K, A> ma, Func, B>> f) => ma.As().Bind(f); static K, B> Monad>.Recur(A value, Func, Next>> f) => Monad.unsafeRecur(value, f); static K, B> Functor>.Map(Func f, K, A> ma) => ma.As().Map(f); static K, A> Applicative>.Pure(A value) => new PureSourceT(value); static K, B> Applicative>.Apply(K, Func> mf, K, A> ma) => mf >> ma.Map; static K, B> Applicative>.Apply(K, Func> mf, Memo, A> ma) => mf >> ma.Map; static K, A> SemigroupK>.Combine(K, A> fa, K, A> fb) => fa.As().Combine(fb.As()); static K, A> MonoidK>.Empty() => EmptySourceT.Default; static K, A> MonadIO>.LiftIO(IO ma) => SourceT.liftIO(ma); static K, IO> MonadUnliftIO>.ToIO(K, A> ma) => new ToIOSourceT(ma.As()); static K, B> MonadUnliftIO>.MapIO(K, A> ma, Func, IO> f) => new MapIOSourceT(ma.As(), f); } ================================================ FILE: LanguageExt.Streaming/Transducers/Reduced.cs ================================================ using System; using System.Threading.Tasks; namespace LanguageExt; /// /// Result of a `Reducer` delegate /// public readonly record struct Reduced(bool Continue, A Value) { public Reduced Map(Func f) => new (Continue, f(Value)); } /// /// `Reduced〈A〉` constructors /// public static class Reduced { static Reduced() { Unit = Continue(default); UnitAsync = new ValueTask>(Unit); } /// /// Return a result and indicate that we're happy to continue /// /// Result /// Result value type /// `Reduced` structure in a continue state public static Reduced Continue(A value) => new(true, value); /// /// Return a result and indicate that we're done and don't want to process any more /// /// Result /// Result value type /// `Reduced` structure in a done state public static Reduced Done(A value) => new(false, value); /// /// `Reduced〈A〉` in a continue state /// public static readonly Reduced Unit; /// /// Return a result and indicate that we're happy to continue /// /// Result /// Result value type /// `Reduced` structure in a continue state public static IO> ContinueIO(A value) => IO.pure(new Reduced(true, value)); /// /// Return a result and indicate that we're done and don't want to process any more /// /// Result /// Result value type /// `Reduced` structure in a done state public static IO> DoneIO(A value) => IO.pure(new Reduced(false, value)); /// /// `Reduced〈A〉` in a continue state /// public static readonly ValueTask> UnitAsync; } ================================================ FILE: LanguageExt.Streaming/Transducers/ReducedM.cs ================================================ using System.Threading.Tasks; using LanguageExt.Traits; namespace LanguageExt; /// /// Result of a `Reducer` delegate /// public readonly record struct ReducedM(bool Continue, K Value); /// /// `Reduced〈A〉` constructors /// public static class ReducedM { /// /// Return a result and indicate that we're happy to continue /// /// Result /// Result value type /// `ReducedM` structure in a continue state public static ReducedM Continue(A value) where M : Applicative => new(true, M.Pure(value)); /// /// Return a result and indicate that we're happy to continue /// /// Result /// Result value type /// `ReducedM` structure in a continue state public static ReducedM Continue(K ma) where M : Applicative => new(true, ma); /// /// Return a result and indicate that we're done and don't want to process any more /// /// Result /// Result value type /// `ReducedM` structure in a done state public static ReducedM Done(A value) where M : Applicative => new(false, M.Pure(value)); /// /// Return a result and indicate that we're done and don't want to process any more /// /// Result /// Result value type /// `ReducedM` structure in a done state public static ReducedM Done(K ma) where M : Applicative => new(false, ma); /// /// `ReducedM〈A〉` in a continue state /// public static ReducedM Unit() where M : Applicative => ReducedMInternal.Unit; /// /// Return a result and indicate that we're happy to continue /// /// Result /// Result value type /// `ReducedM` structure in a continue state public static ValueTask> ContinueAsync(A value) where M : Applicative => new(new ReducedM(true, M.Pure(value))); /// /// Return a result and indicate that we're happy to continue /// /// Result /// Result value type /// `ReducedM` structure in a continue state public static ValueTask> ContinueAsync(K ma) where M : Applicative => new(new ReducedM(true, ma)); /// /// Return a result and indicate that we're done and don't want to process any more /// /// Result /// Result value type /// `ReducedM` structure in a done state public static ValueTask> DoneAsync(A value) where M : Applicative => new(new ReducedM(false, M.Pure(value))); /// /// Return a result and indicate that we're done and don't want to process any more /// /// Result /// Result value type /// `ReducedM` structure in a done state public static ValueTask> DoneAsync(K ma) where M : Applicative => new(new ReducedM(false, ma)); /// /// `ReducedM〈A〉` in a continue state /// public static ValueTask> UnitAsync() where M : Applicative => ReducedMInternal.UnitAsync; } /// /// `Reduced〈A〉` constructors /// static class ReducedMInternal where M : Applicative { static ReducedMInternal() { Unit = new ReducedM(true, M.Pure(default)); UnitAsync = new ValueTask>(Unit); } /// /// `ReducedM〈A〉` in a continue state /// public static readonly ReducedM Unit; /// /// `ReducedM〈A〉` in a continue state /// public static readonly ValueTask> UnitAsync; } ================================================ FILE: LanguageExt.Streaming/Transducers/Reducer.cs ================================================ using System.Threading.Tasks; using LanguageExt.Traits; namespace LanguageExt; /// /// Reducer delegate /// /// State /// Value public delegate Reduced Reducer(S state, A input); ================================================ FILE: LanguageExt.Streaming/Transducers/ReducerIO.cs ================================================ using System.Threading.Tasks; using LanguageExt.Traits; namespace LanguageExt; /// /// ReducerAsync delegate /// /// State /// Value public delegate IO> ReducerIO(S state, A input); ================================================ FILE: LanguageExt.Streaming/Transducers/ReducerM.cs ================================================ using LanguageExt.Traits; namespace LanguageExt; /// /// ReducerM delegate /// /// State /// Value public delegate K> ReducerM(S state, A input); ================================================ FILE: LanguageExt.Streaming/Transducers/Transducer/DSL/BindTransducer.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; record BindTransducer1(Transducer First, Func, B>> F) : Transducer { public override ReducerIO Reduce(ReducerIO reducer) => (s, env) => First.Reduce( (s1, x) => F(x).As().Reduce(reducer)(s1, env))(s, env); public override TransducerM Lift() => new BindTransducerM1(First.Lift(), x => F(x).As().Lift()); } record BindTransducer2(Transducer First, Func> F) : Transducer { public override ReducerIO Reduce(ReducerIO reducer) => (s, env) => First.Reduce( (s1, x) => F(x).Reduce(reducer)(s1, env))(s, env); public override TransducerM Lift() => new BindTransducerM2(First.Lift(), x => F(x).Lift()); } ================================================ FILE: LanguageExt.Streaming/Transducers/Transducer/DSL/ComposeTransducer.cs ================================================ using System.Threading.Tasks; using LanguageExt.Traits; namespace LanguageExt; record ComposeTransducer(Transducer TF, Transducer TG) : Transducer { public override ReducerIO Reduce(ReducerIO reducer) => (s, x) => TF.Reduce((s1, y) => TG.Reduce(reducer)(s1, y))(s, x); public override Transducer Compose(Transducer t) => new ComposeTransducer(TF, TG, t); public override TransducerM Lift() => new ComposeTransducerM(TF.Lift(), TG.Lift()); } record ComposeTransducer(Transducer TF, Transducer TG, Transducer TH) : Transducer { public override ReducerIO Reduce(ReducerIO reducer) => (s, x) => TF.Reduce((s1, y) => TG.Reduce((s2, z) => TH.Reduce(reducer)(s2, z))(s1, y))(s, x); public override Transducer Compose(Transducer t) => new ComposeTransducer(TF, TG, TH, t); public override TransducerM Lift() => new ComposeTransducerM(TF.Lift(), TG.Lift(), TH.Lift()); } record ComposeTransducer( Transducer TF, Transducer TG, Transducer TH, Transducer TI) : Transducer { public override ReducerIO Reduce(ReducerIO reducer) => (s, w) => TF.Reduce( (s1, x) => TG.Reduce( (s2, y) => TH.Reduce( (s3, z) => TI.Reduce(reducer)(s3, z)) (s2, y)) (s1, x)) (s, w); public override Transducer Compose(Transducer t) => new ComposeTransducer(TF, TG, TH, TI, t); public override TransducerM Lift() => new ComposeTransducerM(TF.Lift(), TG.Lift(), TH.Lift(), TI.Lift()); } record ComposeTransducer( Transducer TF, Transducer TG, Transducer TH, Transducer TI, Transducer TJ) : Transducer { public override ReducerIO Reduce(ReducerIO reducer) => (s, v) => TF.Reduce( (s1, w) => TG.Reduce( (s2, x) => TH.Reduce( (s3, y) => TI.Reduce( (s4, z) => TJ.Reduce(reducer)(s4, z)) (s3, y)) (s2, x)) (s1, w)) (s, v); public override Transducer Compose(Transducer t) => new ComposeTransducer(TF, TG, TH, TI, TJ, t); public override TransducerM Lift() => new ComposeTransducerM(TF.Lift(), TG.Lift(), TH.Lift(), TI.Lift(), TJ.Lift()); } record ComposeTransducer( Transducer TF, Transducer TG, Transducer TH, Transducer TI, Transducer TJ, Transducer TK) : Transducer { public override ReducerIO Reduce(ReducerIO reducer) => (s, u) => TF.Reduce( (s1, v) => TG.Reduce( (s2, w) => TH.Reduce( (s3, x) => TI.Reduce( (s4, y) => TJ.Reduce( (s5, z) => TK.Reduce(reducer)(s5, z)) (s4, y)) (s3, x)) (s2, w)) (s1, v)) (s, u); public override TransducerM Lift() => new ComposeTransducerM(TF.Lift(), TG.Lift(), TH.Lift(), TI.Lift(), TJ.Lift(), TK.Lift()); } ================================================ FILE: LanguageExt.Streaming/Transducers/Transducer/DSL/ConstTransducer.cs ================================================ namespace LanguageExt; record ConstTransducer(B Value) : Transducer { public override ReducerIO Reduce(ReducerIO reducer) => (s, _) => reducer(s, Value); public override TransducerM Lift() => new ConstTransducerM(Value); } ================================================ FILE: LanguageExt.Streaming/Transducers/Transducer/DSL/FilterTransducer.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; record FilterTransducer(Func Predicate) : Transducer { public override ReducerIO Reduce(ReducerIO reducer) => (s, x) => IO.liftVAsync(async e => !e.Token.IsCancellationRequested && Predicate(x) ? await reducer(s, x).RunAsync(e.Token) : Reduced.Continue(s)); public override TransducerM Lift() => new FilterTransducerM(Predicate); } ================================================ FILE: LanguageExt.Streaming/Transducers/Transducer/DSL/FoldUntilTransducer.cs ================================================ using System; using System.Threading.Tasks; namespace LanguageExt; record FoldUntilTransducer( Func Folder, Func Pred, S State) : Transducer { public override ReducerIO Reduce(ReducerIO reducer) { var state = State; return (s1, x) => IO.liftVAsync(async e => { if(e.Token.IsCancellationRequested) return Reduced.Done(s1); state = Folder(state, x); if (Pred(state, x)) { switch (await reducer(s1, state).RunAsync(e)) { case { Continue: true, Value: var nstate }: state = State; // reset return Reduced.Continue(nstate); case { Value: var nstate }: return Reduced.Done(nstate); } } else { return Reduced.Continue(s1); } }); } public override TransducerM Lift() => new FoldUntilTransducerM2(Schedule.Forever, Folder, Pred, State); } record FoldUntilTransducer2( Schedule Schedule, Func Folder, Func Pred, S State) : Transducer { public override ReducerIO Reduce(ReducerIO reducer) { var state = State; var sch = Duration.Zero.Cons(Schedule.Run()).GetEnumerator(); return (s1, x) => IO.liftVAsync(async e => { if(e.Token.IsCancellationRequested) return Reduced.Done(s1); state = Folder(state, x); if (Pred(state, x)) { // Schedule if (sch.MoveNext()) { if (!sch.Current.IsZero) await Task.Delay((TimeSpan)sch.Current); } else { return Reduced.Done(s1); } switch (await reducer(s1, state).RunAsync(e)) { case { Continue: true, Value: var nstate }: state = State; // reset return Reduced.Continue(nstate); case { Value: var nstate }: return Reduced.Done(nstate); } } else { return Reduced.Continue(s1); } }); } public override TransducerM Lift() => new FoldUntilTransducerM2(Schedule, Folder, Pred, State); } ================================================ FILE: LanguageExt.Streaming/Transducers/Transducer/DSL/FoldWhileTransducer.cs ================================================ using System; using System.Threading.Tasks; namespace LanguageExt; record FoldWhileTransducer( Func Folder, Func Pred, S State) : Transducer { public override ReducerIO Reduce(ReducerIO reducer) { var state = State; return (s1, x) => IO.liftVAsync(async e => { if(e.Token.IsCancellationRequested) return Reduced.Done(s1); if (Pred(state, x)) { state = Folder(state, x); return Reduced.Continue(s1); } else { switch (await reducer(s1, state).RunAsync(e)) { case { Continue: true, Value: var nstate }: state = Folder(State /* reset */, x); return Reduced.Continue(nstate); case { Value: var nstate }: return Reduced.Done(nstate); } } }); } public override TransducerM Lift() => new FoldWhileTransducerM(Folder, Pred, State); } record FoldWhileTransducer2( Schedule Schedule, Func Folder, Func Pred, S State) : Transducer { public override ReducerIO Reduce(ReducerIO reducer) { var state = State; var sch = Duration.Zero.Cons(Schedule.Run()).GetEnumerator(); return (s1, x) => IO.liftVAsync(async e => { if (Pred(state, x)) { state = Folder(state, x); return Reduced.Continue(s1); } else { // Schedule if (sch.MoveNext()) { if (!sch.Current.IsZero) await Task.Delay((TimeSpan)sch.Current); } else { return Reduced.Done(s1); } switch (await reducer(s1, state).RunAsync(e)) { case { Continue: true, Value: var nstate }: state = Folder(State /* reset */, x); return Reduced.Continue(nstate); case { Value: var nstate }: return Reduced.Done(nstate); } } }); } public override TransducerM Lift() => new FoldWhileTransducerM2(Schedule, Folder, Pred, State); } ================================================ FILE: LanguageExt.Streaming/Transducers/Transducer/DSL/IdentityTransducer.cs ================================================ namespace LanguageExt; record IdentityTransducer : Transducer { public static readonly Transducer Default = new IdentityTransducer(); public override ReducerIO Reduce(ReducerIO reducer) => reducer; public override TransducerM Lift() => new IdentityTransducerM(); } ================================================ FILE: LanguageExt.Streaming/Transducers/Transducer/DSL/MapTransducer.cs ================================================ using System; namespace LanguageExt; record MapTransducer(Func F) : Transducer { public override ReducerIO Reduce(ReducerIO reducer) => (s, x) => reducer(s, F(x)); public override TransducerM Lift() => new MapTransducerM(F); } ================================================ FILE: LanguageExt.Streaming/Transducers/Transducer/DSL/SelectManyTransducer.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; record SelectManyTransducer1( Transducer First, Func, B>> F, Func G) : Transducer { public override ReducerIO Reduce(ReducerIO reducer) => (s, env) => First.Reduce((s1, x) => F(x).As().Reduce((s2, y) => reducer(s2, G(x, y)))(s1, env))(s, env); public override TransducerM Lift() => new SelectManyTransducerM1(First.Lift(), x => F(x).As().Lift(), G); } record SelectManyTransducer2(Transducer First, Func> F, Func G) : Transducer { public override ReducerIO Reduce(ReducerIO reducer) => (s, env) => First.Reduce( (s1, x) => F(x).Reduce((s2, y) => reducer(s2, G(x, y)))(s1, env))(s, env); public override TransducerM Lift() => new SelectManyTransducerM2(First.Lift(), x => F(x).Lift(), G); } ================================================ FILE: LanguageExt.Streaming/Transducers/Transducer/DSL/SkipTransducer.cs ================================================ namespace LanguageExt; record SkipTransducer(int Amount) : Transducer { public override ReducerIO Reduce(ReducerIO reducer) { var amount = Amount; return (s, x) => IO.liftVAsync(async e => { if(e.Token.IsCancellationRequested) return Reduced.Done(s); if (amount > 0) { amount--; return Reduced.Continue(s); } else { return await reducer(s, x).RunAsync(e); } }); } public override TransducerM Lift() => new SkipTransducerM(Amount); } ================================================ FILE: LanguageExt.Streaming/Transducers/Transducer/DSL/TakeTransducer.cs ================================================ namespace LanguageExt; record TakeTransducer(int Amount) : Transducer { public override ReducerIO Reduce(ReducerIO reducer) { var taken = 0; return (s, x) => IO.liftVAsync(async e => { if(e.Token.IsCancellationRequested) return Reduced.Done(s); if (taken < Amount) { taken++; return await reducer(s, x).RunAsync(e); } else { return Reduced.Done(s); } }); } public override TransducerM Lift() => new TakeTransducerM(Amount); } ================================================ FILE: LanguageExt.Streaming/Transducers/Transducer/TransduceFrom.TraitImpl.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public class TransduceFrom : Monad>, Readable, IN> { static K, B> Monad>.Bind( K, A> ma, Func, B>> f) => ma.As().Bind(f); static K, B> Monad>.Recur(A value, Func, Next>> f) => Monad.unsafeRecur(value, f); static K, B> Functor>.Map( Func f, K, A> ma) => ma.As().Map(f); static K, A> Applicative>.Pure(A value) => Transducer.constant(value); static K, B> Applicative>.Apply( K, Func> mf, K, A> ma) => mf.Bind(ma.Map); static K, B> Applicative>.Apply( K, Func> mf, Memo, A> ma) => mf.Bind(ma.Map); public static K, A> Asks(Func f) => Transducer.map(f); public static K, A> Local(Func f, K, A> ma) => ma.As().Comap(f); } ================================================ FILE: LanguageExt.Streaming/Transducers/Transducer/TransduceTo.TraitImpl.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public class TransduceTo : Cofunctor> { static K, A> Cofunctor>. Comap(Func f, K, B> fb) => Transducer.compose(Transducer.map(f), fb.As()); } ================================================ FILE: LanguageExt.Streaming/Transducers/Transducer/Transducer.Extensions.cs ================================================ using LanguageExt.Traits; namespace LanguageExt; public static class TransducerExtensions { /// /// Downcast operator /// public static Transducer As(this K, B> ma) => (Transducer)ma; /// /// Downcast operator /// public static Transducer As(this K, A> ma) => (Transducer)ma; } ================================================ FILE: LanguageExt.Streaming/Transducers/Transducer/Transducer.Module.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public static class Transducer { /// /// Identity transducer. Has no effect on values flowing through. /// /// Bound value type /// Identity transducer public static Transducer identity() => IdentityTransducer.Default; /// /// Constant transducer. Ignores all incoming values and yields the constant value. /// /// Constant value type /// Constant transducer public static Transducer constant(B value) => new ConstTransducer(value); /// /// Compose transducers together. The output of each transducer is fed into the next transducer. /// /// First transducer /// Second transducer /// Composed transducer public static Transducer compose( Transducer ta, Transducer tb) => new ComposeTransducer(ta, tb); /// /// Compose transducers together. The output of each transducer is fed into the next transducer. /// /// First transducer /// Second transducer /// Third transducer /// Composed transducer public static Transducer compose( Transducer ta, Transducer tb, Transducer tc) => new ComposeTransducer(ta, tb, tc); /// /// Compose transducers together. The output of each transducer is fed into the next transducer. /// /// First transducer /// Second transducer /// Third transducer /// Fourth transducer /// Composed transducer public static Transducer compose( Transducer ta, Transducer tb, Transducer tc, Transducer td) => new ComposeTransducer(ta, tb, tc, td); /// /// Compose transducers together. The output of each transducer is fed into the next transducer. /// /// First transducer /// Second transducer /// Third transducer /// Fourth transducer /// Fifth transducer /// Composed transducer public static Transducer compose( Transducer ta, Transducer tb, Transducer tc, Transducer td, Transducer te) => new ComposeTransducer(ta, tb, tc, td, te); /// /// Compose transducers together. The output of each transducer is fed into the next transducer. /// /// First transducer /// Second transducer /// Third transducer /// Fourth transducer /// Fifth transducer /// Sixth transducer /// Composed transducer public static Transducer compose( Transducer ta, Transducer tb, Transducer tc, Transducer td, Transducer te, Transducer tf) => new ComposeTransducer(ta, tb, tc, td, te, tf); /// /// Skip `amount` items in the sequence before yielding /// /// Number of items to skip /// Value type /// Transducer that skips values public static Transducer skip(int amount) => new SkipTransducer(amount); /// /// Take `amount` items in the sequence before terminating /// /// Number of items to take /// Value type /// Transducer that takes `amount` values only public static Transducer take(int amount) => new TakeTransducer(amount); /// /// Functor map transducer /// /// Function to map values of type `A` to values of type `B` /// Input value type /// Output value type /// Mapping transducer public static Transducer map(Func f) => new MapTransducer(f); /// /// Applicative filter transducer /// /// Filters each value flowing through the transducer. If `true` the value will flow /// downstream; if `false`, the value is dropped /// Bound value type /// Filtering transducer public static Transducer filter(Func predicate) => new FilterTransducer(predicate); /// /// Monad bind transducer /// /// /// Chains two transducers together /// /// Initial transducer to run /// Chaining function to run with the result of `ta` that will produce a new `Transducer` /// Input value type /// Result value type of the first transducer /// Result value type of returned transducer /// A monadic bind transducer operation public static Transducer bind(Transducer ta, Func, B>> f) => new BindTransducer1(ta, f); /// /// Monad bind transducer /// /// /// Chains two transducers together /// /// Initial transducer to run /// Chaining function to run with the result of `ta` that will produce a new `Transducer` /// Input value type /// Result value type of the first transducer /// Result value type of returned transducer /// A monadic bind transducer operation public static Transducer bind(Transducer ta, Func> f) => new BindTransducer2(ta, f); /// /// Fold items in the stream while the predicate returns true; once the predicate returns false, the /// aggregated value is yielded downstream. /// /// Aggregating binary fold function /// Predicate /// Initial state /// Bound value type /// Yielded aggregate value type /// Aggregating binary folding transducer public static Transducer foldWhile( Func Folder, Func Pred, S State) => new FoldWhileTransducer(Folder, Pred, State); /// /// Fold items in the stream while the predicate returns true; once the predicate returns false, the /// aggregated value is yielded downstream. /// /// Schedule for each yielded item /// Aggregating binary fold function /// Predicate /// Initial state /// Bound value type /// Yielded aggregate value type /// Aggregating binary folding transducer public static Transducer foldWhile( Schedule Schedule, Func Folder, Func Pred, S State) => new FoldWhileTransducer2(Schedule, Folder, Pred, State); /// /// Fold items in the stream until the predicate returns true; once the predicate returns true, the /// aggregated value is yielded downstream. /// /// Aggregating binary fold function /// Predicate /// Initial state /// Bound value type /// Yielded aggregate value type /// Aggregating binary folding transducer public static Transducer foldUntil( Func Folder, Func Pred, S State) => new FoldUntilTransducer(Folder, Pred, State); /// /// Fold items in the stream until the predicate returns true; once the predicate returns true, the /// aggregated value is yielded downstream. /// /// Schedule for each yielded item /// Aggregating binary fold function /// Predicate /// Initial state /// Bound value type /// Yielded aggregate value type /// Aggregating binary folding transducer public static Transducer foldUntil( Schedule Schedule, Func Folder, Func Pred, S State) => new FoldUntilTransducer2(Schedule, Folder, Pred, State); } ================================================ FILE: LanguageExt.Streaming/Transducers/Transducer/Transducer.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public abstract record Transducer : K, B>, K, A> { /// /// Fold the input stream using the supplied reducer. /// /// Reducer that folds the stream of values flowing through the transducer /// State /// public abstract ReducerIO Reduce(ReducerIO reducer); /// /// Lift the pure value transducer into a `M` value transducer /// /// Lifted space /// Lifted transducer public abstract TransducerM Lift() where M : Applicative; /// /// Compose two transducers together. The output of the first transducer is the input to the second. /// public virtual Transducer Compose(Transducer tg) => new ComposeTransducer(this, tg); /// /// Functor map /// public Transducer Map(Func f) => Compose(Transducer.map(f)); /// /// Contravariant functor map /// public Transducer Comap(Func f) => Transducer.map(f).Compose(this); /// /// Monadic bind /// public Transducer Bind(Func, C>> f) => new BindTransducer1(this, f); /// /// Monadic bind /// public Transducer Bind(Func> f) => new BindTransducer2(this, f); /// /// Monadic bind /// public Transducer SelectMany(Func, C>> bind, Func project) => new SelectManyTransducer1(this, bind, project); /// /// Monadic bind /// public Transducer SelectMany(Func> bind, Func project) => new SelectManyTransducer2(this, bind, project); /// /// Filter values flowing through the transducer. /// public Transducer Filter(Func f) => Compose(Transducer.filter(f)); /// /// Filter values flowing through the transducer. /// public Transducer Where(Func f) => Compose(Transducer.filter(f)); /// /// Take the first `n` values from the stream. /// public Transducer Take(int n) => Compose(Transducer.take(n)); /// /// Skip the first `n` values from the stream. /// public Transducer Skip(int n) => Compose(Transducer.skip(n)); public Transducer FoldWhile( Func Folder, Func Pred, S State) => Compose(Transducer.foldWhile(Folder, Pred, State)); public Transducer FoldWhile( Schedule Schedule, Func Folder, Func Pred, S State) => Compose(Transducer.foldWhile(Schedule, Folder, Pred, State)); public Transducer FoldUntil( Func Folder, Func Pred, S State) => Compose(Transducer.foldUntil(Folder, Pred, State)); public Transducer FoldUntil( Schedule Schedule, Func Folder, Func Pred, S State) => Compose(Transducer.foldUntil(Schedule, Folder, Pred, State)); } ================================================ FILE: LanguageExt.Streaming/Transducers/TransducerM/DSL/BindTransducer.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; record BindTransducerM1(TransducerM First, Func, B>> F) : TransducerM { public override ReducerM Reduce(ReducerM reducer) => (s, env) => First.Reduce( (s1, x) => F(x).As().Reduce(reducer)(s1, env))(s, env); } record BindTransducerM2(TransducerM First, Func> F) : TransducerM { public override ReducerM Reduce(ReducerM reducer) => (s, env) => First.Reduce( (s1, x) => F(x).Reduce(reducer)(s1, env))(s, env); } ================================================ FILE: LanguageExt.Streaming/Transducers/TransducerM/DSL/ComposeTransducer.cs ================================================ namespace LanguageExt; record ComposeTransducerM(TransducerM TF, TransducerM TG) : TransducerM { public override ReducerM Reduce(ReducerM reducer) => (s, x) => TF.Reduce((s1, y) => TG.Reduce(reducer)(s1, y))(s, x); public override TransducerM Compose(TransducerM t) => new ComposeTransducerM(TF, TG, t); } record ComposeTransducerM(TransducerM TF, TransducerM TG, TransducerM TH) : TransducerM { public override ReducerM Reduce(ReducerM reducer) => (s, x) => TF.Reduce((s1, y) => TG.Reduce((s2, z) => TH.Reduce(reducer)(s2, z))(s1, y))(s, x); public override TransducerM Compose(TransducerM t) => new ComposeTransducerM(TF, TG, TH, t); } record ComposeTransducerM( TransducerM TF, TransducerM TG, TransducerM TH, TransducerM TI) : TransducerM { public override ReducerM Reduce(ReducerM reducer) => (s, w) => TF.Reduce( (s1, x) => TG.Reduce( (s2, y) => TH.Reduce( (s3, z) => TI.Reduce(reducer)(s3, z)) (s2, y)) (s1, x)) (s, w); public override TransducerM Compose(TransducerM t) => new ComposeTransducerM(TF, TG, TH, TI, t); } record ComposeTransducerM( TransducerM TF, TransducerM TG, TransducerM TH, TransducerM TI, TransducerM TJ) : TransducerM { public override ReducerM Reduce(ReducerM reducer) => (s, v) => TF.Reduce( (s1, w) => TG.Reduce( (s2, x) => TH.Reduce( (s3, y) => TI.Reduce( (s4, z) => TJ.Reduce(reducer)(s4, z)) (s3, y)) (s2, x)) (s1, w)) (s, v); public override TransducerM Compose(TransducerM t) => new ComposeTransducerM(TF, TG, TH, TI, TJ, t); } record ComposeTransducerM( TransducerM TF, TransducerM TG, TransducerM TH, TransducerM TI, TransducerM TJ, TransducerM TK) : TransducerM { public override ReducerM Reduce(ReducerM reducer) => (s, u) => TF.Reduce( (s1, v) => TG.Reduce( (s2, w) => TH.Reduce( (s3, x) => TI.Reduce( (s4, y) => TJ.Reduce( (s5, z) => TK.Reduce(reducer)(s5, z)) (s4, y)) (s3, x)) (s2, w)) (s1, v)) (s, u); } ================================================ FILE: LanguageExt.Streaming/Transducers/TransducerM/DSL/ConstTransducer.cs ================================================ namespace LanguageExt; record ConstTransducerM(B Value) : TransducerM { public override ReducerM Reduce(ReducerM reducer) => (s, _) => reducer(s, Value); } ================================================ FILE: LanguageExt.Streaming/Transducers/TransducerM/DSL/FilterTransducer.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; record FilterTransducerM(Func Predicate) : TransducerM where M : Applicative { public override ReducerM Reduce(ReducerM reducer) => (s, x) => Predicate(x) ? reducer(s, x) : M.Pure(Reduced.Continue(s)); } ================================================ FILE: LanguageExt.Streaming/Transducers/TransducerM/DSL/FoldUntilTransducer.cs ================================================ using System; using System.Threading.Tasks; using LanguageExt.Traits; namespace LanguageExt; record FoldUntilTransducerM( Func Folder, Func Pred, S State) : TransducerM where M : Applicative { public override ReducerM Reduce(ReducerM reducer) { var state = State; return (s1, x) => { state = Folder(state, x); if (Pred(state, x)) { return reducer(s1, state); } else { return M.Pure(Reduced.Done(s1)); } }; } } record FoldUntilTransducerM2( Schedule Schedule, Func Folder, Func Pred, S State) : TransducerM where M : Applicative { public override ReducerM Reduce(ReducerM reducer) { var state = State; var sch = Duration.Zero.Cons(Schedule.Run()).GetEnumerator(); return (s1, x) => { state = Folder(state, x); if (Pred(state, x)) { // Schedule if (sch.MoveNext()) { if (!sch.Current.IsZero) Task.Delay((TimeSpan)sch.Current).GetAwaiter().GetResult(); } else { return M.Pure(Reduced.Done(s1)); } return reducer(s1, state); } else { return M.Pure(Reduced.Done(s1)); } }; } } ================================================ FILE: LanguageExt.Streaming/Transducers/TransducerM/DSL/FoldWhileTransducer.cs ================================================ using System; using System.Threading.Tasks; using LanguageExt.Traits; namespace LanguageExt; record FoldWhileTransducerM( Func Folder, Func Pred, S State) : TransducerM where M : Applicative { public override ReducerM Reduce(ReducerM reducer) { var state = State; return (s1, x) => { if (Pred(state, x)) { state = Folder(state, x); return M.Pure(Reduced.Done(s1)); } else { return reducer(s1, state) .Map(ns => { state = Folder(State /* reset */, x); return ns; }); } }; } } record FoldWhileTransducerM2( Schedule Schedule, Func Folder, Func Pred, S State) : TransducerM where M : Applicative { public override ReducerM Reduce(ReducerM reducer) { var state = State; var sch = Duration.Zero.Cons(Schedule.Run()).GetEnumerator(); return (s1, x) => { if (Pred(state, x)) { state = Folder(state, x); return M.Pure(Reduced.Done(s1)); } else { // Schedule if (sch.MoveNext()) { if (!sch.Current.IsZero) Task.Delay((TimeSpan)sch.Current).GetAwaiter().GetResult(); } else { return M.Pure(Reduced.Done(s1)); } return reducer(s1, state) .Map(ns => { state = Folder(State /* reset */, x); return ns; }); } }; } } ================================================ FILE: LanguageExt.Streaming/Transducers/TransducerM/DSL/IdentityTransducer.cs ================================================ namespace LanguageExt; record IdentityTransducerM : TransducerM { public static readonly TransducerM Default = new IdentityTransducerM(); public override ReducerM Reduce(ReducerM reducer) => reducer; } ================================================ FILE: LanguageExt.Streaming/Transducers/TransducerM/DSL/MapTransducer.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; record MapTransducerM(Func F) : TransducerM { public override ReducerM Reduce(ReducerM reducer) => (s, x) => reducer(s, F(x)); } ================================================ FILE: LanguageExt.Streaming/Transducers/TransducerM/DSL/SelectManyTransducer.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; record SelectManyTransducerM1(TransducerM First, Func, B>> F, Func G) : TransducerM { public override ReducerM Reduce(ReducerM reducer) => (s, env) => First.Reduce( (s1, x) => F(x).As().Reduce((s2, y) => reducer(s2, G(x, y)))(s1, env))(s, env); } record SelectManyTransducerM2(TransducerM First, Func> F, Func G) : TransducerM { public override ReducerM Reduce(ReducerM reducer) => (s, env) => First.Reduce( (s1, x) => F(x).Reduce((s2, y) => reducer(s2, G(x, y)))(s1, env))(s, env); } ================================================ FILE: LanguageExt.Streaming/Transducers/TransducerM/DSL/SkipTransducer.cs ================================================ using LanguageExt.Traits; namespace LanguageExt; record SkipTransducerM(int Amount) : TransducerM where M : Applicative { public override ReducerM Reduce(ReducerM reducer) { var amount = Amount; return (s, x) => { if (amount > 0) { amount--; return M.Pure(Reduced.Continue(s)); } else { return reducer(s, x); } }; } } ================================================ FILE: LanguageExt.Streaming/Transducers/TransducerM/DSL/TakeTransducer.cs ================================================ using LanguageExt.Traits; namespace LanguageExt; record TakeTransducerM(int Amount) : TransducerM where M : Applicative { public override ReducerM Reduce(ReducerM reducer) { var taken = 0; return (s, x) => { if (taken < Amount) { taken++; return reducer(s, x); } else { return M.Pure(Reduced.Done(s)); } }; } } ================================================ FILE: LanguageExt.Streaming/Transducers/TransducerM/TransduceFromM.TraitImpl.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public class TransduceFromM : Monad>, Readable, IN> { static K, B> Monad>.Bind( K, A> ma, Func, B>> f) => ma.As().Bind(f); static K, B> Monad>.Recur(A value, Func, Next>> f) => Monad.unsafeRecur(value, f); static K, B> Functor>.Map( Func f, K, A> ma) => ma.As().Map(f); static K, A> Applicative>.Pure(A value) => TransducerM.constant(value); static K, B> Applicative>.Apply( K, Func> mf, K, A> ma) => mf.Bind(ma.Map); static K, B> Applicative>.Apply( K, Func> mf, Memo, A> ma) => mf.Bind(ma.Map); public static K, A> Asks(Func f) => TransducerM.map(f); public static K, A> Local(Func f, K, A> ma) => ma.As().Comap(f); } ================================================ FILE: LanguageExt.Streaming/Transducers/TransducerM/TransduceToM.TraitImpl.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public class TransduceToM : Cofunctor> { static K, A> Cofunctor>. Comap(Func f, K, B> fb) => TransducerM.map(f).Compose(fb.As()); } ================================================ FILE: LanguageExt.Streaming/Transducers/TransducerM/TransducerM.Extensions.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public static class TransducerMExtensions { /// /// Downcast operator /// public static TransducerM As(this K, B> ma) => (TransducerM)ma; /// /// Downcast operator /// public static TransducerM As(this K, A> ma) => (TransducerM)ma; /// /// Filter values flowing through the transducer. /// public static TransducerM Filter(this TransducerM tab, Func f) where M : Applicative => tab.Compose(TransducerM.filter(f)); /// /// Filter values flowing through the transducer. /// public static TransducerM Where(this TransducerM tab, Func f) where M : Applicative => tab.Compose(TransducerM.filter(f)); /// /// Take the first `n` values from the stream. /// public static TransducerM Take(this TransducerM tab, int n) where M : Applicative => tab.Compose(TransducerM.take(n)); /// /// Skip the first `n` values from the stream. /// public static TransducerM Skip(this TransducerM tab, int n) where M : Applicative => tab.Compose(TransducerM.skip(n)); public static TransducerM FoldWhile( this TransducerM tab, Func Folder, Func Pred, S State) where M : Applicative => tab.Compose(TransducerM.foldWhile(Folder, Pred, State)); public static TransducerM FoldWhile( this TransducerM tab, Schedule Schedule, Func Folder, Func Pred, S State) where M : Applicative => tab.Compose(TransducerM.foldWhile(Schedule, Folder, Pred, State)); public static TransducerM FoldUntil( this TransducerM tab, Func Folder, Func Pred, S State) where M : Applicative => tab.Compose(TransducerM.foldUntil(Folder, Pred, State)); public static TransducerM FoldUntil( this TransducerM tab, Schedule Schedule, Func Folder, Func Pred, S State) where M : Applicative => tab.Compose(TransducerM.foldUntil(Schedule, Folder, Pred, State)); } ================================================ FILE: LanguageExt.Streaming/Transducers/TransducerM/TransducerM.Module.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public static class TransducerM { /// /// Identity transducer. Has no effect on values flowing through. /// /// Bound value type /// Identity transducer public static TransducerM identity() => IdentityTransducerM.Default; /// /// Constant transducer. Ignores all incoming values and yields the constant value. /// /// Constant value type /// Constant transducer public static TransducerM constant(B value) => new ConstTransducerM(value); /// /// Compose transducers together. The output of each transducer is fed into the next transducer. /// /// First transducer /// Second transducer /// Composed transducer public static TransducerM compose( TransducerM ta, TransducerM tb) => new ComposeTransducerM(ta, tb); /// /// Compose transducers together. The output of each transducer is fed into the next transducer. /// /// First transducer /// Second transducer /// Third transducer /// Composed transducer public static TransducerM compose( TransducerM ta, TransducerM tb, TransducerM tc) => new ComposeTransducerM(ta, tb, tc); /// /// Compose transducers together. The output of each transducer is fed into the next transducer. /// /// First transducer /// Second transducer /// Third transducer /// Fourth transducer /// Composed transducer public static TransducerM compose( TransducerM ta, TransducerM tb, TransducerM tc, TransducerM td) => new ComposeTransducerM(ta, tb, tc, td); /// /// Compose transducers together. The output of each transducer is fed into the next transducer. /// /// First transducer /// Second transducer /// Third transducer /// Fourth transducer /// Fifth transducer /// Composed transducer public static TransducerM compose( TransducerM ta, TransducerM tb, TransducerM tc, TransducerM td, TransducerM te) => new ComposeTransducerM(ta, tb, tc, td, te); /// /// Compose transducers together. The output of each transducer is fed into the next transducer. /// /// First transducer /// Second transducer /// Third transducer /// Fourth transducer /// Fifth transducer /// Sixth transducer /// Composed transducer public static TransducerM compose( TransducerM ta, TransducerM tb, TransducerM tc, TransducerM td, TransducerM te, TransducerM tf) => new ComposeTransducerM(ta, tb, tc, td, te, tf); /// /// Skip `amount` items in the sequence before yielding /// /// Number of items to skip /// Value type /// Transducer that skips values public static TransducerM skip(int amount) where M : Applicative => new SkipTransducerM(amount); /// /// Take `amount` items in the sequence before terminating /// /// Number of items to take /// Value type /// Transducer that takes `amount` values only public static TransducerM take(int amount) where M : Applicative => new TakeTransducerM(amount); /// /// Functor map transducer /// /// Function to map values of type `A` to values of type `B` /// Input value type /// Output value type /// Mapping transducer public static TransducerM map(Func f) => new MapTransducerM(f); /// /// Applicative filter transducer /// /// Filters each value flowing through the transducer. If `true` the value will flow /// downstream; if `false`, the value is dropped /// Bound value type /// Filtering transducer public static TransducerM filter(Func predicate) where M : Applicative => new FilterTransducerM(predicate); /// /// Monad bind transducer /// /// /// Chains two transducers together /// /// Initial transducer to run /// Chaining function to run with the result of `ta` that will produce a new `Transducer` /// Input value type /// Result value type of the first transducer /// Result value type of returned transducer /// A monadic bind transducer operation public static TransducerM bind(TransducerM ta, Func, B>> f) => new BindTransducerM1(ta, f); /// /// Monad bind transducer /// /// /// Chains two transducers together /// /// Initial transducer to run /// Chaining function to run with the result of `ta` that will produce a new `Transducer` /// Input value type /// Result value type of the first transducer /// Result value type of returned transducer /// A monadic bind transducer operation public static TransducerM bind(TransducerM ta, Func> f) => new BindTransducerM2(ta, f); /// /// Fold items in the stream while the predicate returns true; once the predicate returns false, the /// aggregated value is yielded downstream. /// /// Aggregating binary fold function /// Predicate /// Initial state /// Bound value type /// Yielded aggregate value type /// Aggregating binary folding transducer public static TransducerM foldWhile( Func Folder, Func Pred, S State) where M : Applicative => new FoldWhileTransducerM(Folder, Pred, State); /// /// Fold items in the stream while the predicate returns true; once the predicate returns false, the /// aggregated value is yielded downstream. /// /// Schedule for each yielded item /// Aggregating binary fold function /// Predicate /// Initial state /// Bound value type /// Yielded aggregate value type /// Aggregating binary folding transducer public static TransducerM foldWhile( Schedule Schedule, Func Folder, Func Pred, S State) where M : Applicative => new FoldWhileTransducerM2(Schedule, Folder, Pred, State); /// /// Fold items in the stream until the predicate returns true; once the predicate returns true, the /// aggregated value is yielded downstream. /// /// Aggregating binary fold function /// Predicate /// Initial state /// Bound value type /// Yielded aggregate value type /// Aggregating binary folding transducer public static TransducerM foldUntil( Func Folder, Func Pred, S State) where M : Applicative => new FoldUntilTransducerM(Folder, Pred, State); /// /// Fold items in the stream until the predicate returns true; once the predicate returns true, the /// aggregated value is yielded downstream. /// /// Schedule for each yielded item /// Aggregating binary fold function /// Predicate /// Initial state /// Bound value type /// Yielded aggregate value type /// Aggregating binary folding transducer public static TransducerM foldUntil( Schedule Schedule, Func Folder, Func Pred, S State) where M : Applicative => new FoldUntilTransducerM2(Schedule, Folder, Pred, State); } ================================================ FILE: LanguageExt.Streaming/Transducers/TransducerM/TransducerM.cs ================================================ using System; using LanguageExt.Traits; namespace LanguageExt; public abstract record TransducerM : K, B>, K, A> { /// /// Fold the input stream using the supplied reducer. /// /// Reducer that folds the stream of values flowing through the transducer /// State /// public abstract ReducerM Reduce(ReducerM reducer); /// /// Compose two transducers together. The output of the first transducer is the input to the second. /// public virtual TransducerM Compose(TransducerM tg) => new ComposeTransducerM(this, tg); /// /// Functor map /// public TransducerM Map(Func f) => Compose(TransducerM.map(f)); /// /// Contravariant functor map /// public TransducerM Comap(Func f) => TransducerM.map(f).Compose(this); /// /// Monadic bind /// public TransducerM Bind(Func, C>> f) => new BindTransducerM1(this, f); /// /// Monadic bind /// public TransducerM Bind(Func> f) => new BindTransducerM2(this, f); /// /// Monadic bind /// public TransducerM SelectMany(Func, C>> bind, Func project) => new SelectManyTransducerM1(this, bind, project); /// /// Monadic bind /// public TransducerM SelectMany(Func> bind, Func project) => new SelectManyTransducerM2(this, bind, project); } ================================================ FILE: LanguageExt.Sys/ActivityEnv.cs ================================================ using System; using System.Diagnostics; using System.Reflection; namespace LanguageExt; public record ActivityEnv( ActivitySource ActivitySource, Activity? Activity, string? ParentId) : IDisposable { public static ActivityEnv Default; static ActivityEnv() { var asm = Assembly.GetEntryAssembly()?.GetName(); Default = new ActivityEnv(new ActivitySource(asm?.Name ?? "", asm?.Version?.ToString() ?? "0.0"), null, null); } public void Dispose() { ActivitySource.Dispose(); Activity?.Dispose(); } public override string ToString() => "Activity Environment"; } ================================================ FILE: LanguageExt.Sys/EffOpt.cs ================================================ using System.Runtime.CompilerServices; namespace LanguageExt { internal static class EffOpt { internal const MethodImplOptions mops = MethodImplOptions.AggressiveInlining; } } ================================================ FILE: LanguageExt.Sys/LanguageExt.Sys.csproj ================================================ net10.0 default 5.0.0-beta-77 LanguageExt.Sys LanguageExt.Sys Paul Louth BCL System namespace wrapped up into Aff and Eff effects for controlled functional side-effects in C# Copyright (c) Paul Louth. All rights reserved. README.nuget.md Extensions to language-ext framework effects system that wraps the IO behaviours from the .NET BCL C#, Functional, Language Extension, Aff, Eff, Monad, Option, Either, Reader, Writer, State, List, Set, Map, Queue, Memo, Memoization, Immutable, Lambda, Pattern Matching, Tuple lang-ext-small.png https://github.com/louthy/language-ext MIT net10.0 false library 5.0.0.0 5.0.0.0 enable ================================================ FILE: LanguageExt.Sys/Live/Implementations/ActivitySourceIO.cs ================================================ using System; using System.Collections.Generic; using static LanguageExt.Prelude; using System.Diagnostics; namespace LanguageExt.Sys.Live.Implementations; public record ActivitySourceIO(ActivityEnv Env) : LanguageExt.Sys.Traits.ActivitySourceIO { /// /// Creates a new activity if there are active listeners for it, using the specified name and activity kind. /// /// The operation name of the activity. /// The activity kind. /// The created activity object, if it had active listeners, or `None` if it has no event /// listeners. public IO StartActivity(string name, ActivityKind kind) => lift(() => Env.ActivitySource.StartActivity(name, kind)); /// /// Creates a new activity if there are active listeners for it, using the specified name, activity kind, parent /// activity context, tags, optional activity link and optional start time. /// /// The operation name of the activity. /// The activity kind. /// The parent `ActivityContext` object to initialize the created activity object /// with /// The optional tags list to initialise the created activity object with. /// The optional `ActivityLink` list to initialise the created activity object with. /// The optional start timestamp to set on the created activity object. /// The created activity object, if it had active listeners, or null if it has no event listeners. public IO StartActivity( string name, ActivityKind kind, ActivityContext parentContext, HashMap tags = default, Seq links = default, DateTimeOffset startTime = default) => lift(() => Env.ActivitySource.StartActivity( name, kind, parentContext, tags.AsIterable().Map(pair => new KeyValuePair(pair.Key, pair.Value)), links, startTime)); /// /// Creates a new activity if there are active listeners for it, using the specified name, activity kind, parent /// activity context, tags, optional activity link and optional start time. /// /// The operation name of the activity. /// The activity kind. /// The parent Id to initialize the created activity object with. /// The optional tags list to initialise the created activity object with. /// The optional `ActivityLink` list to initialise the created activity object with. /// The optional start timestamp to set on the created activity object. /// The created activity object, if it had active listeners, or null if it has no event listeners. public IO StartActivity( string name, ActivityKind kind, string parentId, HashMap tags = default, Seq links = default, DateTimeOffset startTime = default) => lift(() => Env.ActivitySource.StartActivity( name, kind, parentId, tags.AsIterable().Map(pair => new KeyValuePair(pair.Key, pair.Value)), links, startTime)); } ================================================ FILE: LanguageExt.Sys/Live/Implementations/ConsoleIO.cs ================================================ using System; using static LanguageExt.Prelude; namespace LanguageExt.Sys.Live.Implementations; public record ConsoleIO : Sys.Traits.ConsoleIO { public static readonly Sys.Traits.ConsoleIO Default = new ConsoleIO(); public IO> ReadKey() => lift(() => Optional(Console.ReadKey())); public IO Clear() => lift(Console.Clear); public IO SetBgColor(ConsoleColor color) => lift(() => { Console.BackgroundColor = color;}); public IO SetColor(ConsoleColor color) => lift(() => { Console.ForegroundColor = color; }); public IO ResetColor() => lift(Console.ResetColor); public IO BgColor=> lift(() => Console.BackgroundColor); public IO Color=> lift(() => Console.ForegroundColor); public IO> Read() => lift(() => { var k = Console.Read(); return k == -1 ? None : Some(k); }); public IO> ReadLine()=> lift(() => Optional(Console.ReadLine())); public IO WriteLine() => lift(Console.WriteLine); public IO WriteLine(string value) => lift(() => Console.WriteLine(value)); public IO Write(string value) => lift(() => Console.Write(value)); } ================================================ FILE: LanguageExt.Sys/Live/Implementations/DirectoryIO.cs ================================================ using System; using System.IO; using static LanguageExt.Prelude; namespace LanguageExt.Sys.Live.Implementations; public record DirectoryIO : Traits.DirectoryIO { public static readonly Traits.DirectoryIO Default = new DirectoryIO(); public IO Create(string path) => lift(() => Directory.CreateDirectory(path)); public IO Delete(string path, bool recursive = true) => lift(() => Directory.Delete(path, recursive)); public IO> GetParent(string path) => lift(() => Optional(Directory.GetParent(path))); public IO Exists(string path) => lift(() => Directory.Exists(path)); public IO SetCreationTime(string path, DateTime creationTime) => lift(() => Directory.SetCreationTime(path, creationTime)); public IO SetCreationTimeUtc(string path, DateTime creationTimeUtc) => lift(() => Directory.SetCreationTimeUtc(path, creationTimeUtc)); public IO GetCreationTime(string path) => lift(() => Directory.GetCreationTime(path)); public IO GetCreationTimeUtc(string path) => lift(() => Directory.GetCreationTimeUtc(path)); public IO SetLastWriteTime(string path, DateTime lastWriteTime) => lift(() => Directory.SetLastWriteTime(path, lastWriteTime)); public IO SetLastWriteTimeUtc(string path, DateTime lastWriteTimeUtc) => lift(() => Directory.SetLastWriteTime(path, lastWriteTimeUtc)); public IO GetLastWriteTime(string path) => lift(() => Directory.GetLastWriteTime(path)); public IO GetLastWriteTimeUtc(string path) => lift(() => Directory.GetLastWriteTimeUtc(path)); public IO SetLastAccessTime(string path, DateTime lastAccessTime) => lift(() => Directory.SetLastAccessTime(path, lastAccessTime)); public IO SetLastAccessTimeUtc(string path, DateTime lastAccessTimeUtc) => lift(() => Directory.SetLastAccessTimeUtc(path, lastAccessTimeUtc)); public IO GetLastAccessTime(string path) => lift(() => Directory.GetLastAccessTime(path)); public IO GetLastAccessTimeUtc(string path) => lift(() => Directory.GetLastAccessTimeUtc(path)); public IO> EnumerateDirectories(string path) => lift(() => Directory.EnumerateDirectories(path).AsIterable().ToSeq()); public IO> EnumerateDirectories(string path, string searchPattern) => lift(() => Directory.EnumerateDirectories(path, searchPattern).AsIterable().ToSeq()); public IO> EnumerateDirectories(string path, string searchPattern, SearchOption searchOption) => lift(() => Directory.EnumerateDirectories(path, searchPattern, searchOption).AsIterable().ToSeq()); public IO> EnumerateFiles(string path) => lift(() => Directory.EnumerateFiles(path).AsIterable().ToSeq()); public IO> EnumerateFiles(string path, string searchPattern) => lift(() => Directory.EnumerateFiles(path, searchPattern).AsIterable().ToSeq()); public IO> EnumerateFiles(string path, string searchPattern, SearchOption searchOption) => lift(() => Directory.EnumerateFiles(path, searchPattern, searchOption).AsIterable().ToSeq()); public IO> EnumerateFileSystemEntries(string path) => lift(() => Directory.EnumerateFileSystemEntries(path).AsIterable().ToSeq()); public IO> EnumerateFileSystemEntries(string path, string searchPattern) => lift(() => Directory.EnumerateFileSystemEntries(path, searchPattern).AsIterable().ToSeq()); public IO> EnumerateFileSystemEntries(string path, string searchPattern, SearchOption searchOption) => lift(() => Directory.EnumerateFileSystemEntries(path, searchPattern, searchOption).AsIterable().ToSeq()); public IO GetDirectoryRoot(string path) => lift(() => Directory.GetDirectoryRoot(path)); public IO GetCurrentDirectory() => lift(Directory.GetCurrentDirectory); public IO SetCurrentDirectory(string path) => lift(() => Directory.SetCurrentDirectory(path)); public IO Move(string sourceDirName, string destDirName) => lift(() => Directory.Move(sourceDirName, destDirName)); public IO> GetLogicalDrives() => lift(() => Directory.GetLogicalDrives().AsIterable().ToSeq()); } ================================================ FILE: LanguageExt.Sys/Live/Implementations/EncodingIO.cs ================================================ using System.Text; namespace LanguageExt.Sys.Live.Implementations; public class EncodingIO : Sys.Traits.EncodingIO { public static Sys.Traits.EncodingIO Default = new EncodingIO(); public IO Encoding => LanguageExt.IO.pure(System.Text.Encoding.Default); } ================================================ FILE: LanguageExt.Sys/Live/Implementations/EnvironmentIO.cs ================================================ using System; using System.Collections; using LanguageExt.Common; using static LanguageExt.Prelude; namespace LanguageExt.Sys.Live.Implementations; public record EnvironmentIO : Sys.Traits.EnvironmentIO { public static readonly Sys.Traits.EnvironmentIO Default = new EnvironmentIO(); /// /// Gets the command line for this process. /// public IO CommandLine() => lift(() => Environment.CommandLine); /// /// Sets the fully qualified path of the current working directory. /// /// directory: fully qualified path of the current working directory. public IO SetCurrentDirectory(string directory) => lift(() => { Environment.CurrentDirectory = directory; }); /// /// Gets a unique identifier for the current managed thread. /// public IO CurrentManagedThreadId() => lift(() => Environment.CurrentManagedThreadId); /// /// Terminates this process and returns an exit code to the operating /// public IO Exit(int exitCode) => lift(() => Environment.Exit(exitCode)); /// /// Gets the exit code of the process. /// public IO ExitCode() => lift(() => Environment.ExitCode); /// /// Sets the exit code of the process. /// // exitCode: exit code of the process public IO SetExitCode(int exitCode) => lift(() => { Environment.ExitCode = exitCode; }); /// /// Replaces the name of each environment variable embedded in the specified string with the string equivalent of the value of the variable, then returns the resulting string. /// /// name: A string containing the names of zero or more environment variables. Each environment variable is quoted with the percent sign character (%). public IO ExpandEnvironmentVariables(string name) => lift(() => Environment.ExpandEnvironmentVariables(name)); /// /// Immediately terminates a process after writing a message to the Windows Application event log, and then includes the message in error reporting to Microsoft. /// /// message: A message that explains why the process was terminated, or null if no explanation is provided. public IO FailFast(Option message) => lift(() => Environment.FailFast(message.IfNone(""))); /// /// Immediately terminates a process after writing a message to the Windows Application event log, and then includes the message and exception information in error reporting to Microsoft. /// /// message: A message that explains why the process was terminated, or null if no explanation is provided. /// exception: An exception that represents the error that caused the termination. This is typically the exception in a catch block. public IO FailFast(Option message, Option exception) => lift(() => Environment.FailFast(message.IfNone(""), exception.IfNone(() => BottomException.Default))); /// /// Returns a string array containing the command-line arguments for the current process. /// public IO> GetCommandLineArgs() => lift(() => Environment.GetCommandLineArgs().AsIterable().ToSeq()); /// /// Retrieves the value of an environment variable from the current process. /// /// variable: The name of an environment variable. public IO> GetEnvironmentVariable(string variable) => lift(() => Optional(Environment.GetEnvironmentVariable(variable))); /// /// Retrieves the value of an environment variable from the current process or from the Windows operating system registry key for the current user or local machine. /// /// variable: The name of an environment variable. public IO> GetEnvironmentVariable(string variable, EnvironmentVariableTarget target) => lift(() => Optional(Environment.GetEnvironmentVariable(variable, target))); /// /// Retrieves all environment variable names and their values from the current process. /// public IO>> GetEnvironmentVariables() => lift(() => { var hm = HashMap>(); foreach (DictionaryEntry de in Environment.GetEnvironmentVariables()) { if (de.Key.ToString() is { } key) { hm = hm.Add(key, Optional(de.Value?.ToString())); } } return hm; }); /// /// Retrieves all environment variable names and their values from the current process, or from the Windows operating system registry key for the current user or local machine. /// /// target: One of the EnvironmentVariableTarget values. Only EnvironmentVariableTarget.Process is supported on .NET Core running on Unix-based systems. public IO>> GetEnvironmentVariables(EnvironmentVariableTarget target) => lift(() => { var hm = HashMap>(); foreach (DictionaryEntry de in Environment.GetEnvironmentVariables(target)) { if (de.Key.ToString() is { } key) { hm = hm.Add(key, Optional(de.Value?.ToString())); } } return hm; }); /// /// Gets the path to the system special folder that is identified by the specified enumeration. /// /// folder: One of enumeration values that identifies a system special folder. public IO GetFolderPath(Environment.SpecialFolder folder) => lift(() => Environment.GetFolderPath(folder)); /// /// Gets the path to the system special folder that is identified by the specified enumeration, and uses a specified option for accessing special folders. /// /// folder: One of the enumeration values that identifies a system special folder. /// option: One of the enumeration values that specifies options to use for accessing a special folder. public IO GetFolderPath(Environment.SpecialFolder folder, Environment.SpecialFolderOption option) => lift(() => Environment.GetFolderPath(folder, option)); /// /// Returns an array of string containing the names of the logical drives on the current computer. /// /// string[] Environment.GetLogicalDrives() public IO> GetLogicalDrives() => lift(() => Environment.GetLogicalDrives().AsIterable().ToSeq()); /// /// Gets a value that indicates whether the current application domain is being unloaded or the common language runtime (CLR) is shutting down. /// public IO HasShutdownStarted() => lift(() => Environment.HasShutdownStarted); /// /// Determines whether the current operating system is a 64-bit operating /// public IO Is64BitOperatingSystem() => lift(() => Environment.Is64BitOperatingSystem); /// /// Determines whether the current process is a 64-bit process. /// public IO Is64BitProcess() => lift(() => Environment.Is64BitProcess); /// /// Gets the NetBIOS name of this local computer. /// public IO MachineName() => lift(() => Environment.MachineName); /// /// Gets the newline string defined for this environment. /// public IO NewLine() => lift(() => Environment.NewLine); /// /// Gets an OperatingSystem object that contains the current platform identifier and version number. /// public IO OSVersion() => lift(() => Environment.OSVersion); /// /// Gets the number of processors on the current machine. /// public IO ProcessorCount() => lift(() => Environment.ProcessorCount); /// /// Creates, modifies, or deletes an environment variable stored in the current process. /// /// variable: The name of an environment variable. /// value: A value to assign to variable . public IO SetEnvironmentVariable(string variable, Option value) => lift(() => Environment.SetEnvironmentVariable(variable, value.IfNone(""))); /// /// Creates, modifies, or deletes an environment variable stored in the current process or in the Windows operating system registry key reserved for the current user or local machine. /// /// variable: The name of an environment variable. /// value: A value to assign to variable. /// target: One of the enumeration values that specifies the location of the environment variable. public IO SetEnvironmentVariable(string variable, Option value, EnvironmentVariableTarget target) => lift(() => Environment.SetEnvironmentVariable(variable, value.IfNone(""), target)); /// /// Gets current stack trace information. /// public IO StackTrace() => lift(() => Environment.StackTrace); /// /// Gets the fully qualified path of the system directory. /// public IO SystemDirectory() => lift(() => Environment.SystemDirectory); /// /// Gets the number of bytes in the operating system's memory page. /// public IO SystemPageSize() => lift(() => Environment.SystemPageSize); /// /// Gets the number of milliseconds elapsed since the system started. /// public IO TickCount() => lift(() => Environment.TickCount64); /// /// Gets the network domain name associated with the current user. /// public IO UserDomainName() => lift(() => Environment.UserDomainName); /// /// Gets a value indicating whether the current process is running in user interactive mode. /// public IO UserInteractive() => lift(() => Environment.UserInteractive); /// /// Gets the user name of the person who is currently logged on to the operating /// public IO UserName() => lift(() => Environment.UserName); /// /// Gets a Version object that describes the major, minor, build, and revision numbers of the common language runtime. /// public IO Version() => lift(() => Environment.Version); /// /// Gets the amount of physical memory mapped to the process context. /// public IO WorkingSet() => lift(() => Environment.WorkingSet); } ================================================ FILE: LanguageExt.Sys/Live/Implementations/FileIO.cs ================================================ using System.Collections.Generic; using System.IO; using System.Text; using static LanguageExt.Prelude; namespace LanguageExt.Sys.Live.Implementations; /// /// Real world interaction with the file-system /// public record FileIO : Sys.Traits.FileIO { public static readonly Sys.Traits.FileIO Default = new FileIO(); /// /// Copy file from one place to another /// public IO Copy(string fromPath, string toPath, bool overwrite = false) => lift(() => File.Copy(fromPath, toPath, overwrite)); /// /// Move file from one place to another /// public IO Move(string fromPath, string toPath) => lift(() => File.Move(fromPath, toPath)); /// /// Move file from one place to another /// public IO Move(string fromPath, string toPath, bool overwrite) => lift(() => File.Move(fromPath, toPath, overwrite)); /// /// Append lines to the end of a file /// public IO AppendAllLines(string path, IEnumerable lines, Encoding encoding) => liftIO(async env => { await File.AppendAllLinesAsync(path, lines, encoding, env.Token).ConfigureAwait(false); return unit; }); /// /// Read all lines from a file /// public IO> ReadAllLines(string path, Encoding encoding) => liftIO(async env => (await File.ReadAllLinesAsync(path, encoding, env.Token).ConfigureAwait(false)).AsIterable().ToSeq()); /// /// Write all lines to a file /// public IO WriteAllLines(string path, IEnumerable lines, Encoding encoding) => liftIO(async env => { await File.WriteAllLinesAsync(path, lines, encoding, env.Token).ConfigureAwait(false); return unit; }); /// /// Read text from a file /// public IO ReadAllText(string path, Encoding encoding) => liftIO(env => File.ReadAllTextAsync(path, encoding, env.Token)); /// /// Read data from a file /// public IO ReadAllBytes(string path) => liftIO(env => File.ReadAllBytesAsync(path, env.Token)); /// /// Write text to a file /// public IO WriteAllText(string path, string text, Encoding encoding) => liftIO(async env => { await File.WriteAllTextAsync(path, text, encoding, env.Token).ConfigureAwait(false); return unit; }); /// /// Write data to a file /// public IO WriteAllBytes(string path, byte[] data) => liftIO(async env => { await File.WriteAllBytesAsync(path, data, env.Token).ConfigureAwait(false); return unit; }); /// /// Delete a file /// public IO Delete(string path) => lift(() => File.Delete(path)); /// /// True if a file at the path exists /// public IO Exists(string path) => lift(() => File.Exists(path)); /// /// Open a text file /// public IO OpenText(string path) => lift(() =>File.OpenText(path)); /// /// Create a new text file to stream to /// public IO CreateText(string path) => lift(() =>File.CreateText(path)); /// /// Return a stream to append text to /// public IO AppendText(string path) => lift(() => File.AppendText(path)); /// /// Open a file-stream /// public IO OpenRead(string path) => lift(() =>File.OpenRead(path)); /// /// Open a file-stream /// public IO Open(string path, FileMode mode) => lift(() => File.Open(path, mode)); /// /// Open a file-stream /// public IO Open(string path, FileMode mode, FileAccess access) => lift(() =>File.Open(path, mode, access)); /// /// Open a file-stream /// public IO OpenWrite(string path) => lift(() =>File.OpenWrite(path)); } ================================================ FILE: LanguageExt.Sys/Live/Implementations/TextReadIO.cs ================================================ using System; using System.IO; using static LanguageExt.Prelude; namespace LanguageExt.Sys.Live.Implementations; public record TextReadIO : Sys.Traits.TextReadIO { public static readonly Sys.Traits.TextReadIO Default = new TextReadIO(); /// /// Read a line of text from the stream /// public IO> ReadLine(TextReader reader) => liftIO(async env => { var str = await reader.ReadLineAsync(env.Token).ConfigureAwait(false); return str == null ? None : Some(str); }); /// /// Read the rest of the text in the stream /// public IO ReadToEnd(TextReader reader) => liftIO(env => reader.ReadToEndAsync(env.Token)); /// /// Read chars from the stream into the buffer /// Returns the number of chars read /// public IO Read(TextReader reader, Memory buffer) => liftIO(async env => await reader.ReadAsync(buffer, env.Token)); /// /// Close the reader /// public IO Close(TextReader reader) => lift(() => { reader.Close(); reader.Dispose(); return unit; }); } ================================================ FILE: LanguageExt.Sys/Live/Implementations/TimeIO.cs ================================================ using System; using System.Threading.Tasks; using static LanguageExt.Prelude; namespace LanguageExt.Sys.Live.Implementations; public readonly struct TimeIO : Sys.Traits.TimeIO { public static readonly Sys.Traits.TimeIO Default = new TimeIO(); /// /// Current date time /// public IO Now => lift(() => DateTime.Now); /// /// Current date time /// public IO UtcNow => lift(() => DateTime.UtcNow); /// /// Today's date /// public IO Today => lift(() => DateTime.Today); /// /// Pause a task until a specified time /// public IO SleepUntil(DateTime dt) => from now in Now from res in dt <= now ? unitIO : liftIO(async env => await Task.Delay(dt - now, env.Token).ConfigureAwait(false)) select res; /// /// Pause a task until for a specified length of time /// public IO SleepFor(TimeSpan ts) => liftIO(async env => await Task.Delay(ts, env.Token).ConfigureAwait(false)); } ================================================ FILE: LanguageExt.Sys/Live/Runtime.cs ================================================ using System; using LanguageExt.Traits; using LanguageExt.Sys.Traits; namespace LanguageExt.Sys.Live; /// /// Live IO runtime /// public record Runtime(RuntimeEnv Env) : Local, ActivityEnv>, Has, ActivitySourceIO>, Has, ConsoleIO>, Has, FileIO>, Has, TextReadIO>, Has, TimeIO>, Has, EnvironmentIO>, Has, DirectoryIO>, Has, EncodingIO> { /// /// Constructor function /// public static Runtime New() => new (new RuntimeEnv(ActivityEnv.Default)); static K, A> asks(Func f) => Readable.asks, Runtime, A>(f); static K, A> local(Func f, K, A> ma) => Readable.local(f, ma); static K, A> pure(A value) => Eff.Pure(value); /// /// Activity /// static K, ActivitySourceIO> Has, ActivitySourceIO>.Ask => asks(rt => new Implementations.ActivitySourceIO(rt.Env.Activity)); /// /// Access the console environment /// /// Console environment static K, ConsoleIO> Has, ConsoleIO>.Ask { get; } = pure(Implementations.ConsoleIO.Default); /// /// Access the file environment /// /// File environment static K, FileIO> Has, FileIO>.Ask { get; } = pure(Implementations.FileIO.Default); /// /// Access the TextReader environment /// /// TextReader environment static K, TextReadIO> Has, TextReadIO>.Ask { get; } = pure(Implementations.TextReadIO.Default); /// /// Access the time environment /// /// Time environment static K, TimeIO> Has, TimeIO>.Ask { get; } = pure(Implementations.TimeIO.Default); /// /// Access the operating-system environment /// /// Operating-system environment environment static K, EnvironmentIO> Has, EnvironmentIO>.Ask { get; } = pure(Implementations.EnvironmentIO.Default); /// /// Access the directory environment /// /// Directory environment static K, DirectoryIO> Has, DirectoryIO>.Ask { get; } = pure(Implementations.DirectoryIO.Default); /// /// Access the directory environment /// /// Directory environment static K, EncodingIO> Has, EncodingIO>.Ask { get; } = pure(Implementations.EncodingIO.Default); /// /// Run with a local ActivityEnv /// static K, A> Local, ActivityEnv>.With(Func f, K, A> ma) => local(rt => rt with { Env = rt.Env with { Activity = f(rt.Env.Activity) } }, ma); /// /// Read the current ActivityEnv /// static K, ActivityEnv> Has, ActivityEnv>.Ask => asks(rt => rt.Env.Activity); } public record RuntimeEnv(ActivityEnv Activity) : IDisposable { public void Dispose() => Activity.Dispose(); } ================================================ FILE: LanguageExt.Sys/MemoryConsole.cs ================================================ using System; using System.Threading; using System.Collections; using System.Collections.Generic; using static LanguageExt.Prelude; using System.Collections.Concurrent; using System.Linq; using LanguageExt.UnsafeValueAccess; namespace LanguageExt.Sys { /// /// An in-memory console emulation /// /// /// Mostly for use in test runtimes. This simulates the real System.IO.Console, with additional functionality /// for pre-building a keyboard buffer (WriteKey, WriteKeyChar, WriteKeyString) and committing them as though /// they were typed. ReadKey and the other Read* methods will read the entries from the pre-built keyboard /// buffer. /// /// There is no public API for this, other than the prepping of the keyboard buffer, which is outside of the /// normal console system. /// public class MemoryConsole : IEnumerable { const ConsoleColor DefaultBgColor = ConsoleColor.Black; const ConsoleColor DefaultColor = ConsoleColor.White; readonly ConcurrentQueue KeyboardBuffer = new(); readonly ConcurrentStack Console = new(); public MemoryConsole() => Console.Push(""); /// /// Write a key into the keyboard buffer /// /// /// Won't show in the console until Commit or Read* is called /// public Unit WriteKey(ConsoleKeyInfo key) { KeyboardBuffer.Enqueue(key); return default; } /// /// Write a character into the keyboard buffer /// /// /// Won't show in the console until Commit or Read* is called /// public Unit WriteKeyChar(char ch) { KeyboardBuffer.Enqueue(new ConsoleKeyInfo(ch, CharToConsoleKey(ch), false, false, false)); return default; } /// /// Write a character into the keyboard buffer /// /// /// No newline character is written /// Won't show in the console until Commit or Read* is called /// public Unit WriteKeyString(string str) { foreach (var ch in str) { WriteKeyChar(ch); } return default; } /// /// Write a series of characters into the keyboard buffer followed by a newline /// /// /// Won't show in the console until Commit or Read* is called /// public Unit WriteKeyLine(string str) { WriteKeyString(str); WriteKeyChar('\n'); return default; } /// /// Commit what's in the keyboard buffer (as though they've been typed in) /// /// public Unit Commit() { while(KeyboardBuffer.Count > 0) { ReadKey().Map(WriteKey); } return default; } internal Option ReadKey() => KeyboardBuffer.TryDequeue(out var key) ? key : None; internal ConsoleKey CharToConsoleKey(char ch) => Enum.TryParse(ch.ToString(), out var ck) ? ck : default; internal Unit Clear() { Console.Clear(); return default; } internal Unit SetBgColor(ConsoleColor color) { BgColor = color; return default; } internal Unit SetColor(ConsoleColor color) { Color = color; return default; } internal Unit ResetColor() { BgColor = DefaultBgColor; Color = DefaultColor; return default; } internal ConsoleColor BgColor { get; private set; } = DefaultBgColor; internal ConsoleColor Color { get; private set; } = DefaultColor; internal Option Read() { var ok = ReadKey(); if (ok.IsNone) return None; var kc = ok.ValueUnsafe().KeyChar; var ch = Convert.ToChar(kc); if (ch == '\n') { Console.Push(""); } else { lock (Console) { if (Console.TryPop(out var line)) { Console.Push(line + Convert.ToChar(kc)); } } } return kc; } internal Option ReadLine() { List chs = new(); while (true) { var ok = ReadKey(); if (ok.IsNone) return None; var ch = ok.ValueUnsafe().KeyChar; if (ch.ToString() == Environment.NewLine || ch == '\n'|| ch == '\r') return new string(chs.ToArray()); chs.Add(ch); } } internal Unit WriteLine() { Console.Push(""); return unit; } internal Unit WriteLine(string value) { Console.Push(value); return unit; } internal Unit Write(string value) { lock (Console) { if (Console.TryPop(out var line)) { Console.Push(line + value); } } return unit; } public IEnumerator GetEnumerator() => Console.Reverse().Skip(1).GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => Console.Reverse().Skip(1).GetEnumerator(); } } ================================================ FILE: LanguageExt.Sys/MemorySystemEnvironment.cs ================================================ using System; using System.Collections; using System.Collections.Concurrent; using static LanguageExt.Prelude; namespace LanguageExt.Sys; /// /// In-memory representation of the System.Environment /// /// /// Use: MemorySystemEnvironment.InitFromSystem(), to initialise from the real System.Environment and then tweak /// for use in unit-tests using the With(...) method. /// public class MemorySystemEnvironment { public MemorySystemEnvironment( ConcurrentDictionary> processEnvironmentVariables, ConcurrentDictionary> userEnvironmentVariables, ConcurrentDictionary> systemEnvironmentVariables, int exitCode, string commandLine, int currentManagedThreadId, Seq commandLineArgs, Seq logicalDrives, string newLine, bool hasShutdownStarted, bool is64BitOperatingSystem, bool is64BitProcess, string machineName, OperatingSystem osVersion, int processorCount, string stackTrace, string systemDirectory, int systemPageSize, long tickCount, string userDomainName, bool userInteractive, string userName, Version version, long workingSet, Func getFolderPath) { ProcessEnvironmentVariables = processEnvironmentVariables; UserEnvironmentVariables = userEnvironmentVariables; SystemEnvironmentVariables = systemEnvironmentVariables; ExitCode = exitCode; CommandLine = commandLine; CurrentManagedThreadId = currentManagedThreadId; CommandLineArgs = commandLineArgs; LogicalDrives = logicalDrives; NewLine = newLine; HasShutdownStarted = hasShutdownStarted; Is64BitOperatingSystem = is64BitOperatingSystem; Is64BitProcess = is64BitProcess; MachineName = machineName; OSVersion = osVersion; ProcessorCount = processorCount; StackTrace = stackTrace; SystemDirectory = systemDirectory; SystemPageSize = systemPageSize; TickCount = tickCount; UserDomainName = userDomainName; UserInteractive = userInteractive; UserName = userName; Version = version; WorkingSet = workingSet; GetFolderPath = getFolderPath; } // Mutable public ConcurrentDictionary> ProcessEnvironmentVariables; public ConcurrentDictionary> UserEnvironmentVariables; public ConcurrentDictionary> SystemEnvironmentVariables; public int ExitCode; public bool HasShutdownStarted; // Immutable public readonly string CommandLine; public readonly int CurrentManagedThreadId; public readonly Seq CommandLineArgs; public readonly Seq LogicalDrives; public readonly string NewLine; public readonly bool Is64BitOperatingSystem; public readonly bool Is64BitProcess; public readonly string MachineName; public readonly OperatingSystem OSVersion; public readonly int ProcessorCount; public readonly string StackTrace; public readonly string SystemDirectory; public readonly int SystemPageSize; public readonly long TickCount; public readonly string UserDomainName; public readonly bool UserInteractive; public readonly string UserName; public readonly Version Version; public readonly long WorkingSet; // Injectable public readonly Func GetFolderPath; public MemorySystemEnvironment With( ConcurrentDictionary>? ProcessEnvironmentVariables = null, ConcurrentDictionary>? UserEnvironmentVariables = null, ConcurrentDictionary>? SystemEnvironmentVariables = null, int? ExitCode = null, string? CommandLine = null, int? CurrentManagedThreadId = null, Seq? CommandLineArgs = null, Seq? LogicalDrives = null, string? NewLine = null, bool? HasShutdownStarted = null, bool? Is64BitOperatingSystem = null, bool? Is64BitProcess = null, string? MachineName = null, OperatingSystem? OSVersion = null, int? ProcessorCount = null, string? StackTrace = null, string? SystemDirectory = null, int? SystemPageSize = null, long? TickCount = null, string? UserDomainName = null, bool? UserInteractive = null, string? UserName = null, Version? Version = null, long? WorkingSet = null, Func? GetFolderPath = null) => new MemorySystemEnvironment( ProcessEnvironmentVariables ?? this.ProcessEnvironmentVariables, UserEnvironmentVariables ?? this.UserEnvironmentVariables, SystemEnvironmentVariables ?? this.SystemEnvironmentVariables, ExitCode ?? this.ExitCode, CommandLine ?? this.CommandLine, CurrentManagedThreadId ?? this.CurrentManagedThreadId, CommandLineArgs ?? this.CommandLineArgs, LogicalDrives ?? this.LogicalDrives, NewLine ?? this.NewLine, HasShutdownStarted ?? this.HasShutdownStarted, Is64BitOperatingSystem ?? this.Is64BitOperatingSystem, Is64BitProcess ?? this.Is64BitProcess, MachineName ?? this.MachineName, OSVersion ?? this.OSVersion, ProcessorCount ?? this.ProcessorCount, StackTrace ?? this.StackTrace, SystemDirectory ?? this.SystemDirectory, SystemPageSize ?? this.SystemPageSize, TickCount ?? this.TickCount, UserDomainName ?? this.UserDomainName, UserInteractive ?? this.UserInteractive, UserName ?? this.UserName, Version ?? this.Version, WorkingSet ?? this.WorkingSet, GetFolderPath ?? this.GetFolderPath ); public static MemorySystemEnvironment InitFromSystem() => new MemorySystemEnvironment( processEnvironmentVariables: GetEnvs(EnvironmentVariableTarget.Process), userEnvironmentVariables: GetEnvs(EnvironmentVariableTarget.User), systemEnvironmentVariables: GetEnvs(EnvironmentVariableTarget.Machine), exitCode: Environment.ExitCode, commandLine: Environment.CommandLine, currentManagedThreadId: Environment.CurrentManagedThreadId, commandLineArgs: Environment.GetCommandLineArgs().AsIterable().ToSeq().Strict(), logicalDrives: Environment.GetLogicalDrives().AsIterable().ToSeq().Strict(), newLine: Environment.NewLine, hasShutdownStarted: Environment.HasShutdownStarted, is64BitOperatingSystem: Environment.Is64BitOperatingSystem, is64BitProcess: Environment.Is64BitProcess, machineName: Environment.MachineName, osVersion: Environment.OSVersion, processorCount: Environment.ProcessorCount, stackTrace: Environment.StackTrace, systemDirectory: Environment.SystemDirectory, systemPageSize: Environment.SystemPageSize, tickCount: Environment.TickCount, userDomainName: Environment.UserDomainName, userInteractive: Environment.UserInteractive, userName: Environment.UserName, version: Environment.Version, workingSet: Environment.WorkingSet, getFolderPath: Environment.GetFolderPath ); static ConcurrentDictionary> GetEnvs(EnvironmentVariableTarget target) { var dict = new ConcurrentDictionary>(); foreach (DictionaryEntry de in Environment.GetEnvironmentVariables(target)) { if (de.Key.ToString() is {} key) { dict.TryAdd(key, Optional(de.Value).Bind(v => Optional(v.ToString()))); } } return dict; } } ================================================ FILE: LanguageExt.Sys/README.nuget.md ================================================ # LanguageExt.Sys `LanguageExt.Sys` is the a wrapper around the `System` namespace IO functions and is part of the [language-ext functional programming framework](https://github.com/louthy/language-ext). The framework uses and abuses the features of C# to provide a pure functional-programming 'Base Class Library' that, if you squint, can look like extensions to the language itself. The desire here is to make programming in C# much more robust by helping the engineer's inertia flow in the direction of declarative and pure functional code rather than imperative. Using these techniques for large code-bases can bring tangible benefits to long-term maintenance by removing hidden complexity and by easing the engineer's cognitive load. ## Features ### [Functional effects and IO](https://louthy.github.io/language-ext/LanguageExt.Core/Effects/index.html) | Location | Feature | Description | |----------|--------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `Core` | `IO` | [A synchronous and asynchronous side-effect: an IO monad](https://louthy.github.io/language-ext/LanguageExt.Core/Effects/IO/index.html) | | `Core` | `Eff` | [A synchronous and asynchronous side-effect with error handling](https://louthy.github.io/language-ext/LanguageExt.Core/Effects/Eff/Eff%20no%20runtime/index.html) | | `Core` | `Eff` | [Same as `Eff` but with an injectable runtime for dependency-injection: a unit testable IO monad](https://louthy.github.io/language-ext/LanguageExt.Core/Effects/Eff/Eff%20with%20runtime/index.html) | | `Core` | Pipes | [A clean and powerful stream processing system that lets you build and connect reusable streaming components](https://louthy.github.io/language-ext/LanguageExt.Core/Effects/Pipes/index.html) | | `Core` | StreamT | [less powerful (than Pipes), but easier to use streaming effects transformer](https://louthy.github.io/language-ext/LanguageExt.Core/Effects/StreamT/index.html) | ### [Atomic concurrency and collections](https://louthy.github.io/language-ext/LanguageExt.Core/Concurrency/index.html) | Location | Feature | Description | |----------|------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------| | `Core` | `Atom` | [A lock-free atomically mutable reference for working with shared state](https://louthy.github.io/language-ext/LanguageExt.Core/Concurrency/Atom) | | `Core` | `Ref` | [An atomic reference to be used in the transactional memory system](https://louthy.github.io/language-ext/LanguageExt.Core/Concurrency/STM) | | `Core` | `AtomHashMap` | [An immutable `HashMap` with a lock-free atomically mutable reference](https://louthy.github.io/language-ext/LanguageExt.Core/Concurrency/AtomHashMap) | | `Core` | `AtomSeq` | [An immutable `Seq` with a lock-free atomically mutable reference](https://louthy.github.io/language-ext/LanguageExt.Core/Concurrency/AtomSeq) | | `Core` | `VectorClock` | [Understand distributed causality](https://louthy.github.io/language-ext/LanguageExt.Core/Concurrency/VectorClock) | | `Core` | `VersionVector` | [A vector clock with some versioned data](https://louthy.github.io/language-ext/LanguageExt.Core/Concurrency/VersionVector) | | `Core` | `VersionHashMap ` | [Distrubuted atomic versioning of keys in a hash-map](https://louthy.github.io/language-ext/LanguageExt.Core/Concurrency/VersionHashMap) | ### [Immutable collections](https://louthy.github.io/language-ext/LanguageExt.Core/Immutable%20Collections/index.html) | Location | Feature | Description | |----------|----------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `Core` | `Arr` | [Immutable array](https://louthy.github.io/language-ext/LanguageExt.Core/Immutable%20Collections/Arr/index.html) | | `Core` | `Seq` | [Lazy immutable list, evaluate at-most-once](https://louthy.github.io/language-ext/LanguageExt.Core/Immutable%20Collections/Seq/index.html) - very, very fast! | | `Core` | `Iterable` | [Wrapper around `IEnumerable` with support for traits](https://louthy.github.io/language-ext/LanguageExt.Core/Immutable%20Collections/Iterable/index.html) - enables the higher-kinded traits to work with enumerables. | | `Core` | `Lst` | [Immutable list](https://louthy.github.io/language-ext/LanguageExt.Core/Immutable%20Collections/List/index.html) - use `Seq` over `Lst` unless you need `InsertAt` | | `Core` | `Map` | [Immutable map](https://louthy.github.io/language-ext/LanguageExt.Core/Immutable%20Collections/Map/index.html) | | `Core` | `Map` | [Immutable map with Ord constraint on `K`](https://louthy.github.io/language-ext/LanguageExt.Core/Immutable%20Collections/Map/index.html) | | `Core` | `HashMap` | [Immutable hash-map](https://louthy.github.io/language-ext/LanguageExt.Core/Immutable%20Collections/HashMap/index.html) | | `Core` | `HashMap` | [Immutable hash-map with Eq constraint on `K`](https://louthy.github.io/language-ext/LanguageExt.Core/Immutable%20Collections/HashMap/index.html) | | `Core` | `Set` | [Immutable set](https://louthy.github.io/language-ext/LanguageExt.Core/Immutable%20Collections/Set/index.html) | | `Core` | `Set` | [Immutable set with Ord constraint on `A`](https://louthy.github.io/language-ext/LanguageExt.Core/Immutable%20Collections/Set/index.html) | | `Core` | `HashSet` | [Immutable hash-set](https://louthy.github.io/language-ext/LanguageExt.Core/Immutable%20Collections/HashSet/index.html) | | `Core` | `HashSet` | [Immutable hash-set with Eq constraint on `A`](https://louthy.github.io/language-ext/LanguageExt.Core/Immutable%20Collections/HashSet/index.html) | | `Core` | `Que` | [Immutable queue](https://louthy.github.io/language-ext/LanguageExt.Core/Immutable%20Collections/Queue/index.html) | | `Core` | `Stck` | [Immutable stack](https://louthy.github.io/language-ext/LanguageExt.Core/Immutable%20Collections/Stack/index.html) | ### [Optional and alternative value monads](https://louthy.github.io/language-ext/LanguageExt.Core/Monads/Alternative%20Monads/index.html) | Location | Feature | Description | |----------|---------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `Core` | `Option` | [Option monad](https://louthy.github.io/language-ext/LanguageExt.Core/Monads/Alternative%20Monads/Option/index.html) | | `Core` | `OptionT` | [Option monad-transformer](https://louthy.github.io/language-ext/LanguageExt.Core/Monads/Alternative%20Monads/OptionT/index.html) | | `Core` | `Either` | [Right/Left choice monad](https://louthy.github.io/language-ext/LanguageExt.Core/Monads/Alternative%20Monads/Either/index.html) | | `Core` | `EitherT` | [Right/Left choice monad-transformer](https://louthy.github.io/language-ext/LanguageExt.Core/Monads/Alternative%20Monads/EitherT/index.html) | | `Core` | `Fin` | [`Error` handling monad, like `Either`](https://louthy.github.io/language-ext/LanguageExt.Core/Monads/Alternative%20Monads/Fin/index.html) | | `Core` | `FinT` | [`Error` handling monad-transformer](https://louthy.github.io/language-ext/LanguageExt.Core/Monads/Alternative%20Monads/FinT/index.html) | | `Core` | `Try` | [Exception handling monad](https://louthy.github.io/language-ext/LanguageExt.Core/Monads/Alternative%20Monads/Try/index.html) | | `Core` | `TryT` | [Exception handling monad-transformer](https://louthy.github.io/language-ext/LanguageExt.Core/Monads/Alternative%20Monads/TryT/index.html) | | `Core` | `Validation` | [Validation applicative and monad](https://louthy.github.io/language-ext/LanguageExt.Core/Monads/Alternative%20Monads/Validation/index.html) for collecting multiple errors before aborting an operation | | `Core` | `ValidationT` | [Validation applicative and monad-transformer](https://louthy.github.io/language-ext/LanguageExt.Core/Monads/Alternative%20Monads/ValidationT/index.html) | ### [State managing monads](https://louthy.github.io/language-ext/LanguageExt.Core/Monads/State%20and%20Environment%20Monads/index.html) | Location | Feature | Description | |----------|--------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `Core` | `Reader` | [Reader monad](https://louthy.github.io/language-ext/LanguageExt.Core/Monads/State%20and%20Environment%20Monads/Reader/Reader/index.html) | | `Core` | `ReaderT` | [Reader monad-transformer](https://louthy.github.io/language-ext/LanguageExt.Core/Monads/State%20and%20Environment%20Monads/Reader/ReaderT/index.html) | | `Core` | `Writer` | [Writer monad that logs to a `W` constrained to be a Monoid](https://louthy.github.io/language-ext/LanguageExt.Core/Monads/State%20and%20Environment%20Monads/Writer/Writer/index.html) | | `Core` | `WriterT` | [Writer monad-transformer](https://louthy.github.io/language-ext/LanguageExt.Core/Monads/State%20and%20Environment%20Monads/Writer/WriterT/index.html) | | `Core` | `State` | [State monad](https://louthy.github.io/language-ext/LanguageExt.Core/Monads/State%20and%20Environment%20Monads/State/State/index.html) | | `Core` | `StateT` | [State monad-transformer](https://louthy.github.io/language-ext/LanguageExt.Core/Monads/State%20and%20Environment%20Monads/State/StateT/index.html) | ### [Parser combinators](https://louthy.github.io/language-ext/LanguageExt.Parsec/index.html) | Location | Feature | Description | |----------|----------------|--------------------------------------------------------------------------------------------------------------------------------| | `Parsec` | `Parser` | [String parser monad and full parser combinators library](https://louthy.github.io/language-ext/LanguageExt.Parsec/index.html) | | `Parsec` | `Parser` | [Parser monad that can work with any input stream type](https://louthy.github.io/language-ext/LanguageExt.Parsec/index.html) | ### [Pretty](https://louthy.github.io/language-ext/LanguageExt.Core/Pretty/index.html) | Location | Feature | Description | |----------|----------|--------------------------------------------------| | `Core` | `Doc` | Produce nicely formatted text with smart layouts | ### [Differencing](https://louthy.github.io/language-ext/LanguageExt.Core/DataTypes/Patch/index.html) | Location | Feature | Description | |----------|-----------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `Core` | `Patch` | Uses patch-theory to efficiently calculate the difference (`Patch.diff(list1, list2)`) between two collections of `A` and build a patch which can be applied (`Patch.apply(patch, list)`) to one to make the other (think git diff). | ### [Traits](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/index.html) The traits are major feature of `v5`+ language-ext that makes generic programming with higher-kinds a reality. Check out Paul's [series on Higher Kinds](https://paullouth.com/higher-kinds-in-c-with-language-ext/) to get a deeper insight. | Location | Feature | Description | |----------|----------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `Core` | `MonoidK` | [A monoid on applicative functors](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Alternative/index.html) | | `Core` | `Applicative` | [Applicative functor](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Applicative/index.html) | | `Core` | `Eq` | [Ad-hoc equality trait](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Eq/index.html) | | `Core` | `Fallible` | [Trait that describes types that can fail](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Fallible/index.html) | | `Core` | `Foldable` | [Aggregation over a structure](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Foldable/index.html) | | `Core` | `Functor` | [Functor `Map`](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Functor/index.html) | | `Core` | `Has` | [Used in runtimes to enable DI-like capabilities](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Has/index.html) | | `Core` | `Hashable` | [Ad-hoc has-a-hash-code trait](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Hashable/index.html) | | `Core` | `Local` | [Creates a local environment to run a computation ](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Local/index.html) | | `Core` | `Monad` | [Monad trait](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Monads/Monad/index.html) | | `Core` | `MonadT` | [Monad transformer trait](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Monads/MonadT/index.html) | | `Core` | `Monoid` | [A monoid is a type with an identity `Empty` and an associative binary operation `+`](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Monoid/index.html) | | `Core` | `MonoidK` | [Equivalent of monoids for working on higher-kinded types](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/MonoidK/index.html) | | `Core` | `Mutates` | [Used in runtimes to enable stateful operations](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Mutates/index.html) | | `Core` | `Ord` | [Ad-hoc ordering / comparisons](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Ord/index.html) | | `Core` | `Range` | [Abstraction of a range of values](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Range/index.html) | | `Core` | `Readable` | [Generalised Reader monad abstraction](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Readable/index.html) | | `Core` | `SemigroupK` | [A semigroup on functors](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/SemigroupK/index.html) | | `Core` | `Semigroup` | [Provides an associative binary operation `+`](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Semigroup/index.html) | | `Core` | `SemigroupK` | [Equivalent of semigroups for working with higher-kinded types](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/SemigroupK/index.html) | | `Core` | `Stateful` | [Generalised State monad abstraction](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Stateful/index.html) | | `Core` | `Traversable` | [Traversable structures support element-wise sequencing of Applicative effects](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Traversable/index.html) | | `Core` | `Writable` | [Generalised Writer monad abstraction](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Writable/index.html) | ### [Value traits](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Domain/index.html) These work a little like `NewType` but they impart semantic meaning and some common operators for the underlying value. | Location | Feature | Description | |----------|--------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `Core` | `DomainType` | Provides a mapping from `SELF` to an underlying representation: `REPR` | | `Core` | `Identifier ` | Identifiers (like IDs in databases: `PersonId` for example), they are equivalent to `DomaintType` with equality. | | `Core` | `VectorSpace` | Scalable values; can add and subtract self, but can only multiply and divide by a scalar. Can also negate. | | `Core` | `Amount ` | Quantities, such as the amount of money in USD on a bank account or a file size in bytes. Derives `VectorSpace`, `IdentifierLike`, `DomainType`, and is orderable (comparable). | | `Core` | `LocusLike ` | Works with space-like structures. Spaces have absolute and relative distances. Has an origin/zero point and derives `DomainType`, `IdentifierLike`, `AmountLike` and `VectorSpace`. `DISTANCE` must also be an `AmountLike`. | ================================================ FILE: LanguageExt.Sys/Sys/Console.Eff.cs ================================================ using System; using LanguageExt.Pipes; using LanguageExt.Traits; using LanguageExt.Sys.Traits; namespace LanguageExt.Sys; /// /// Time IO /// public static class Console where RT : Has, ConsoleIO> { /// /// Read a key from the console /// public static Eff readKey => Console, RT>.readKey.As(); /// /// Read keys from the console and push them downstream /// public static Producer readKeys => Console, RT>.readKeys; /// /// Clear the console /// public static Eff clear => Console, RT>.clear.As(); /// /// Read from the console /// public static Eff read => Console, RT>.read.As(); /// /// Read chars from the console and push them downstream /// public static Producer reads => Console, RT>.reads; /// /// Read from the console /// public static Eff readLine => Console, RT>.readLine.As(); /// /// Read lines from the console and push them downstream /// public static Producer readLines => Console, RT>.readLines; /// /// Write an empty line to the console /// public static Eff writeEmptyLine => Console, RT>.writeEmptyLine.As(); /// /// Write a line to the console (returning unit) /// public static Eff writeLine(string line) => Console, RT>.writeLine(line).As(); /// /// Write a line to the console (returning the original line) /// public static Eff writeLine2(string line) => Console, RT>.writeLine2(line).As(); /// /// Write a string to the console /// public static Eff write(string line) => Console, RT>.write(line).As(); /// /// Write a string to the console /// public static Eff write(char line) => Console, RT>.write(line).As(); public static Eff setBgColour(ConsoleColor colour) => Console, RT>.setBgColour(colour).As(); public static Eff setColour(ConsoleColor colour) => Console, RT>.setColour(colour).As(); public static Eff resetColour => Console, RT>.resetColour().As(); public static Eff bgColour => Console, RT>.bgColour.As(); public static Eff color => Console, RT>.colour.As(); } ================================================ FILE: LanguageExt.Sys/Sys/Console.cs ================================================ using System; using LanguageExt.Common; using static LanguageExt.Prelude; using LanguageExt.Pipes; using LanguageExt.Sys.Traits; using LanguageExt.Traits; namespace LanguageExt.Sys; /// /// Time IO /// public static class Console where M : MonadIO, Fallible where RT : Has { static K consoleIO => Has.ask; /// /// Read a key from the console /// public static K readKey => from t in consoleIO from k in t.ReadKey() from r in k.Match(Some: M.Pure, None: M.Fail(Errors.EndOfStream)) select r; /// /// Read keys from the console and push them downstream /// public static ProducerT readKeys => from ln in readKey from __ in ProducerT.yield(ln) select unit; /// /// Clear the console /// public static K clear => consoleIO.Bind(e => e.Clear()); /// /// Read from the console /// public static K read => from t in consoleIO from k in t.Read() from r in k.Match(Some: M.Pure, None: M.Fail(Errors.EndOfStream)) select r; /// /// Read chars from the console and push them downstream /// public static ProducerT reads => from ln in read from __ in ProducerT.yield(ln) select unit; /// /// Read from the console /// public static K readLine => from t in consoleIO from k in t.ReadLine() from r in k.Match(Some: M.Pure, None: M.Fail(Errors.EndOfStream)) select r; /// /// Read lines from the console and push them downstream /// public static ProducerT readLines => from ln in readLine from _ in ProducerT.yield(ln) select unit; /// /// Write an empty line to the console /// public static K writeEmptyLine => consoleIO.Bind(e => e.WriteLine()); /// /// Write a line to the console (returning unit) /// public static K writeLine(string line) => consoleIO.Bind(e => e.WriteLine(line)); /// /// Write a line to the console (returning the original line) /// public static K writeLine2(string line) => consoleIO.Bind(e => e.WriteLine(line)).Map(_ => line); /// /// Write a string to the console /// public static K write(string line) => consoleIO.Bind(e => e.Write(line)); /// /// Write a string to the console /// public static K write(char line) => consoleIO.Bind(e => e.Write(line.ToString())); public static K setBgColour(ConsoleColor col) => consoleIO.Bind(e => e.SetBgColor(col)); public static K setColour(ConsoleColor col) => consoleIO.Bind(e => e.SetColor(col)); public static K resetColour() => consoleIO.Bind(e => e.ResetColor()); public static K bgColour => consoleIO.Bind(e => e.BgColor); public static K colour => consoleIO.Bind(e => e.Color); } ================================================ FILE: LanguageExt.Sys/Sys/Diag/Activity.Eff.cs ================================================ using System; using System.Diagnostics; using LanguageExt.Sys.Traits; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt.Sys.Diag; /// /// An `Activity` has an operation name, an ID, a start time and duration, tags, and baggage. /// /// Activities should be created by calling the `span` functions, configured as necessary. Each `span` function /// takes an `Eff` or `Aff` operation to run (which is the activity). The runtime system will maintain the parent/ /// child relationships for the activities, and maintains the 'current' activity. /// /// Runtime public class Activity where RT : Has, ActivitySourceIO>, Local, ActivityEnv> { static Eff env => Has, RT, ActivityEnv>.ask.As(); static Eff currentActivity => env.Map(e => e.Activity); public static Eff startActivity( string name, ActivityKind activityKind, HashMap activityTags, Seq activityLinks, DateTimeOffset startTime, ActivityContext? parentContext = default) => Activity, RT>.startActivity( name, activityKind, activityTags, activityLinks, startTime, parentContext).As(); /// /// Creates a new activity if there are active listeners for it, using the specified name, activity kind, parent /// activity context, tags, optional activity link and optional start time. /// /// The operation name of the activity. /// The operation to whose activity will be traced /// The result of the `operation` public static Eff span(string name, K, A> operation) => Activity, RT>.span(name, operation).As(); /// /// Creates a new activity if there are active listeners for it, using the specified name, activity kind, parent /// activity context, tags, optional activity link and optional start time. /// /// The operation name of the activity. /// The activity kind. /// The operation to whose activity will be traced /// The result of the `operation` public static Eff span( string name, ActivityKind activityKind, K, A> operation) => Activity, RT>.span(name, activityKind, operation).As(); /// /// Creates a new activity if there are active listeners for it, using the specified name, activity kind, parent /// activity context, tags, optional activity link and optional start time. /// /// The operation name of the activity. /// The activity kind. /// The optional tags list to initialise the created activity object with. /// The operation to whose activity will be traced /// The result of the `operation` public static Eff span( string name, ActivityKind activityKind, HashMap activityTags, K, A> operation) => Activity, RT>.span(name, activityKind, activityTags, operation).As(); /// /// Creates a new activity if there are active listeners for it, using the specified name, activity kind, parent /// activity context, tags, optional activity link and optional start time. /// /// The operation name of the activity. /// The activity kind. /// The optional tags list to initialise the created activity object with. /// The optional `ActivityLink` list to initialise the created activity object with. /// The optional start timestamp to set on the created activity object. /// The operation to whose activity will be traced /// The result of the `operation` public static Eff span( string name, ActivityKind activityKind, HashMap activityTags, Seq activityLinks, DateTimeOffset startTime, K, TA> operation) => Activity, RT>.span(name, activityKind, activityTags, activityLinks, startTime, operation).As(); /// /// Creates a new activity if there are active listeners for it, using the specified name, activity kind, parent /// activity context, tags, optional activity link and optional start time. /// /// The operation name of the activity. /// The activity kind. /// The parent `ActivityContext` object to initialize the created activity object /// with /// The optional tags list to initialise the created activity object with. /// The optional `ActivityLink` list to initialise the created activity object with. /// The optional start timestamp to set on the created activity object. /// The operation to whose activity will be traced /// The result of the `operation` public static Eff span( string name, ActivityKind activityKind, ActivityContext parentContext, HashMap activityTags, Seq activityLinks, DateTimeOffset startTime, K, A> operation) => Activity, RT>.span( name, activityKind, parentContext, activityTags, activityLinks, startTime, operation).As(); /// /// Set the state trace string /// /// Trace state string /// Unit effect public static Eff setTraceState(string traceStateString) => Activity, RT>.setTraceState(traceStateString).As(); /// /// Read the trace-state string of the current activity /// public static Eff> traceState => Activity, RT>.traceState.As(); /// /// Read the trace ID of the current activity /// public static Eff> traceId => Activity, RT>.traceId.As(); /// /// Add baggage to the current activity /// /// Baggage key /// Baggage value /// Unit effect public static Eff addBaggage(string key, string? value) => Activity, RT>.addBaggage(key, value).As(); /// /// Read the baggage of the current activity /// public static Eff> baggage => Activity, RT>.baggage.As(); /// /// Add tag to the current activity /// /// Tag name /// Tag value /// Unit effect public static Eff addTag(string name, string? value) => Activity, RT>.addTag(name, value).As(); /// /// Add tag to the current activity /// /// Tag name /// Tag value public static Eff addTag(string name, object? value) => Activity, RT>.addTag(name, value).As(); /// /// Read the tags of the current activity /// public static Eff> tags => Activity, RT>.tags.As(); /// /// Read the tags of the current activity /// public static Eff> tagObjects => Activity, RT>.tagObjects.As(); /// /// Read the context of the current activity /// /// None if there is no current activity public static Eff> context => Activity, RT>.context.As(); /// /// Read the duration of the current activity /// /// None if there is no current activity public static Eff> duration => Activity, RT>.duration.As(); /// /// Add an event to the current activity /// /// Event public static Eff addEvent(ActivityEvent @event) => Activity, RT>.addEvent(@event).As(); /// /// Read the events of the current activity /// public static Eff> events => Activity, RT>.events.As(); /// /// Read the ID of the current activity /// /// None if there is no current activity public static Eff> id => Activity, RT>.id.As(); /// /// Read the kind of the current activity /// /// None if there is no current activity public static Eff> kind => Activity, RT>.kind.As(); /// /// Read the links of the current activity /// public static Eff> links => Activity, RT>.links.As(); /// /// Read the current activity /// /// None if there is no current activity public static Eff> current => Activity, RT>.current.As(); /// /// Read the parent ID of the current activity /// /// None if there is no current activity public static Eff> parentId => Activity, RT>.parentId.As(); /// /// Read the parent span ID of the current activity /// /// None if there is no current activity public static Eff> parentSpanId => Activity, RT>.parentSpanId.As(); /// /// Read the recorded flag of the current activity /// /// None if there is no current activity public static Eff> recorded => Activity, RT>.recorded.As(); /// /// Read the display-name of the current activity /// /// None if there is no current activity public static Eff> displayName => Activity, RT>.displayName.As(); /// /// Read the operation-name of the current activity /// /// None if there is no current activity public static Eff> operationName => Activity, RT>.operationName.As(); /// /// Read the root ID of the current activity /// /// None if there is no current activity public static Eff> rootId => Activity, RT>.rootId.As(); /// /// Read the span ID of the current activity /// /// None if there is no current activity public static Eff> spanId => Activity, RT>.spanId.As(); /// /// Read the start-time of the current activity /// /// None if there is no current activity public static Eff> startTimeUTC => Activity, RT>.startTimeUTC.As(); } ================================================ FILE: LanguageExt.Sys/Sys/Diag/Activity.cs ================================================ using System; using System.Diagnostics; using LanguageExt.Sys.Traits; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt.Sys.Diag; /// /// An `Activity` has an operation name, an ID, a start time and duration, tags, and baggage. /// /// Activities should be created by calling the `span` functions, configured as necessary. Each `span` function /// takes an `Eff` or `Aff` operation to run (which is the activity). The runtime system will maintain the parent/ /// child relationships for the activities, and maintains the 'current' activity. /// /// Monad trait /// Runtime public class Activity where M : MonadIO where RT : Has, Local { static K activityIO => Has.ask; static K env => Has.ask; public static K currentActivity => env.Map(e => e.Activity); public static K startActivity( string name, ActivityKind activityKind, HashMap activityTags, Seq activityLinks, DateTimeOffset startTime, ActivityContext? parentContext = null) => from src in activityIO from cur in currentActivity from act in use(src.StartActivity( name, activityKind, cur is null ? default : parentContext ?? cur.Context, activityTags, activityLinks, startTime).Map(a => a ?? throw new NullReferenceException("Activity is null"))) select act; /// /// Creates a new activity if there are active listeners for it, using the specified name, activity kind, parent /// activity context, tags, optional activity link and optional start time. /// /// The operation name of the activity. /// The operation to whose activity will be traced /// The result of the `operation` public static K span(string name, K operation) => span(name, ActivityKind.Internal, default, default, DateTimeOffset.Now, operation); /// /// Creates a new activity if there are active listeners for it, using the specified name, activity kind, parent /// activity context, tags, optional activity link and optional start time. /// /// The operation name of the activity. /// The activity kind. /// The operation to whose activity will be traced /// The result of the `operation` public static K span( string name, ActivityKind activityKind, K operation) => span(name, activityKind, default, default, DateTimeOffset.Now, operation); /// /// Creates a new activity if there are active listeners for it, using the specified name, activity kind, parent /// activity context, tags, optional activity link and optional start time. /// /// The operation name of the activity. /// The activity kind. /// The optional tags list to initialise the created activity object with. /// The operation to whose activity will be traced /// The result of the `operation` public static K span( string name, ActivityKind activityKind, HashMap activityTags, K operation) => span(name, activityKind, activityTags, default, DateTimeOffset.Now, operation); /// /// Creates a new activity if there are active listeners for it, using the specified name, activity kind, parent /// activity context, tags, optional activity link and optional start time. /// /// The operation name of the activity. /// The activity kind. /// The optional tags list to initialise the created activity object with. /// The optional `ActivityLink` list to initialise the created activity object with. /// The optional start timestamp to set on the created activity object. /// The operation to whose activity will be traced /// The result of the `operation` public static K span( string name, ActivityKind activityKind, HashMap activityTags, Seq activityLinks, DateTimeOffset startTime, K operation) => from a in startActivity(name, activityKind, activityTags, activityLinks, startTime) from r in Local.with(e => e with { Activity = a }, operation) select r; /// /// Creates a new activity if there are active listeners for it, using the specified name, activity kind, parent /// activity context, tags, optional activity link and optional start time. /// /// The operation name of the activity. /// The activity kind. /// The parent `ActivityContext` object to initialize the created activity object /// with /// The optional tags list to initialise the created activity object with. /// The optional `ActivityLink` list to initialise the created activity object with. /// The optional start timestamp to set on the created activity object. /// The operation to whose activity will be traced /// The result of the `operation` public static K span( string name, ActivityKind activityKind, ActivityContext parentContext, HashMap activityTags, Seq activityLinks, DateTimeOffset startTime, K operation) => from a in startActivity( name, activityKind, activityTags, activityLinks, startTime, parentContext) from r in Local.with(e => e with { Activity = a }, operation) select r; /// /// Set the state trace string /// /// Trace state string /// Unit effect public static K setTraceState(string traceStateString) => currentActivity.Map( a => { if (a is not null) a.TraceStateString = traceStateString; return unit; }); /// /// Read the trace-state string of the current activity /// public static K> traceState => currentActivity.Map(a => Optional(a?.TraceStateString)); /// /// Read the trace ID of the current activity /// public static K> traceId => currentActivity.Map(a => Optional(a?.TraceId)); /// /// Add baggage to the current activity /// /// Baggage key /// Baggage value /// Unit effect public static K addBaggage(string key, string? value) => currentActivity.Map( a => { a?.AddBaggage(key, value); return unit; }); /// /// Read the baggage of the current activity /// public static K> baggage => currentActivity.Map( a => a is not null ? a.Baggage.AsIterable().Map(kv => (kv.Key, kv.Value)).ToHashMap() : HashMap()); /// /// Add tag to the current activity /// /// Tag name /// Tag value /// Unit effect public static K addTag(string name, string? value) => currentActivity.Map( a => { a?.AddTag(name, value); return unit; }); /// /// Add tag to the current activity /// /// Tag name /// Tag value public static K addTag(string name, object? value) => currentActivity.Map( a => { a?.AddTag(name, value); return unit; }); /// /// Read the tags of the current activity /// public static K> tags => currentActivity.Map( a => a is not null ? a.Tags.AsIterable().Map(kv => (kv.Key, kv.Value)).ToHashMap() : HashMap()); /// /// Read the tags of the current activity /// public static K> tagObjects => currentActivity.Map( a => a is not null ? a.TagObjects.AsIterable().Map(kv => (kv.Key, kv.Value)).ToHashMap() : HashMap()); /// /// Read the context of the current activity /// /// None if there is no current activity public static K> context => currentActivity.Map(a => Optional(a?.Context)); /// /// Read the duration of the current activity /// /// None if there is no current activity public static K> duration => currentActivity.Map(a => Optional(a?.Duration)); /// /// Add an event to the current activity /// /// Event public static K addEvent(ActivityEvent @event) => currentActivity.Map( a => { a?.AddEvent(@event); return unit; }); /// /// Read the events of the current activity /// public static K> events => currentActivity.Map( a => a is not null ? a.Events.AsIterable().ToSeq() : Seq()); /// /// Read the ID of the current activity /// /// None if there is no current activity public static K> id => currentActivity.Map(a => Optional(a?.Id)); /// /// Read the kind of the current activity /// /// None if there is no current activity public static K> kind => currentActivity.Map(a => Optional(a?.Kind)); /// /// Read the links of the current activity /// public static K> links => currentActivity.Map( a => a is not null ? a.Links.AsIterable().ToSeq() : Seq()); /// /// Read the current activity /// /// None if there is no current activity public static K> current => currentActivity.Map(a => Optional(a)); /// /// Read the parent ID of the current activity /// /// None if there is no current activity public static K> parentId => currentActivity.Map(a => Optional(a?.ParentId)); /// /// Read the parent span ID of the current activity /// /// None if there is no current activity public static K> parentSpanId => currentActivity.Map(a => Optional(a?.ParentSpanId)); /// /// Read the recorded flag of the current activity /// /// None if there is no current activity public static K> recorded => currentActivity.Map(a => Optional(a?.Recorded)); /// /// Read the display-name of the current activity /// /// None if there is no current activity public static K> displayName => currentActivity.Map(a => Optional(a?.DisplayName)); /// /// Read the operation-name of the current activity /// /// None if there is no current activity public static K> operationName => currentActivity.Map(a => Optional(a?.OperationName)); /// /// Read the root ID of the current activity /// /// None if there is no current activity public static K> rootId => currentActivity.Map(a => Optional(a?.RootId)); /// /// Read the span ID of the current activity /// /// None if there is no current activity public static K> spanId => currentActivity.Map(a => Optional(a?.SpanId)); /// /// Read the start-time of the current activity /// /// None if there is no current activity public static K> startTimeUTC => currentActivity.Map(a => Optional(a?.StartTimeUtc)); } ================================================ FILE: LanguageExt.Sys/Sys/Encoding.Eff.cs ================================================ using LanguageExt.Sys.Traits; using LanguageExt.Traits; namespace LanguageExt.Sys; public static class Enc where RT : Has, EncodingIO> { /// /// Encoding /// public static Eff encoding => Enc, RT>.encoding.As(); } ================================================ FILE: LanguageExt.Sys/Sys/Encoding.cs ================================================ using LanguageExt.Sys.Traits; using LanguageExt.Traits; namespace LanguageExt.Sys; public static class Enc where M : MonadIO where RT : Has { static readonly K encIO = Has.ask; /// /// Encoding /// public static K encoding => encIO.Bind(e => e.Encoding); } ================================================ FILE: LanguageExt.Sys/Sys/Environment.Eff.cs ================================================ using System; using LanguageExt.Sys.Traits; using LanguageExt.Traits; namespace LanguageExt.Sys; /// /// Environment IO /// public static class Environment where RT : Has, EnvironmentIO> { /// /// Gets the command line for this process. /// public static Eff commandLine => Environment, RT>.commandLine.As(); /// /// Gets a unique identifier for the current managed thread. /// public static Eff currentManagedThreadId => Environment, RT>.currentManagedThreadId.As(); /// /// Terminates this process and returns an exit code to the operating system. /// public static Eff exit(int exitCode) => Environment, RT>.exit(exitCode).As(); /// /// Gets the exit code of the process. /// public static Eff exitCode => Environment, RT>.exitCode.As(); /// /// Sets the exit code of the process. /// /// exit code of the process public static Eff setExitCode(int exitCode) => Environment, RT>.setExitCode(exitCode).As(); /// /// Replaces the name of each environment variable embedded in the specified string with the string equivalent of the value of the variable, then returns the resulting string. /// /// A string containing the names of zero or more environment variables. Each environment variable is quoted with the percent sign character (%). public static Eff expandEnvironmentVariables(string name) => Environment, RT>.expandEnvironmentVariables(name).As(); /// /// Immediately terminates a process after writing a message to the Windows Application event log, and then includes the message in error reporting to Microsoft. /// /// A message that explains why the process was terminated, or null if no explanation is provided. public static Eff failFast(Option message) => Environment, RT>.failFast(message).As(); /// /// Immediately terminates a process after writing a message to the Windows Application event log, and then includes the message and exception information in error reporting to Microsoft. /// /// A message that explains why the process was terminated, or null if no explanation is provided. /// An exception that represents the error that caused the termination. This is typically the exception in a catch block. public static Eff failFast(Option message, Option exception) => Environment, RT>.failFast(message, exception).As(); /// /// Returns a string array containing the command-line arguments for the current process. /// public static Eff> commandLineArgs => Environment, RT>.commandLineArgs.As(); /// /// Retrieves the value of an environment variable from the current process. /// /// The name of an environment variable. public static Eff> getEnvironmentVariable(string variable) => Environment, RT>.getEnvironmentVariable(variable).As(); /// /// Retrieves the value of an environment variable from the current process or from the Windows operating system registry key for the current user or local machine. /// /// The name of an environment variable. /// Target public static Eff> getEnvironmentVariable(string variable, EnvironmentVariableTarget target) => Environment, RT>.getEnvironmentVariable(variable, target).As(); /// /// Retrieves all environment variable names and their values from the current process. /// public static Eff>> environmentVariables => Environment, RT>.environmentVariables.As(); /// /// Retrieves all environment variable names and their values from the current process, or from the Windows operating system registry key for the current user or local machine. /// /// One of the System.EnvironmentVariableTarget values. Only System.EnvironmentVariableTarget.Process is supported on .NET Core running on Unix-based systems. public static Eff>> getEnvironmentVariables(EnvironmentVariableTarget target) => Environment, RT>.getEnvironmentVariables(target).As(); /// /// Gets the path to the system special folder that is identified by the specified enumeration. /// /// One of enumeration values that identifies a system special folder. public static Eff getFolderPath(Environment.SpecialFolder folder) => Environment, RT>.getFolderPath(folder).As(); /// /// Gets the path to the system special folder that is identified by the specified enumeration, and uses a specified option for accessing special folders. /// /// One of the enumeration values that identifies a system special folder. /// One of the enumeration values that specifies options to use for accessing a special folder. public static Eff getFolderPath(Environment.SpecialFolder folder, Environment.SpecialFolderOption option) => Environment, RT>.getFolderPath(folder, option).As(); /// /// Returns an array of string containing the names of the logical drives on the current computer. /// public static Eff> logicalDrives => Environment, RT>.logicalDrives.As(); /// /// Gets a value that indicates whether the current application domain is being unloaded or the common language runtime (CLR) is shutting down. /// public static Eff hasShutdownStarted => Environment, RT>.hasShutdownStarted.As(); /// /// Determines whether the current operating system is a 64-bit operating system. /// public static Eff is64BitOperatingSystem => Environment, RT>.is64BitOperatingSystem.As(); /// /// Determines whether the current process is a 64-bit process. /// public static Eff is64BitProcess => Environment, RT>.is64BitProcess.As(); /// /// Gets the NetBIOS name of this local computer. /// public static Eff machineName => Environment, RT>.machineName.As(); /// /// Gets the newline string defined for this environment. /// public static Eff newLine => Environment, RT>.newLine.As(); /// /// Gets an OperatingSystem object that contains the current platform identifier and version number. /// public static Eff osVersion => Environment, RT>.osVersion.As(); /// /// Gets the number of processors on the current machine. /// public static Eff processorCount => Environment, RT>.processorCount.As(); /// /// Creates, modifies, or deletes an environment variable stored in the current process. /// /// The name of an environment variable. /// A value to assign to variable . public static Eff setEnvironmentVariable(string variable, Option value) => Environment, RT>.setEnvironmentVariable(variable, value).As(); /// /// Creates, modifies, or deletes an environment variable stored in the current process or in the Windows operating system registry key reserved for the current user or local machine. /// /// The name of an environment variable. /// A value to assign to variable. /// One of the enumeration values that specifies the location of the environment variable. public static Eff setEnvironmentVariable(string variable, Option value, EnvironmentVariableTarget target) => Environment, RT>.setEnvironmentVariable(variable, value, target).As(); /// /// Gets current stack trace information. /// public static Eff stackTrace => Environment, RT>.stackTrace.As(); /// /// Gets the fully qualified path of the system directory. /// public static Eff systemDirectory => Environment, RT>.systemDirectory.As(); /// /// Gets the number of bytes in the operating system's memory page. /// public static Eff systemPageSize => Environment, RT>.systemPageSize.As(); /// /// Gets the number of milliseconds elapsed since the system started. /// public static Eff tickCount => Environment, RT>.tickCount.As(); /// /// Gets the network domain name associated with the current user. /// public static Eff userDomainName => Environment, RT>.userDomainName.As(); /// /// Gets a value indicating whether the current process is running in user interactive mode. /// public static Eff userInteractive => Environment, RT>.userInteractive.As(); /// /// Gets the user name of the person who is currently logged on to the operating system. /// public static Eff userName => Environment, RT>.userName.As(); /// /// Gets a Version object that describes the major, minor, build, and revision numbers of the common language runtime. /// public static Eff version => Environment, RT>.version.As(); /// /// Gets the amount of physical memory mapped to the process context. /// public static Eff workingSet => Environment, RT>.workingSet.As(); } ================================================ FILE: LanguageExt.Sys/Sys/Environment.cs ================================================ using System; using LanguageExt.Sys.Traits; using LanguageExt.Traits; namespace LanguageExt.Sys; /// /// Environment IO /// public static class Environment where M : MonadIO where RT : Has { static readonly K envIO = Has.ask; /// /// Gets the command line for this process. /// public static K commandLine => envIO.Bind(e => e.CommandLine()); /// /// Gets a unique identifier for the current managed thread. /// public static K currentManagedThreadId => envIO.Bind(e => e.CurrentManagedThreadId()); /// /// Terminates this process and returns an exit code to the operating system. /// public static K exit(int exitCode) => envIO.Bind(e => e.Exit(exitCode)); /// /// Gets the exit code of the process. /// public static K exitCode => envIO.Bind(e => e.ExitCode()); /// /// Sets the exit code of the process. /// /// exit code of the process public static K setExitCode(int exitCode) => envIO.Bind(e => e.SetExitCode(exitCode)); /// /// Replaces the name of each environment variable embedded in the specified string with the string equivalent of the value of the variable, then returns the resulting string. /// /// A string containing the names of zero or more environment variables. Each environment variable is quoted with the percent sign character (%). public static K expandEnvironmentVariables(string name) => envIO.Bind(e => e.ExpandEnvironmentVariables(name)); /// /// Immediately terminates a process after writing a message to the Windows Application event log, and then includes the message in error reporting to Microsoft. /// /// A message that explains why the process was terminated, or null if no explanation is provided. public static K failFast(Option message) => envIO.Bind(e => e.FailFast(message)); /// /// Immediately terminates a process after writing a message to the Windows Application event log, and then includes the message and exception information in error reporting to Microsoft. /// /// A message that explains why the process was terminated, or null if no explanation is provided. /// An exception that represents the error that caused the termination. This is typically the exception in a catch block. public static K failFast(Option message, Option exception) => envIO.Bind(e => e.FailFast(message, exception)); /// /// Returns a string array containing the command-line arguments for the current process. /// public static K> commandLineArgs => envIO.Bind(e => e.GetCommandLineArgs()); /// /// Retrieves the value of an environment variable from the current process. /// /// The name of an environment variable. public static K> getEnvironmentVariable(string variable) => envIO.Bind(e => e.GetEnvironmentVariable(variable)); /// /// Retrieves the value of an environment variable from the current process or from the Windows operating system registry key for the current user or local machine. /// /// The name of an environment variable. /// Target public static K> getEnvironmentVariable(string variable, EnvironmentVariableTarget target) => envIO.Bind(e => e.GetEnvironmentVariable(variable, target)); /// /// Retrieves all environment variable names and their values from the current process. /// public static K>> environmentVariables => envIO.Bind(e => e.GetEnvironmentVariables()); /// /// Retrieves all environment variable names and their values from the current process, or from the Windows operating system registry key for the current user or local machine. /// /// One of the System.EnvironmentVariableTarget values. Only System.EnvironmentVariableTarget.Process is supported on .NET Core running on Unix-based systems. public static K>> getEnvironmentVariables(EnvironmentVariableTarget target) => envIO.Bind(e => e.GetEnvironmentVariables(target)); /// /// Gets the path to the system special folder that is identified by the specified enumeration. /// /// One of enumeration values that identifies a system special folder. public static K getFolderPath(Environment.SpecialFolder folder) => envIO.Bind(e => e.GetFolderPath(folder)); /// /// Gets the path to the system special folder that is identified by the specified enumeration, and uses a specified option for accessing special folders. /// /// One of the enumeration values that identifies a system special folder. /// One of the enumeration values that specifies options to use for accessing a special folder. public static K getFolderPath(Environment.SpecialFolder folder, Environment.SpecialFolderOption option) => envIO.Bind(e => e.GetFolderPath(folder, option)); /// /// Returns an array of string containing the names of the logical drives on the current computer. /// public static K> logicalDrives => envIO.Bind(e => e.GetLogicalDrives()); /// /// Gets a value that indicates whether the current application domain is being unloaded or the common language runtime (CLR) is shutting down. /// public static K hasShutdownStarted => envIO.Bind(e => e.HasShutdownStarted()); /// /// Determines whether the current operating system is a 64-bit operating system. /// public static K is64BitOperatingSystem => envIO.Bind(e => e.Is64BitOperatingSystem()); /// /// Determines whether the current process is a 64-bit process. /// public static K is64BitProcess => envIO.Bind(e => e.Is64BitProcess()); /// /// Gets the NetBIOS name of this local computer. /// public static K machineName => envIO.Bind(e => e.MachineName()); /// /// Gets the newline string defined for this environment. /// public static K newLine => envIO.Bind(e => e.NewLine()); /// /// Gets an OperatingSystem object that contains the current platform identifier and version number. /// public static K osVersion => envIO.Bind(e => e.OSVersion()); /// /// Gets the number of processors on the current machine. /// public static K processorCount => envIO.Bind(e => e.ProcessorCount()); /// /// Creates, modifies, or deletes an environment variable stored in the current process. /// /// The name of an environment variable. /// A value to assign to variable . public static K setEnvironmentVariable(string variable, Option value) => envIO.Bind(e => e.SetEnvironmentVariable(variable, value)); /// /// Creates, modifies, or deletes an environment variable stored in the current process or in the Windows operating system registry key reserved for the current user or local machine. /// /// The name of an environment variable. /// A value to assign to variable. /// One of the enumeration values that specifies the location of the environment variable. public static K setEnvironmentVariable(string variable, Option value, EnvironmentVariableTarget target) => envIO.Bind(e => e.SetEnvironmentVariable(variable, value, target)); /// /// Gets current stack trace information. /// public static K stackTrace => envIO.Bind(e => e.StackTrace()); /// /// Gets the fully qualified path of the system directory. /// public static K systemDirectory => envIO.Bind(e => e.SystemDirectory()); /// /// Gets the number of bytes in the operating system's memory page. /// public static K systemPageSize => envIO.Bind(e => e.SystemPageSize()); /// /// Gets the number of milliseconds elapsed since the system started. /// public static K tickCount => envIO.Bind(e => e.TickCount()); /// /// Gets the network domain name associated with the current user. /// public static K userDomainName => envIO.Bind(e => e.UserDomainName()); /// /// Gets a value indicating whether the current process is running in user interactive mode. /// public static K userInteractive => envIO.Bind(e => e.UserInteractive()); /// /// Gets the user name of the person who is currently logged on to the operating system. /// public static K userName => envIO.Bind(e => e.UserName()); /// /// Gets a Version object that describes the major, minor, build, and revision numbers of the common language runtime. /// public static K version => envIO.Bind(e => e.Version()); /// /// Gets the amount of physical memory mapped to the process context. /// public static K workingSet => envIO.Bind(e => e.WorkingSet()); } ================================================ FILE: LanguageExt.Sys/Sys/IO/Directory.Eff.cs ================================================ using System; using System.IO; using LanguageExt.Sys.Traits; using LanguageExt.Traits; namespace LanguageExt.Sys.IO; public class Directory where RT : Has, DirectoryIO> { /// /// Create a directory /// public static Eff create(string path) => Directory, RT>.create(path).As(); /// /// Delete a directory /// public static Eff delete(string path, bool recursive = true) => Directory, RT>.delete(path, recursive).As(); /// /// Get parent directory /// public static Eff> getParent(string path) => Directory, RT>.getParent(path).As(); /// /// Check if directory exists /// public static Eff exists(string path) => Directory, RT>.exists(path).As(); /// /// Set the directory creation time /// public static Eff setCreationTime(string path, DateTime creationTime) => Directory, RT>.setCreationTime(path, creationTime).As(); /// /// Set the directory creation time /// public static Eff setCreationTimeUtc(string path, DateTime creationTimeUtc) => Directory, RT>.setCreationTimeUtc(path, creationTimeUtc).As(); /// /// Get the directory creation time /// public static Eff getCreationTime(string path) => Directory, RT>.getCreationTime(path).As(); /// /// Get the directory creation time /// public static Eff getCreationTimeUtc(string path) => Directory, RT>.getCreationTimeUtc(path).As(); /// /// Set the directory last write time /// public static Eff setLastWriteTime(string path, DateTime lastWriteTime) => Directory, RT>.setLastWriteTime(path, lastWriteTime).As(); /// /// Set the directory last write time /// public static Eff setLastWriteTimeUtc(string path, DateTime lastWriteTimeUtc) => Directory, RT>.setLastWriteTimeUtc(path, lastWriteTimeUtc).As(); /// /// Get the directory last write time /// public static Eff getLastWriteTime(string path) => Directory, RT>.getLastWriteTime(path).As(); /// /// Get the directory last write time /// public static Eff getLastWriteTimeUtc(string path) => Directory, RT>.getLastWriteTimeUtc(path).As(); /// /// Set the directory last access time /// public static Eff setLastAccessTime(string path, DateTime lastAccessTime) => Directory, RT>.setLastAccessTime(path, lastAccessTime).As(); /// /// Set the directory last access time /// public static Eff setLastAccessTimeUtc(string path, DateTime lastAccessTimeUtc) => Directory, RT>.setLastAccessTimeUtc(path, lastAccessTimeUtc).As(); /// /// Get the directory last access time /// public static Eff getLastAccessTime(string path) => Directory, RT>.getLastAccessTime(path).As(); /// /// Get the directory last access time /// public static Eff getLastAccessTimeUtc(string path) => Directory, RT>.getLastAccessTimeUtc(path).As(); /// /// Enumerate directories /// public static Eff> enumerateDirectories(string path) => Directory, RT>.enumerateDirectories(path).As(); /// /// Enumerate directories /// public static Eff> enumerateDirectories(string path, string searchPattern) => Directory, RT>.enumerateDirectories(path, searchPattern).As(); /// /// Enumerate directories /// public static Eff> enumerateDirectories(string path, string searchPattern, SearchOption searchOption) => Directory, RT>.enumerateDirectories(path, searchPattern, searchOption).As(); /// /// Enumerate files /// public static Eff> enumerateFiles(string path) => Directory, RT>.enumerateFiles(path).As(); /// /// Enumerate files /// public static Eff> enumerateFiles(string path, string searchPattern) => Directory, RT>.enumerateFiles(path, searchPattern).As(); /// /// Enumerate files /// public static Eff> enumerateFiles(string path, string searchPattern, SearchOption searchOption) => Directory, RT>.enumerateFiles(path, searchPattern, searchOption).As(); /// /// Enumerate file system entries /// public static Eff> enumerateFileSystemEntries(string path) => Directory, RT>.enumerateFileSystemEntries(path).As(); /// /// Enumerate file system entries /// public static Eff> enumerateFileSystemEntries(string path, string searchPattern) => Directory, RT>.enumerateFileSystemEntries(path, searchPattern).As(); /// /// Enumerate file system entries /// public static Eff> enumerateFileSystemEntries(string path, string searchPattern, SearchOption searchOption) => Directory, RT>.enumerateFileSystemEntries(path, searchPattern, searchOption).As(); /// /// Get the root of the path provided /// public static Eff getRoot(string path) => Directory, RT>.getRoot(path).As(); /// /// Get the current directory /// public static Eff current => Directory, RT>.current.As(); /// /// Set the current directory /// /// public static Eff setCurrent(string path) => Directory, RT>.setCurrent(path).As(); /// /// Move a directory /// public static Eff move(string sourceDirName, string destDirName) => Directory, RT>.move(sourceDirName, destDirName).As(); /// /// Get the logical drives /// public static Eff> logicalDrives => Directory, RT>.logicalDrives.As(); } ================================================ FILE: LanguageExt.Sys/Sys/IO/Directory.cs ================================================ using System; using System.IO; using LanguageExt.Sys.Traits; using LanguageExt.Traits; namespace LanguageExt.Sys.IO; public class Directory where M : MonadIO where RT : Has { static K directoryIO => Has.ask; /// /// Create a directory /// public static K create(string path) => directoryIO.Bind(rt => rt.Create(path)); /// /// Delete a directory /// public static K delete(string path, bool recursive = true) => directoryIO.Bind(rt => rt.Delete(path, recursive)); /// /// Get parent directory /// public static K> getParent(string path) => directoryIO.Bind(rt => rt.GetParent(path)); /// /// Check if directory exists /// public static K exists(string path) => directoryIO.Bind(rt => rt.Exists(path)); /// /// Set the directory creation time /// public static K setCreationTime(string path, DateTime creationTime) => directoryIO.Bind(rt => rt.SetCreationTime(path, creationTime)); /// /// Set the directory creation time /// public static K setCreationTimeUtc(string path, DateTime creationTimeUtc) => directoryIO.Bind(rt => rt.SetCreationTimeUtc(path, creationTimeUtc)); /// /// Get the directory creation time /// public static K getCreationTime(string path) => directoryIO.Bind(rt => rt.GetCreationTime(path)); /// /// Get the directory creation time /// public static K getCreationTimeUtc(string path) => directoryIO.Bind(rt => rt.GetCreationTimeUtc(path)); /// /// Set the directory last write time /// public static K setLastWriteTime(string path, DateTime lastWriteTime) => directoryIO.Bind(rt => rt.SetLastWriteTime(path, lastWriteTime)); /// /// Set the directory last write time /// public static K setLastWriteTimeUtc(string path, DateTime lastWriteTimeUtc) => directoryIO.Bind(rt => rt.SetLastWriteTimeUtc(path, lastWriteTimeUtc)); /// /// Get the directory last write time /// public static K getLastWriteTime(string path) => directoryIO.Bind(rt => rt.GetLastWriteTime(path)); /// /// Get the directory last write time /// public static K getLastWriteTimeUtc(string path) => directoryIO.Bind(rt => rt.GetLastWriteTimeUtc(path)); /// /// Set the directory last access time /// public static K setLastAccessTime(string path, DateTime lastAccessTime) => directoryIO.Bind(rt => rt.SetLastAccessTime(path, lastAccessTime)); /// /// Set the directory last access time /// public static K setLastAccessTimeUtc(string path, DateTime lastAccessTimeUtc) => directoryIO.Bind(rt => rt.SetLastAccessTimeUtc(path, lastAccessTimeUtc)); /// /// Get the directory last access time /// public static K getLastAccessTime(string path) => directoryIO.Bind(rt => rt.GetLastAccessTime(path)); /// /// Get the directory last access time /// public static K getLastAccessTimeUtc(string path) => directoryIO.Bind(rt => rt.GetLastAccessTimeUtc(path)); /// /// Enumerate directories /// public static K> enumerateDirectories(string path) => directoryIO.Bind(rt => rt.EnumerateDirectories(path)); /// /// Enumerate directories /// public static K> enumerateDirectories(string path, string searchPattern) => directoryIO.Bind(rt => rt.EnumerateDirectories(path, searchPattern)); /// /// Enumerate directories /// public static K> enumerateDirectories(string path, string searchPattern, SearchOption searchOption) => directoryIO.Bind(rt => rt.EnumerateDirectories(path, searchPattern, searchOption)); /// /// Enumerate files /// public static K> enumerateFiles(string path) => directoryIO.Bind(rt => rt.EnumerateFiles(path)); /// /// Enumerate files /// public static K> enumerateFiles(string path, string searchPattern) => directoryIO.Bind(rt => rt.EnumerateFiles(path, searchPattern)); /// /// Enumerate files /// public static K> enumerateFiles(string path, string searchPattern, SearchOption searchOption) => directoryIO.Bind(rt => rt.EnumerateFiles(path, searchPattern, searchOption)); /// /// Enumerate file system entries /// public static K> enumerateFileSystemEntries(string path) => directoryIO.Bind(rt => rt.EnumerateFileSystemEntries(path)); /// /// Enumerate file system entries /// public static K> enumerateFileSystemEntries(string path, string searchPattern) => directoryIO.Bind(rt => rt.EnumerateFileSystemEntries(path, searchPattern)); /// /// Enumerate file system entries /// public static K> enumerateFileSystemEntries(string path, string searchPattern, SearchOption searchOption) => directoryIO.Bind(rt => rt.EnumerateFileSystemEntries(path, searchPattern, searchOption)); /// /// Get the root of the path provided /// public static K getRoot(string path) => directoryIO.Bind(rt => rt.GetDirectoryRoot(path)); /// /// Get the current directory /// public static K current => directoryIO.Bind(rt => rt.GetCurrentDirectory()); /// /// Set the current directory /// /// public static K setCurrent(string path) => directoryIO.Bind(rt => rt.SetCurrentDirectory(path)); /// /// Move a directory /// public static K move(string sourceDirName, string destDirName) => directoryIO.Bind(rt => rt.Move(sourceDirName, destDirName)); /// /// Get the logical drives /// public static K> logicalDrives => directoryIO.Bind(rt => rt.GetLogicalDrives()); } ================================================ FILE: LanguageExt.Sys/Sys/IO/File.Eff.cs ================================================ using System.IO; using LanguageExt.Pipes; using LanguageExt.Traits; using LanguageExt.Sys.Traits; using System.Collections.Generic; using System.Diagnostics.Contracts; using System.Runtime.CompilerServices; namespace LanguageExt.Sys.IO; /// /// File IO /// public class File where RT : Has, FileIO>, Has, EncodingIO> { /// /// Copy file /// /// Source path /// Destination path /// Overwrite if the file already exists at the destination /// Runtime /// Unit [Pure, MethodImpl(EffOpt.mops)] public static Eff copy(string fromPath, string toPath, bool overwrite = false) => File, RT>.copy(fromPath, toPath, overwrite).As(); /// /// Append lines to the end of the file provided /// [Pure, MethodImpl(EffOpt.mops)] public static Eff appendAllLines(string path, IEnumerable contents) => File, RT>.appendAllLines(path, contents).As(); /// /// Read all of the lines from the path provided /// [Pure, MethodImpl(EffOpt.mops)] public static Eff> readAllLines(string path) => File, RT>.readAllLines(path).As(); /// /// Write all of the lines to the path provided /// [Pure, MethodImpl(EffOpt.mops)] public static Eff writeAllLines(string path, Seq lines) => File, RT>.writeAllLines(path, lines).As(); /// /// Read all of the text from the path provided /// [Pure, MethodImpl(EffOpt.mops)] public static Eff readAllText(string path) => File, RT>.readAllText(path).As(); /// /// Read all of the data from the path provided /// [Pure, MethodImpl(EffOpt.mops)] public static Eff readAllBytes(string path) => File, RT>.readAllBytes(path).As(); /// /// Write all of the text to the path provided /// [Pure, MethodImpl(EffOpt.mops)] public static Eff writeAllText(string path, string text) => File, RT>.writeAllText(path, text).As(); /// /// Write all of the data to the path provided /// [Pure, MethodImpl(EffOpt.mops)] public static Eff writeAllBytes(string path, byte[] data) => File, RT>.writeAllBytes(path, data).As(); /// /// Delete the file provided /// [Pure, MethodImpl(EffOpt.mops)] public static Eff delete(string path) => File, RT>.delete(path).As(); /// /// True if a file exists at the path /// [Pure, MethodImpl(EffOpt.mops)] public static Eff exists(string path) => File, RT>.exists(path).As(); /// /// Open a text file /// [Pure, MethodImpl(EffOpt.mops)] public static Producer openText(string path) => File, RT>.openText(path); /// /// Create a new text file to stream to /// [Pure, MethodImpl(EffOpt.mops)] public static Eff createText(string path) => File, RT>.createText(path).As(); /// /// Return a stream to append text to /// [Pure, MethodImpl(EffOpt.mops)] public static Eff appendText(string path) => File, RT>.appendText(path).As(); /// /// Open a file-stream /// public static Producer openRead(string path) => File, RT>.openRead(path); /// /// Open a file-stream /// [Pure, MethodImpl(EffOpt.mops)] public static Producer open(string path, FileMode mode) => File, RT>.open(path, mode); /// /// Open a file-stream /// [Pure, MethodImpl(EffOpt.mops)] public static Producer open(string path, FileMode mode, FileAccess access) => File, RT>.open(path, mode, access); /// /// Open a file-stream /// [Pure, MethodImpl(EffOpt.mops)] public static Producer openWrite(string path) => File, RT>.openWrite(path); } ================================================ FILE: LanguageExt.Sys/Sys/IO/File.cs ================================================ using System.IO; using LanguageExt.Pipes; using LanguageExt.Sys.Traits; using System.Collections.Generic; using static LanguageExt.Prelude; using System.Diagnostics.Contracts; using System.Runtime.CompilerServices; using LanguageExt.Traits; namespace LanguageExt.Sys.IO; /// /// File IO /// public class File where M : MonadIO where RT : Has, Has { static K fileIO => Has.ask; /// /// Copy file /// /// Source path /// Destination path /// Overwrite if the file already exists at the destination /// Runtime /// Unit [Pure, MethodImpl(EffOpt.mops)] public static K copy(string fromPath, string toPath, bool overwrite = false) => fileIO.Bind(e => e.Copy(fromPath, toPath, overwrite)); /// /// Move file /// /// Source path /// Destination path /// Runtime /// Unit [Pure, MethodImpl(EffOpt.mops)] public static K move(string fromPath, string toPath) => fileIO.Bind(e => e.Move(fromPath, toPath)); /// /// Move file /// /// Source path /// Destination path /// Overwrite if the file already exists at the destination /// Runtime /// Unit [Pure, MethodImpl(EffOpt.mops)] public static K move(string fromPath, string toPath, bool overwrite) => fileIO.Bind(e => e.Move(fromPath, toPath, overwrite)); /// /// Append lines to the end of the file provided /// [Pure, MethodImpl(EffOpt.mops)] public static K appendAllLines(string path, IEnumerable contents) => from en in Enc.encoding from rs in fileIO.Bind(e => e.AppendAllLines(path, contents, en)) select rs; /// /// Read all of the lines from the path provided /// [Pure, MethodImpl(EffOpt.mops)] public static K> readAllLines(string path) => from en in Enc.encoding from rs in fileIO.Bind(e => e.ReadAllLines(path, en)) select rs; /// /// Write all of the lines to the path provided /// [Pure, MethodImpl(EffOpt.mops)] public static K writeAllLines(string path, Seq lines) => from en in Enc.encoding from rs in fileIO.Bind(e => e.WriteAllLines(path, lines, en)) select rs; /// /// Read all of the text from the path provided /// [Pure, MethodImpl(EffOpt.mops)] public static K readAllText(string path) => from en in Enc.encoding from rs in fileIO.Bind(e => e.ReadAllText(path, en)) select rs; /// /// Read all of the data from the path provided /// [Pure, MethodImpl(EffOpt.mops)] public static K readAllBytes(string path) => fileIO.Bind(e => e.ReadAllBytes(path)); /// /// Write all of the text to the path provided /// [Pure, MethodImpl(EffOpt.mops)] public static K writeAllText(string path, string text) => from en in Enc.encoding from rs in fileIO.Bind(e => e.WriteAllText(path, text, en)) select rs; /// /// Write all of the data to the path provided /// [Pure, MethodImpl(EffOpt.mops)] public static K writeAllBytes(string path, byte[] data) => fileIO.Bind(e => e.WriteAllBytes(path, data)); /// /// Delete the file provided /// [Pure, MethodImpl(EffOpt.mops)] public static K delete(string path) => fileIO.Bind(e => e.Delete(path)); /// /// True if a file exists at the path /// [Pure, MethodImpl(EffOpt.mops)] public static K exists(string path) => fileIO.Bind(e => e.Exists(path)); /// /// Open a text file /// [Pure, MethodImpl(EffOpt.mops)] public static ProducerT openText(string path) => from t in openTextInternal(path) from _ in ProducerT.yield(t) select unit; /// /// Create a new text file to stream to /// [Pure, MethodImpl(EffOpt.mops)] public static K createText(string path) => fileIO.Bind(e => e.CreateText(path)); /// /// Return a stream to append text to /// [Pure, MethodImpl(EffOpt.mops)] public static K appendText(string path) => fileIO.Bind(e => e.AppendText(path)); /// /// Open a file-stream /// public static ProducerT openRead(string path) => from s in openReadInternal(path) from _ in ProducerT.yield(s) select unit; /// /// Open a file-stream /// [Pure, MethodImpl(EffOpt.mops)] public static ProducerT open(string path, FileMode mode) => from s in openInternal(path, mode) from _ in ProducerT.yield(s) select unit; /// /// Open a file-stream /// [Pure, MethodImpl(EffOpt.mops)] public static ProducerT open(string path, FileMode mode, FileAccess access) => from s in openInternal(path, mode, access) from _ in ProducerT.yield(s) select unit; /// /// Open a file-stream /// [Pure, MethodImpl(EffOpt.mops)] public static ProducerT openWrite(string path) => from s in openWriteInternal(path) from _ in ProducerT.yield(s) select unit; // -- Internal ------------------------------------------------------------------------------------------------- /// /// Open a file-stream /// static K openReadInternal(string path) => from io in fileIO.Map(e => e.OpenRead(path)) from rs in use(io) select rs; /// /// Open a file-stream /// [Pure, MethodImpl(EffOpt.mops)] static K openInternal(string path, FileMode mode) => from io in fileIO.Map(e => e.Open(path, mode)) from rs in use(io) select rs; /// /// Open a file-stream /// [Pure, MethodImpl(EffOpt.mops)] static K openInternal(string path, FileMode mode, FileAccess access) => from io in fileIO.Map(e => e.Open(path, mode, access)) from rs in use(io) select rs; /// /// Open a file-stream /// [Pure, MethodImpl(EffOpt.mops)] static K openWriteInternal(string path) => from io in fileIO.Map(e => e.OpenWrite(path)) from rs in use(io) select rs; /// /// Open a text file /// [Pure, MethodImpl(EffOpt.mops)] static K openTextInternal(string path) => from io in fileIO.Map(e => e.OpenText(path)) from rs in use(io) select rs; } ================================================ FILE: LanguageExt.Sys/Sys/IO/Stream.cs ================================================ using System.IO; using System.Buffers; using LanguageExt.Pipes; using System.Collections.Generic; using LanguageExt.Traits; using static LanguageExt.Prelude; using LanguageExt.UnsafeValueAccess; namespace LanguageExt.Sys.IO; public static class Stream where M : MonadIO { /// /// Get a pipe of chunks from a Stream /// public static PipeT, M, Unit> read(int chunkSize) { return from fs in PipeT.awaiting>() from _ in PipeT.yieldAll>(chunks(fs, chunkSize)) select unit; static async IAsyncEnumerable> chunks(Stream fs, int chunkSize) { var pool = ArrayPool.Shared; while (true) { var buffer = pool.Rent(chunkSize); var count = await fs.ReadAsync(buffer, 0, chunkSize).ConfigureAwait(false); if (count < 1) { pool.Return(buffer); yield break; } yield return buffer.ToSeqLoanUnsafe(count, pool); } } } } ================================================ FILE: LanguageExt.Sys/Sys/IO/TextRead.Eff.cs ================================================ using System.IO; using LanguageExt.Pipes; using LanguageExt.Traits; using LanguageExt.Sys.Traits; using System.Diagnostics.Contracts; using System.Runtime.CompilerServices; namespace LanguageExt.Sys.IO; public static class TextRead where RT : Has, TextReadIO> { /// /// Open a text file and streams the lines through the pipe /// [Pure] public static Pipe readLine => TextRead, RT>.readLine; /// /// Open a text file and streams the chars through the pipe /// [Pure] public static Pipe readChar => TextRead, RT>.readChar; /// /// Read the rest of the text in the stream /// [Pure] public static Pipe readToEnd => TextRead, RT>.readToEnd; /// /// Repeatedly read a number of chars from the stream /// [Pure] public static Pipe, Unit> readChars(int charCount) => TextRead, RT>.readChars(charCount).As(); /// /// Read a number of chars from the stream /// [Pure] public static Pipe read(int charCount) => TextRead, RT>.read(charCount).As(); /// /// Close the reader /// [Pure, MethodImpl(EffOpt.mops)] public static Eff close(TextReader reader) => TextRead, RT>.close(reader).As(); } ================================================ FILE: LanguageExt.Sys/Sys/IO/TextRead.cs ================================================ using System.Buffers; using System.IO; using LanguageExt.Pipes; using LanguageExt.Sys.Traits; using static LanguageExt.Prelude; using System.Collections.Generic; using System.Diagnostics.Contracts; using LanguageExt.UnsafeValueAccess; using System.Runtime.CompilerServices; using LanguageExt.Traits; using static LanguageExt.Pipes.PipeT; namespace LanguageExt.Sys.IO; public static class TextRead where RT : Has where M : MonadIO { static K textReadIO => Has.ask; /// /// Open a text file and streams the lines through the pipe /// [Pure] public static PipeT readLine { get { return from tr in awaiting() from _ in yieldAll(go(tr)) select unit; static async IAsyncEnumerable go(TextReader reader) { while (true) { var line = await reader.ReadLineAsync().ConfigureAwait(false); if(line == null) yield break; yield return line; } } } } /// /// Open a text file and streams the chars through the pipe /// [Pure] public static PipeT readChar { get { return from tr in awaiting() from _ in yieldAll(go(tr)) select unit; static async IAsyncEnumerable go(TextReader reader) { var buffer = new char[1]; while (true) { var nread = await reader.ReadAsync(buffer, 0, 1).ConfigureAwait(false); if(nread < 1) yield break; yield return buffer[0]; } } } } /// /// Read the rest of the text in the stream /// [Pure] public static PipeT readToEnd => from tr in awaiting() from tx in LanguageExt.IO.liftAsync(async e => await tr.ReadToEndAsync(e.Token)) from __ in yield(tx) select unit; /// /// Repeatedly read a number of chars from the stream /// [Pure] public static PipeT, M, Unit> readChars(int charCount) { return from tr in awaiting>() from _ in yieldAll>(go(tr, charCount)) select unit; static async IAsyncEnumerable> go(TextReader reader, int count) { var pool = ArrayPool.Shared; while (true) { var buffer = pool.Rent(count); var nread = await reader.ReadAsync(buffer, 0, count).ConfigureAwait(false); if(nread < 0) yield break; yield return buffer.ToSeqLoanUnsafe(nread, pool); } } } /// /// Read a number of chars from the stream /// [Pure] public static PipeT read(int charCount) { return from tr in awaiting() from _ in yieldAll(go(tr, charCount)) select unit; static async IAsyncEnumerable go(TextReader reader, int count) { var pool = ArrayPool.Shared; var buffer = pool.Rent(count); try { while (true) { var nread = await reader.ReadAsync(buffer, 0, count).ConfigureAwait(false); if (nread < 0) yield break; yield return new string(buffer); } } finally { pool.Return(buffer); } } } /// /// Close the reader /// [Pure, MethodImpl(EffOpt.mops)] public static K close(TextReader reader) => textReadIO.Bind(e => e.Close(reader)); } ================================================ FILE: LanguageExt.Sys/Sys/Time.Eff.cs ================================================ using System; using LanguageExt.Traits; using LanguageExt.Sys.Traits; using System.Diagnostics.Contracts; using System.Runtime.CompilerServices; namespace LanguageExt.Sys; /// /// DateTime IO /// public static class Time where RT : Has, TimeIO> { /// /// Current local date time /// public static Eff now => Time, RT>.now.As(); /// /// Current universal date time /// public static Eff nowUTC => Time, RT>.nowUTC.As(); /// /// Today's date /// public static Eff today => Time, RT>.today.As(); /// /// Pause a task until a specified time /// [Pure, MethodImpl(EffOpt.mops)] public static Eff sleepUntil(DateTime dt) => Time, RT>.sleepUntil(dt).As(); /// /// Pause a task until for a specified length of time /// [Pure, MethodImpl(EffOpt.mops)] public static Eff sleepFor(TimeSpan ts) => Time, RT>.sleepFor(ts).As(); } ================================================ FILE: LanguageExt.Sys/Sys/Time.cs ================================================ using System; using LanguageExt.Traits; using LanguageExt.Sys.Traits; using System.Diagnostics.Contracts; using System.Runtime.CompilerServices; namespace LanguageExt.Sys; /// /// DateTime IO /// public static class Time where M : MonadIO where RT : Has { static readonly K timeIO = Has.ask; /// /// Current local date time /// public static K now => timeIO.Bind(e => e.Now); /// /// Current universal date time /// public static K nowUTC => timeIO.Bind(e => e.UtcNow); /// /// Today's date /// public static K today => timeIO.Bind(e => e.Today); /// /// Pause a task until a specified time /// [Pure, MethodImpl(EffOpt.mops)] public static K sleepUntil(DateTime dt) => timeIO.Bind(e => e.SleepUntil(dt)); /// /// Pause a task until for a specified length of time /// [Pure, MethodImpl(EffOpt.mops)] public static K sleepFor(TimeSpan ts) => timeIO.Bind(e => e.SleepFor(ts)); } ================================================ FILE: LanguageExt.Sys/Test/Implementations/ConsoleIO.cs ================================================ using System; using static LanguageExt.Prelude; namespace LanguageExt.Sys.Test.Implementations; /// /// Encapsulated in-memory console /// No public API exists for this. Use Sys.IO.Console.* to interact with the console /// /// /// Primarily used for testing (for use with TestRuntime or your own testing runtime) /// public record ConsoleIO(MemoryConsole mem) : Sys.Traits.ConsoleIO { public IO> ReadKey() => lift(() => mem.ReadKey()); public IO Clear() => lift(() => mem.Clear()); public IO SetBgColor(ConsoleColor color) => lift(() => mem.SetBgColor(color)); public IO SetColor(ConsoleColor color) => lift(() => mem.SetColor(color)); public IO ResetColor() => lift(() => mem.ResetColor()); public IO BgColor => lift(() => mem.BgColor); public IO Color => lift(() => mem.Color); public IO> Read() => lift(() => mem.Read()); public IO> ReadLine() => lift(() => mem.ReadLine()); public IO WriteLine() => lift(() => mem.WriteLine()); public IO WriteLine(string value) => lift(() => mem.WriteLine(value)); public IO Write(string value) => lift(() => mem.Write(value)); } ================================================ FILE: LanguageExt.Sys/Test/Implementations/DirectoryIO.cs ================================================ using System; using System.IO; namespace LanguageExt.Sys.Test.Implementations; public record DirectoryIO(string root) : Traits.DirectoryIO { string FixPath(string path) => Path.Combine(root, path.Replace(":", "_drive")); public IO Create(string path) => Live.Implementations.DirectoryIO.Default.Create(FixPath(path)); public IO Delete(string path, bool recursive = true) => Live.Implementations.DirectoryIO.Default.Delete(FixPath(path), recursive); public IO> GetParent(string path) => Live.Implementations.DirectoryIO.Default.GetParent(FixPath(path)); public IO Exists(string path) => Live.Implementations.DirectoryIO.Default.Exists(FixPath(path)); public IO SetCreationTime(string path, DateTime creationTime) => Live.Implementations.DirectoryIO.Default.SetCreationTime(FixPath(path), creationTime); public IO SetCreationTimeUtc(string path, DateTime creationTimeUtc) => Live.Implementations.DirectoryIO.Default.SetCreationTimeUtc(FixPath(path), creationTimeUtc); public IO GetCreationTime(string path) => Live.Implementations.DirectoryIO.Default.GetCreationTime(FixPath(path)); public IO GetCreationTimeUtc(string path) => Live.Implementations.DirectoryIO.Default.GetCreationTimeUtc(FixPath(path)); public IO SetLastWriteTime(string path, DateTime lastWriteTime) => Live.Implementations.DirectoryIO.Default.SetLastWriteTime(FixPath(path), lastWriteTime); public IO SetLastWriteTimeUtc(string path, DateTime lastWriteTimeUtc) => Live.Implementations.DirectoryIO.Default.SetLastWriteTimeUtc(FixPath(path), lastWriteTimeUtc); public IO GetLastWriteTime(string path) => Live.Implementations.DirectoryIO.Default.GetLastWriteTime(FixPath(path)); public IO GetLastWriteTimeUtc(string path) => Live.Implementations.DirectoryIO.Default.GetLastWriteTimeUtc(FixPath(path)); public IO SetLastAccessTime(string path, DateTime lastAccessTime) => Live.Implementations.DirectoryIO.Default.SetLastAccessTime(FixPath(path), lastAccessTime); public IO SetLastAccessTimeUtc(string path, DateTime lastAccessTimeUtc) => Live.Implementations.DirectoryIO.Default.SetLastAccessTimeUtc(FixPath(path), lastAccessTimeUtc); public IO GetLastAccessTime(string path) => Live.Implementations.DirectoryIO.Default.GetLastAccessTime(FixPath(path)); public IO GetLastAccessTimeUtc(string path) => Live.Implementations.DirectoryIO.Default.GetLastAccessTimeUtc(FixPath(path)); public IO> EnumerateDirectories(string path) => Live.Implementations.DirectoryIO.Default.EnumerateDirectories(FixPath(path)); public IO> EnumerateDirectories(string path, string searchPattern) => Live.Implementations.DirectoryIO.Default.EnumerateDirectories(FixPath(path), searchPattern); public IO> EnumerateDirectories(string path, string searchPattern, SearchOption searchOption) => Live.Implementations.DirectoryIO.Default.EnumerateDirectories(FixPath(path), searchPattern, searchOption); public IO> EnumerateFiles(string path) => Live.Implementations.DirectoryIO.Default.EnumerateFiles(FixPath(path)); public IO> EnumerateFiles(string path, string searchPattern) => Live.Implementations.DirectoryIO.Default.EnumerateFiles(FixPath(path), searchPattern); public IO> EnumerateFiles(string path, string searchPattern, SearchOption searchOption) => Live.Implementations.DirectoryIO.Default.EnumerateFiles(FixPath(path), searchPattern, searchOption); public IO> EnumerateFileSystemEntries(string path) => Live.Implementations.DirectoryIO.Default.EnumerateFileSystemEntries(FixPath(path)); public IO> EnumerateFileSystemEntries(string path, string searchPattern) => Live.Implementations.DirectoryIO.Default.EnumerateFileSystemEntries(FixPath(path), searchPattern); public IO> EnumerateFileSystemEntries(string path, string searchPattern, SearchOption searchOption) => Live.Implementations.DirectoryIO.Default.EnumerateFileSystemEntries(FixPath(path), searchPattern, searchOption); public IO GetDirectoryRoot(string path) => Live.Implementations.DirectoryIO.Default.GetDirectoryRoot(FixPath(path)); public IO GetCurrentDirectory() => Live.Implementations.DirectoryIO.Default.GetCurrentDirectory(); public IO SetCurrentDirectory(string path) => Live.Implementations.DirectoryIO.Default.SetCurrentDirectory(FixPath(path)); public IO Move(string sourceDirName, string destDirName) => Live.Implementations.DirectoryIO.Default.Move(FixPath(sourceDirName), FixPath(destDirName)); public IO> GetLogicalDrives() => Live.Implementations.DirectoryIO.Default.GetLogicalDrives(); } ================================================ FILE: LanguageExt.Sys/Test/Implementations/EnvironmentIO.cs ================================================ using System; using LanguageExt.Common; using static LanguageExt.Prelude; namespace LanguageExt.Sys.Test.Implementations; public record EnvironmentIO(MemorySystemEnvironment env) : Sys.Traits.EnvironmentIO { /// /// Gets the command line for this process. /// public IO CommandLine() => lift(() => env.CommandLine); /// /// Gets a unique identifier for the current managed thread. /// public IO CurrentManagedThreadId() => lift(() => env.CurrentManagedThreadId); /// /// Terminates this process and returns an exit code to the operating system. /// public IO Exit(int exitCode) => lift(() => { Environment.Exit(exitCode); return unit; }); /// /// Gets the exit code of the process. /// public IO ExitCode() => lift(() => env.ExitCode); /// /// Sets the exit code of the process. /// // exitCode: exit code of the process public IO SetExitCode(int exitCode) => lift(() => { env.ExitCode = exitCode; return unit; }); /// /// Replaces the name of each environment variable embedded in the specified string with the string equivalent of the value of the variable, then returns the resulting string. /// /// name: A string containing the names of zero or more environment variables. Each environment variable is quoted with the percent sign character (%). public IO ExpandEnvironmentVariables(string name) => lift(() => throw new NotImplementedException()); /// /// Immediately terminates a process after writing a message to the Windows Application event log, and then includes the message in error reporting to Microsoft. /// /// message: A message that explains why the process was terminated, or null if no explanation is provided. public IO FailFast(Option message) => lift(() => { Environment.FailFast(message.IfNone("")); return unit; }); /// /// Immediately terminates a process after writing a message to the Windows Application event log, and then includes the message and exception information in error reporting to Microsoft. /// /// message: A message that explains why the process was terminated, or null if no explanation is provided. /// exception: An exception that represents the error that caused the termination. This is typically the exception in a catch block. public IO FailFast(Option message, Option exception) => lift(() => { Environment.FailFast(message.IfNone(""), exception.IfNone(BottomException.Default)); return unit; }); /// /// Returns a string array containing the command-line arguments for the current process. /// public IO> GetCommandLineArgs() => lift(() => env.CommandLineArgs); /// /// Retrieves the value of an environment variable from the current process. /// /// variable: The name of an environment variable. public IO> GetEnvironmentVariable(string variable) => lift(() => { if (env.ProcessEnvironmentVariables.TryGetValue(variable, out var processEnvironmentVariable)) { return processEnvironmentVariable; } else if (env.UserEnvironmentVariables.TryGetValue(variable, out var userEnvironmentVariable)) { return userEnvironmentVariable; } else if (env.SystemEnvironmentVariables.TryGetValue(variable, out var environmentVariable)) { return environmentVariable; } else { return None; } }); /// /// Retrieves the value of an environment variable from the current process or from the Windows operating system registry key for the current user or local machine. /// /// variable: The name of an environment variable. public IO> GetEnvironmentVariable(string variable, EnvironmentVariableTarget target) => lift(() => { if (target == EnvironmentVariableTarget.Process && env.ProcessEnvironmentVariables.TryGetValue(variable, out var processEnvironmentVariable)) { return processEnvironmentVariable; } else if (target == EnvironmentVariableTarget.User && env.UserEnvironmentVariables.TryGetValue(variable, out var userEnvironmentVariable)) { return userEnvironmentVariable; } else if (target == EnvironmentVariableTarget.Machine && env.SystemEnvironmentVariables.TryGetValue(variable, out var environmentVariable)) { return environmentVariable; } else { return default; } }); /// /// Retrieves all environment variable names and their values from the current process. /// public IO>> GetEnvironmentVariables() => lift(() => env.ProcessEnvironmentVariables.ToHashMap() + env.UserEnvironmentVariables.ToHashMap() + env.SystemEnvironmentVariables.ToHashMap()); /// /// Retrieves all environment variable names and their values from the current process, or from the Windows operating system registry key for the current user or local machine. /// /// target: One of the System.EnvironmentVariableTarget values. Only System.EnvironmentVariableTarget.Process is supported on .NET Core running on Unix-based systems. public IO>> GetEnvironmentVariables(EnvironmentVariableTarget target) => lift(() => target switch { EnvironmentVariableTarget.Process => env.ProcessEnvironmentVariables.ToHashMap(), EnvironmentVariableTarget.User => env.UserEnvironmentVariables.ToHashMap(), EnvironmentVariableTarget.Machine => env.SystemEnvironmentVariables.ToHashMap(), _ => default }); /// /// Gets the path to the system special folder that is identified by the specified enumeration. /// /// folder: One of enumeration values that identifies a system special folder. public IO GetFolderPath(Environment.SpecialFolder folder) => lift(() => env.GetFolderPath(folder, Environment.SpecialFolderOption.None)); /// /// Gets the path to the system special folder that is identified by the specified enumeration, and uses a specified option for accessing special folders. /// /// folder: One of the enumeration values that identifies a system special folder. /// option: One of the enumeration values that specifies options to use for accessing a special folder. public IO GetFolderPath(Environment.SpecialFolder folder, Environment.SpecialFolderOption option) => lift(() => env.GetFolderPath(folder, option)); /// /// Returns an array of string containing the names of the logical drives on the current computer. /// /// string[] Environment.GetLogicalDrives() public IO> GetLogicalDrives() => lift(() => env.LogicalDrives); /// /// Gets a value that indicates whether the current application domain is being unloaded or the common language runtime (CLR) is shutting down. /// public IO HasShutdownStarted() => lift(() => env.HasShutdownStarted); /// /// Determines whether the current operating system is a 64-bit operating system. /// public IO Is64BitOperatingSystem() => lift(() => env.Is64BitOperatingSystem); /// /// Determines whether the current process is a 64-bit process. /// public IO Is64BitProcess() => lift(() => env.Is64BitProcess); /// /// Gets the NetBIOS name of this local computer. /// public IO MachineName() => lift(() => env.MachineName); /// /// Gets the newline string defined for this environment. /// public IO NewLine() => lift(() => env.NewLine); /// /// Gets an OperatingSystem object that contains the current platform identifier and version number. /// public IO OSVersion() => lift(() => env.OSVersion); /// /// Gets the number of processors on the current machine. /// public IO ProcessorCount() => lift(() => env.ProcessorCount); /// /// Creates, modifies, or deletes an environment variable stored in the current process. /// /// variable: The name of an environment variable. /// value: A value to assign to variable . public IO SetEnvironmentVariable(string variable, Option value) => lift(() => { if (value.IsSome) { env.ProcessEnvironmentVariables.AddOrUpdate(variable, (string)value, (_, _) => (string)value); } else { env.ProcessEnvironmentVariables.TryRemove(variable, out _); } return unit; }); /// /// Creates, modifies, or deletes an environment variable stored in the current process or in the Windows operating system registry key reserved for the current user or local machine. /// /// variable: The name of an environment variable. /// value: A value to assign to variable. /// target: One of the enumeration values that specifies the location of the environment variable. public IO SetEnvironmentVariable(string variable, Option value, EnvironmentVariableTarget target) => lift(() => { switch (target) { case EnvironmentVariableTarget.Process: if (value.IsSome) { env.ProcessEnvironmentVariables.AddOrUpdate( variable, (string)value, (_, _) => (string)value); } else { env.ProcessEnvironmentVariables.TryRemove(variable, out var _); } break; case EnvironmentVariableTarget.User: if (value.IsSome) { env.UserEnvironmentVariables.AddOrUpdate(variable, (string)value, (_, _) => (string)value); } else { env.UserEnvironmentVariables.TryRemove(variable, out _); } break; case EnvironmentVariableTarget.Machine: if (value.IsSome) { env.SystemEnvironmentVariables.AddOrUpdate( variable, (string)value, (_, _) => (string)value); } else { env.SystemEnvironmentVariables.TryRemove(variable, out _); } break; } return unit; }); /// /// Gets current stack trace information. /// public IO StackTrace() => lift(() => env.StackTrace); /// /// Gets the fully qualified path of the system directory. /// public IO SystemDirectory() => lift(() => env.SystemDirectory); /// /// Gets the number of bytes in the operating system's memory page. /// public IO SystemPageSize() => lift(() => env.SystemPageSize); /// /// Gets the number of milliseconds elapsed since the system started. /// public IO TickCount() => lift(() => env.TickCount); // NOTE: This seems to be a newer interface, but I'm not sure how to handle it // Gets the number of milliseconds elapsed since the system started. // public IO // Environment.TickCount64); /// /// Gets the network domain name associated with the current user. /// public IO UserDomainName() => lift(() => env.UserDomainName); /// /// Gets a value indicating whether the current process is running in user interactive mode. /// public IO UserInteractive() => lift(() => env.UserInteractive); /// /// Gets the user name of the person who is currently logged on to the operating system. /// public IO UserName() => lift(() => env.UserName); /// /// Gets a Version object that describes the major, minor, build, and revision numbers of the common language runtime. /// public IO Version() => lift(() => env.Version); /// /// Gets the amount of physical memory mapped to the process context. /// public IO WorkingSet() => lift(() => env.WorkingSet); } ================================================ FILE: LanguageExt.Sys/Test/Implementations/FileIO.cs ================================================ using System.IO; using System.Text; using System.Collections.Generic; namespace LanguageExt.Sys.Test.Implementations; /// /// Test world interaction with the file-system /// /// /// Primarily used for testing (for use with TestRuntime or your own testing runtime) /// public record FileIO(string root) : Sys.Traits.FileIO { string FixPath(string path) => Path.Combine(root, path.Replace(":", "_drive")); /// /// Copy file from one place to another /// public IO Copy(string fromPath, string toPath, bool overwrite = false) => Live.Implementations.FileIO.Default.Copy(FixPath(fromPath), FixPath(toPath), overwrite); /// /// Move file from one place to another /// public IO Move(string fromPath, string toPath) => Live.Implementations.FileIO.Default.Move(fromPath, toPath); /// /// Move file from one place to another /// public IO Move(string fromPath, string toPath, bool overwrite) => Live.Implementations.FileIO.Default.Move(fromPath, toPath, overwrite); /// /// Append lines to the end of a file /// public IO AppendAllLines(string path, IEnumerable lines, Encoding encoding) => Live.Implementations.FileIO.Default.AppendAllLines(FixPath(path), lines, encoding); /// /// Read all lines from a file /// public IO> ReadAllLines(string path, Encoding encoding) => Live.Implementations.FileIO.Default.ReadAllLines(FixPath(path), encoding); /// /// Read all lines from a file /// public IO ReadAllBytes(string path) => Live.Implementations.FileIO.Default.ReadAllBytes(FixPath(path)); /// /// Write all lines to a file /// public IO WriteAllLines(string path, IEnumerable lines, Encoding encoding) => Live.Implementations.FileIO.Default.WriteAllLines(FixPath(path), lines, encoding); /// /// Write all lines to a file /// public IO WriteAllBytes(string path, byte[] data) => Live.Implementations.FileIO.Default.WriteAllBytes(FixPath(path), data); /// /// Read text from a file /// public IO ReadAllText(string path, Encoding encoding) => Live.Implementations.FileIO.Default.ReadAllText(FixPath(path), encoding); /// /// Write text to a file /// public IO WriteAllText(string path, string text, Encoding encoding) => Live.Implementations.FileIO.Default.WriteAllText(FixPath(path), text, encoding); /// /// Delete a file /// public IO Delete(string path) => Live.Implementations.FileIO.Default.Delete(FixPath(path)); /// /// True if a file at the path exists /// public IO Exists(string path) => Live.Implementations.FileIO.Default.Exists(FixPath(path)); /// /// Open a text file /// public IO OpenText(string path) => Live.Implementations.FileIO.Default.OpenText(FixPath(path)); /// /// Create a new text file to stream to /// public IO CreateText(string path) => Live.Implementations.FileIO.Default.CreateText(FixPath(path)); /// /// Return a stream to append text to /// public IO AppendText(string path) => Live.Implementations.FileIO.Default.AppendText(FixPath(path)); /// /// Open a file-stream /// public IO OpenRead(string path) => Live.Implementations.FileIO.Default.OpenRead(FixPath(path)); /// /// Open a file-stream /// public IO Open(string path, FileMode mode) => Live.Implementations.FileIO.Default.Open(FixPath(path), mode); /// /// Open a file-stream /// public IO Open(string path, FileMode mode, FileAccess access) => Live.Implementations.FileIO.Default.Open(FixPath(path), mode, access); /// /// Open a file-stream /// public IO OpenWrite(string path) => Live.Implementations.FileIO.Default.OpenWrite(FixPath(path)); } ================================================ FILE: LanguageExt.Sys/Test/Implementations/TestTimeSpec.cs ================================================ using System; namespace LanguageExt.Sys.Test.Implementations; public record TestTimeSpec(Schedule Schedule, DateTime Start) { /// /// Time never passes, it has a constant value of `start` /// public static TestTimeSpec FixedFromSpecified(DateTime start) => new (Schedule.Forever, start); /// /// Time never passes, it has a constant value of `DateTime.Now` (as it was set when you call this method) /// public static TestTimeSpec FixedFromNow(DateTime start) => FixedFromSpecified(DateTime.UtcNow); /// /// Time passes at 1 millisecond per tick starting from `start` /// public static TestTimeSpec RunningFromSpecified(DateTime start) => new (Schedule.spaced(1), start); /// /// Time passes at 1 millisecond per tick starting from `DateTime.Now` /// public static TestTimeSpec RunningFromNow() => RunningFromSpecified(DateTime.UtcNow); } ================================================ FILE: LanguageExt.Sys/Test/Implementations/TextReadIO.cs ================================================ using System; using System.IO; namespace LanguageExt.Sys.Test.Implementations; public struct TextReadIO : Sys.Traits.TextReadIO { public static Sys.Traits.TextReadIO Default = new TextReadIO(); /// /// Read a line of text from the stream /// public IO> ReadLine(TextReader reader) => Live.Implementations.TextReadIO.Default.ReadLine(reader); /// /// Read the rest of the text in the stream /// public IO ReadToEnd(TextReader reader) => Live.Implementations.TextReadIO.Default.ReadToEnd(reader); /// /// Read chars from the stream into the buffer /// Returns the number of chars read /// public IO Read(TextReader reader, Memory buffer) => Live.Implementations.TextReadIO.Default.Read(reader, buffer); /// /// Close the reader /// public IO Close(TextReader reader) => Live.Implementations.TextReadIO.Default.Close(reader); } ================================================ FILE: LanguageExt.Sys/Test/Implementations/TimeIO.cs ================================================ using System; using System.Collections.Generic; using static LanguageExt.Prelude; namespace LanguageExt.Sys.Test.Implementations; public class TimeIO : Sys.Traits.TimeIO, IDisposable { readonly Atom now; readonly IEnumerator ticks; public TimeIO(TestTimeSpec spec) { now = Atom(spec.Start); ticks = spec.Schedule.Run().GetEnumerator(); } public void Dispose() => ticks.Dispose(); void Tick() => now.Swap(n => { if (!ticks.MoveNext()) throw new TimeoutException("We've reached the heat death of the universe"); return n.AddMilliseconds(ticks.Current); }); /// /// Current UTC date time /// public IO UtcNow => lift(() => { Tick(); return now.Value; }); /// /// Current local date time /// public IO Now => UtcNow.Map(t => t.ToLocalTime()); /// /// Today's date /// public IO Today => Now.Map(t => t.Date); /// /// Pause a task until a specified time /// public IO SleepUntil(DateTime dt) => Live.Implementations.TimeIO.Default.SleepUntil(dt); /// /// Pause a task until for a specified length of time /// public IO SleepFor(TimeSpan ts) => Live.Implementations.TimeIO.Default.SleepFor(ts); } ================================================ FILE: LanguageExt.Sys/Test/Runtime.cs ================================================ using System; using System.IO; using System.Text; using LanguageExt.Sys.Traits; using LanguageExt.Traits; namespace LanguageExt.Sys.Test; /// /// Test IO runtime /// public record Runtime(RuntimeEnv Env) : Local, ActivityEnv>, Has, ActivitySourceIO>, Has, ConsoleIO>, Has, EncodingIO>, Has, FileIO>, Has, TextReadIO>, Has, TimeIO>, Has, EnvironmentIO>, Has, DirectoryIO>, IDisposable { /// /// Constructor function /// public static Runtime New() { var tmp = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); Directory.CreateDirectory(tmp); return new(RuntimeEnv.New(tmp)); } /// /// Constructor function /// /// Data environment for the runtime. Call `RuntimeEnv.Default with { ... }` to modify public static Runtime New(RuntimeEnv env) => new(env); static K, A> asks(Func f) => Readable.asks, Runtime, A>(f); static K, A> local(Func f, K, A> ma) => Readable.local(f, ma); static K, A> localEnv(Func f, K, A> ma) => local(e => e with { Env = f(e.Env) }, ma); static K, A> localActivity(Func f, K, A> ma) => local(e => e with { Env = e.Env with { Activity = f(e.Env.Activity) } }, ma); /// /// Access the console environment /// /// Console environment static K, ConsoleIO> Has, ConsoleIO>.Ask => asks(rt => new Implementations.ConsoleIO(rt.Env.Console)); /// /// Access the file environment /// /// File environment static K, FileIO> Has, FileIO>.Ask => asks(rt => new Implementations.FileIO(rt.Env.RootPath)); /// /// Access the TextReader environment /// /// TextReader environment static K, TextReadIO> Has, TextReadIO>.Ask => asks(_ => Implementations.TextReadIO.Default); /// /// Access the time environment /// /// Time environment static K, TimeIO> Has, TimeIO>.Ask => asks(rt => new Implementations.TimeIO(rt.Env.TimeSpec)); /// /// Access the operating-system environment /// /// Operating-system environment environment static K, EnvironmentIO> Has, EnvironmentIO>.Ask => asks(rt => new Implementations.EnvironmentIO(rt.Env.SysEnv)); /// /// Access the directory environment /// /// Directory environment static K, DirectoryIO> Has, DirectoryIO>.Ask => asks(rt => new Implementations.DirectoryIO(rt.Env.RootPath)); static K, EncodingIO> Has, EncodingIO>.Ask => asks(_ => Live.Implementations.EncodingIO.Default); static K, ActivitySourceIO> Has, ActivitySourceIO>.Ask => asks(rt => new Live.Implementations.ActivitySourceIO(rt.Env.Activity)); /// /// Run with a local ActivityEnv /// static K, A> Local, ActivityEnv>.With(Func f, K, A> ma) => localActivity(f, ma); /// /// Read the current ActivityEnv /// static K, ActivityEnv> Has, ActivityEnv>.Ask => asks(rt => rt.Env.Activity); public override string ToString() => "Test Runtime"; public void Dispose() => Directory.Delete(Env.RootPath, recursive: true); } public record RuntimeEnv( EnvIO EnvIO, Encoding Encoding, MemoryConsole Console, string RootPath, Implementations.TestTimeSpec TimeSpec, MemorySystemEnvironment SysEnv, ActivityEnv Activity) { public RuntimeEnv LocalCancel => this with { EnvIO = EnvIO.LocalCancel }; public static RuntimeEnv New(string rootPath) => new(EnvIO.New(), Encoding.Default, new MemoryConsole(), rootPath, Implementations.TestTimeSpec.RunningFromNow(), MemorySystemEnvironment.InitFromSystem(), ActivityEnv.Default); public override string ToString() => "Runtime Environment"; } ================================================ FILE: LanguageExt.Sys/Traits/ActivitySourceIO.cs ================================================ using System; using System.Diagnostics; namespace LanguageExt.Sys.Traits; public interface ActivitySourceIO { /// /// Creates a new activity if there are active listeners for it, using the specified name and activity kind. /// /// The operation name of the activity. /// The activity kind. /// The created activity object, if it had active listeners, or `None` if it has no event /// listeners. IO StartActivity(string name, ActivityKind kind); /// /// Creates a new activity if there are active listeners for it, using the specified name, activity kind, parent /// activity context, tags, optional activity link and optional start time. /// /// The operation name of the activity. /// The activity kind. /// The parent `ActivityContext` object to initialize the created activity object /// with /// The optional tags list to initialise the created activity object with. /// The optional `ActivityLink` list to initialise the created activity object with. /// The optional start timestamp to set on the created activity object. /// The created activity object, if it had active listeners, or null if it has no event listeners. IO StartActivity( string name, ActivityKind kind, ActivityContext parentContext, HashMap tags = default, Seq links = default, DateTimeOffset startTime = default); /// /// Creates a new activity if there are active listeners for it, using the specified name, activity kind, parent /// activity context, tags, optional activity link and optional start time. /// /// The operation name of the activity. /// The activity kind. /// The parent Id to initialize the created activity object with. /// The optional tags list to initialise the created activity object with. /// The optional `ActivityLink` list to initialise the created activity object with. /// The optional start timestamp to set on the created activity object. /// The created activity object, if it had active listeners, or null if it has no event listeners. IO StartActivity( string name, ActivityKind kind, string parentId, HashMap tags = default, Seq links = default, DateTimeOffset startTime = default); } ================================================ FILE: LanguageExt.Sys/Traits/ConsoleIO.cs ================================================ using System; namespace LanguageExt.Sys.Traits; public interface ConsoleIO { IO Clear(); IO> ReadKey(); IO> Read(); IO> ReadLine(); IO WriteLine(); IO WriteLine(string value); IO Write(string value); IO SetBgColor(ConsoleColor color); IO SetColor(ConsoleColor color); /// /// Sets the foreground and background console colors to their defaults. /// IO ResetColor(); IO BgColor { get; } IO Color { get; } } ================================================ FILE: LanguageExt.Sys/Traits/DirectoryIO.cs ================================================ using System; using System.IO; namespace LanguageExt.Sys.Traits; public interface DirectoryIO { /// /// Create a directory /// IO Create(string path); /// /// Delete a directory /// IO Delete(string path, bool recursive = true); /// /// Get parent directory /// IO> GetParent(string path); /// /// Check if directory exists /// IO Exists(string path); /// /// Set the directory creation time /// IO SetCreationTime(string path, DateTime creationTime); /// /// Set the directory creation time /// IO SetCreationTimeUtc(string path, DateTime creationTimeUtc); /// /// Get the directory creation time /// IO GetCreationTime(string path); /// /// Get the directory creation time /// IO GetCreationTimeUtc(string path); /// /// Set the directory last write time /// IO SetLastWriteTime(string path, DateTime lastWriteTime); /// /// Set the directory last write time /// IO SetLastWriteTimeUtc(string path, DateTime lastWriteTimeUtc); /// /// Get the directory last write time /// IO GetLastWriteTime(string path); /// /// Get the directory last write time /// IO GetLastWriteTimeUtc(string path); /// /// Set the directory last access time /// IO SetLastAccessTime(string path, DateTime lastAccessTime); /// /// Set the directory last access time /// IO SetLastAccessTimeUtc(string path, DateTime lastAccessTimeUtc); /// /// Get the directory last access time /// IO GetLastAccessTime(string path); /// /// Get the directory last access time /// IO GetLastAccessTimeUtc(string path); /// /// Enumerate directories /// IO> EnumerateDirectories(string path); /// /// Enumerate directories /// IO> EnumerateDirectories(string path, string searchPattern); /// /// Enumerate directories /// IO> EnumerateDirectories(string path, string searchPattern, SearchOption searchOption); /// /// Enumerate files /// IO> EnumerateFiles(string path); /// /// Enumerate files /// IO> EnumerateFiles(string path, string searchPattern); /// /// Enumerate files /// IO> EnumerateFiles(string path, string searchPattern, SearchOption searchOption); /// /// Enumerate file system entries /// IO> EnumerateFileSystemEntries(string path); /// /// Enumerate file system entries /// IO> EnumerateFileSystemEntries(string path, string searchPattern); /// /// Enumerate file system entries /// IO> EnumerateFileSystemEntries(string path, string searchPattern, SearchOption searchOption); /// /// Get the root of the path provided /// IO GetDirectoryRoot(string path); /// /// Get the current directory /// IO GetCurrentDirectory(); /// /// Set the current directory /// /// IO SetCurrentDirectory(string path); /// /// Move a directory /// IO Move(string sourceDirName, string destDirName); /// /// Get the logical drives /// IO> GetLogicalDrives(); } ================================================ FILE: LanguageExt.Sys/Traits/EncodingIO.cs ================================================ using System.Text; namespace LanguageExt.Sys.Traits; public interface EncodingIO { IO Encoding { get; } } ================================================ FILE: LanguageExt.Sys/Traits/EnvironmentIO.cs ================================================ using System; namespace LanguageExt.Sys.Traits; public interface EnvironmentIO { IO CommandLine(); IO CurrentManagedThreadId(); IO Exit(int exitCode); IO ExitCode(); IO SetExitCode(int exitCode); IO ExpandEnvironmentVariables(string name); IO FailFast(Option message); IO FailFast(Option message, Option exception); IO> GetCommandLineArgs(); IO> GetEnvironmentVariable(string variable); IO> GetEnvironmentVariable(string variable, EnvironmentVariableTarget target); IO>> GetEnvironmentVariables(); IO>> GetEnvironmentVariables(EnvironmentVariableTarget target); IO GetFolderPath(Environment.SpecialFolder folder); IO GetFolderPath(Environment.SpecialFolder folder, Environment.SpecialFolderOption option); IO> GetLogicalDrives(); IO HasShutdownStarted(); IO Is64BitOperatingSystem(); IO Is64BitProcess(); IO MachineName(); IO NewLine(); IO OSVersion(); IO ProcessorCount(); IO SetEnvironmentVariable(string variable, Option value); IO SetEnvironmentVariable(string variable, Option value, EnvironmentVariableTarget target); IO StackTrace(); IO SystemDirectory(); IO SystemPageSize(); IO TickCount(); IO UserDomainName(); IO UserInteractive(); IO UserName(); IO Version(); IO WorkingSet(); } ================================================ FILE: LanguageExt.Sys/Traits/FileIO.cs ================================================ using System.Collections.Generic; using System.IO; using System.Text; namespace LanguageExt.Sys.Traits; public interface FileIO { /// /// Copy file from one place to another /// IO Copy(string fromPath, string toPath, bool overwrite = false); /// /// Move file from one place to another /// IO Move(string fromPath, string toPath); /// /// Move file from one place to another /// IO Move(string fromPath, string toPath, bool overwrite); /// /// Append lines to the end of a file /// IO AppendAllLines(string path, IEnumerable lines, Encoding encoding); /// /// Read all lines from a file /// IO> ReadAllLines(string path, Encoding encoding); /// /// Read text from a file /// IO ReadAllText(string path, Encoding encoding); /// /// Read text from a file /// IO ReadAllBytes(string path); /// /// Write text to a file /// IO WriteAllText(string path, string lines, Encoding encoding); /// /// Write all lines to a file /// IO WriteAllLines(string path, IEnumerable lines, Encoding encoding); /// /// Write text to a file /// IO WriteAllBytes(string path, byte[] data); /// /// Delete a file /// IO Delete(string path); /// /// True if a file at the path exists /// IO Exists(string path); /// /// Open a text file /// IO OpenText(string path); /// /// Create a new text file to stream to /// IO CreateText(string path); /// /// Return a stream to append text to /// IO AppendText(string path); /// /// Open a file-stream /// IO OpenRead(string path); /// /// Open a file-stream /// IO Open(string path, FileMode mode); /// /// Open a file-stream /// IO Open(string path, FileMode mode, FileAccess access); /// /// Open a file-stream /// IO OpenWrite(string path); } ================================================ FILE: LanguageExt.Sys/Traits/SysIO.cs ================================================ using LanguageExt.Traits; namespace LanguageExt.Sys.Traits; /// /// Convenience trait - captures the BCL IO behaviour /// /// Monad and reader trait public interface HasSys : Has, Has, Has, Has, Has, Has; ================================================ FILE: LanguageExt.Sys/Traits/TextReadIO.cs ================================================ using System; using System.IO; namespace LanguageExt.Sys.Traits; public interface TextReadIO { /// /// Read a line of text from the stream /// IO> ReadLine(TextReader reader); /// /// Read the rest of the text in the stream /// IO ReadToEnd(TextReader reader); /// /// Read chars from the stream into the buffer /// Returns the number of chars read /// IO Read(TextReader reader, Memory buffer); /// /// Close the reader /// IO Close(TextReader reader); } ================================================ FILE: LanguageExt.Sys/Traits/TimeIO.cs ================================================ using System; namespace LanguageExt.Sys.Traits; public interface TimeIO { /// /// Current local date time /// IO Now { get; } /// /// Current universal date time /// IO UtcNow { get; } /// /// Today's date /// IO Today { get; } /// /// Pause a task until a specified time /// IO SleepUntil(DateTime dt); /// /// Pause a task until for a specified length of time /// IO SleepFor(TimeSpan ts); } ================================================ FILE: LanguageExt.Tests/AffTests.cs ================================================ using Xunit; using System; using LanguageExt; using LanguageExt.Common; using System.Threading.Tasks; using static LanguageExt.Prelude; namespace EffTests; public class EffMapFailTests { [Fact] // Fails public void Eff_WithFailingTaskInEff_EndsWithMapFailValue() { // Arrange var affWithException = liftEff(async () => await FailingTask()); var affWithMappedFailState = affWithException.MapFail(error => Error.New(error.Message + "MapFail Eff")); // Act var fin = affWithMappedFailState.Run(); var result = fin.Match(_ => "Success!", error => error.Message); // Assert Assert.True(result.IndexOf("MapFail Eff", StringComparison.Ordinal) > 0); } [Fact] // Fails public void Eff_WithExceptionInEff_EndsWithMapFailValue() { // Arrange var affWithException = lift(() => throw new Exception()).ToEff(); var affWithMappedFailState = affWithException.MapFail(error => Error.New(error.Message + "MapFail Eff")); // Act var fin = affWithMappedFailState.Run(); var result = fin.Match(_ => "Success!", error => error.Message); // Assert Assert.True(result.IndexOf("MapFail Eff", StringComparison.Ordinal) > 0); } [Fact] // Fails public void Eff_FailingTaskToEff_EndsWithMapFailValue() { // Arrange var affWithException = liftEff(async () => await FailingTask()); var affWithMappedFailState = affWithException.MapFail(error => Error.New(error.Message + "MapFail Eff")); // Act var fin = affWithMappedFailState.Run(); var result = fin.Match(_ => "Success!", error => error.Message); // Assert Assert.True(result.IndexOf("MapFail Eff", StringComparison.Ordinal) > 0); } [Fact] // Succeeds public void EffToEff_WithExceptionInEff_EndsWithMapFailValue() { // Arrange var effWithException = lift(() => throw new Exception("Error!")).ToEff(); var effWithMappedFailState = effWithException.MapFail(error => Error.New(error.Message + "MapFail Eff")); // Act var fin = effWithMappedFailState.Run(); var result = fin.Match(_ => "Success!", error => error.Message); // Assert Assert.True(result.IndexOf("MapFail Eff", StringComparison.Ordinal) > 0); } static async Task FailingTask() { await Task.Delay(1); throw new Exception("Error!"); } } ================================================ FILE: LanguageExt.Tests/ArrayTests.cs ================================================ using Xunit; using System; using System.Linq; using static LanguageExt.List; using static LanguageExt.Prelude; namespace LanguageExt.Tests; public class ArrayTests { [Fact] public void ConsTest1() { var test = 1.Cons(2.Cons(3.Cons(4.Cons(empty())))); var array = test.ToArray(); Assert.True(array[0] == 1); Assert.True(array[1] == 2); Assert.True(array[2] == 3); Assert.True(array[3] == 4); } [Fact] public void ListConstruct() { var test = Array(1, 2, 3, 4, 5); var array = test.ToArray(); Assert.True(array[0] == 1); Assert.True(array[1] == 2); Assert.True(array[2] == 3); Assert.True(array[3] == 4); Assert.True(array[4] == 5); } [Fact] public void MapTestFluent() { var res = Array(1, 2, 3, 4, 5) .Map(x => x * 10) .Filter(x => x > 20) .Fold(0, (x, s) => s + x); Assert.True(res == 120); } [Fact] public void ReduceTestFluent() { var res = Array(1, 2, 3, 4, 5) .Map(x => x * 10) .Filter(x => x > 20) .Reduce((x, s) => s + x); Assert.True(res == 120); } [Fact] public void ReverseListTest1() { var list = Array(1, 2, 3, 4, 5); var rev = list.Reverse(); Assert.True(rev[0] == 5); Assert.True(rev[4] == 1); } [Fact] public void ReverseListTest2() { var list = Array(1, 2, 3, 4, 5); var rev = list.Reverse(); Assert.True(rev.IndexOf(1) == 4, "Should have been 4, actually is: " + rev.IndexOf(1)); Assert.True(rev.IndexOf(5) == 0, "Should have been 0, actually is: " + rev.IndexOf(5)); } [Fact] public void ReverseListTest3() { var list = Array(1, 1, 2, 2, 2); var rev = list.Reverse(); Assert.True(rev.LastIndexOf(1) == 4, "Should have been 4, actually is: " + rev.LastIndexOf(1)); Assert.True(rev.LastIndexOf(2) == 2, "Should have been 2, actually is: " + rev.LastIndexOf(5)); } [Fact] public void OpEqualTest() { var goodOnes = Array( (List(1, 2, 3), List(1, 2, 3)), (Lst.Empty, Lst.Empty) ); var badOnes = Array( (List(1, 2, 3), List(1, 2, 4)), (List(1, 2, 3), Lst.Empty) ); goodOnes.Iter(t => t.Iter((fst, snd) => { Assert.True(fst == snd, $"'{fst}' == '{snd}'"); Assert.False(fst != snd, $"'{fst}' != '{snd}'"); })); badOnes.Iter(t => t.Iter((fst, snd) => { Assert.True(fst != snd, $"'{fst}' != '{snd}'"); Assert.False(fst == snd, $"'{fst}' == '{snd}'"); })); } [Fact] public void ArrShouldNotStackOverflowOnEquals() { var arr = default(Arr>); Assert.True(arr.Equals(arr)); } [Fact] public void EqualsTest() { Assert.False(Array(1, 2, 3).Equals(Array())); Assert.False(Array().Equals(Array(1, 2, 3))); Assert.True(Array().Equals(Array())); Assert.True(Array(1).Equals(Array(1))); Assert.True(Array(1, 2).Equals(Array(1, 2))); Assert.False(Array(1, 2).Equals(Array(1, 2, 3))); Assert.False(Array(1, 2, 3).Equals(Array(1, 2))); } [Fact] public void itemLensGetShouldGetExistingValue() { var expected = "3"; var array = Array("0", "1", "2", "3", "4", "5"); var actual = Arr.item(3).Get(array); Assert.Equal(expected, actual); } [Fact] public void itemLensGetShouldThrowExceptionForNonExistingValue() { Assert.Throws(() => { var array = Array("0", "1", "2", "3", "4", "5"); var actual = Arr.item(10).Get(array); }); } [Fact] public void itemOrNoneLensGetShouldGetExistingValue() { var expected = "3"; var array = Array("0", "1", "2", "3", "4", "5"); var actual = Arr.itemOrNone(3).Get(array); Assert.Equal(expected, actual); } [Fact] public void itemOrNoneLensGetShouldReturnNoneForNonExistingValue() { var expected = Option.None; var array = Array("0", "1", "2", "3", "4", "5"); var actual = Arr.itemOrNone(10).Get(array); Assert.Equal(expected, actual); } } ================================================ FILE: LanguageExt.Tests/AtomHashMapEqTests.cs ================================================ using static LanguageExt.Prelude; using Xunit; using LanguageExt.ClassInstances; namespace LanguageExt.Tests; public class AtomHashMapEqTests { [Fact] public void SwapInvokesChange() { var hashMap = AtomHashMap( ("foo", 3), ("bar", 42)); var initialValue = hashMap.ToHashMap(); HashMapPatch state = default; hashMap.Change += v => state = v; hashMap.Swap(m => m.Add("biz", 99)); Assert.Equal(initialValue, state.From); Assert.Equal(hashMap.ToHashMap(), state.To); Assert.True(HashMap>(("biz", Change.Added(99))) == state.Changes); } [Fact] public void SwapKeyInvokesChange() { var hashMap = AtomHashMap( ("foo", 3), ("bar", 42)); var initialValue = hashMap.ToHashMap(); HashMapPatch state = default; hashMap.Change += v => state = v; hashMap.SwapKey("foo", i => i + 1); Assert.Equal(initialValue, state.From); Assert.Equal(hashMap.ToHashMap(), state.To); Assert.Equal( HashMap>(("foo", Change.Mapped(3, 4))), state.Changes); } [Fact] public void SwapKeyOptionalInvokesChange() { var hashMap = AtomHashMap( ("foo", 3), ("bar", 42)); var initialValue = hashMap.ToHashMap(); HashMapPatch state = default; hashMap.Change += v => state = v; hashMap.SwapKey("foo", i => None); Assert.Equal(initialValue, state.From); Assert.Equal(hashMap.ToHashMap(), state.To); Assert.Equal( HashMap>(("foo", Change.Removed(3))), state.Changes); } [Fact] public void FilterInPlaceInvokesChange() { var hashMap = AtomHashMap( ("foo", 3), ("bar", 42), ("biz", 7)); var initialValue = hashMap.ToHashMap(); HashMapPatch state = default; hashMap.Change += v => state = v; hashMap.FilterInPlace(i => i % 2 == 0); Assert.Equal(initialValue, state.From); Assert.Equal(hashMap.ToHashMap(), state.To); Assert.Equal( HashMap>( ("foo", Change.Removed(3)), ("biz", Change.Removed(7))), state.Changes); } [Fact] public void FilterInPlaceWithKeyInvokesChange() { var hashMap = AtomHashMap( ("foo", 3), ("bar", 42), ("biz", 7)); var initialValue = hashMap.ToHashMap(); HashMapPatch state = default; hashMap.Change += v => state = v; hashMap.FilterInPlace((k, i) => k[0] == 'b' && i % 2 == 0); Assert.Equal(initialValue, state.From); Assert.Equal(hashMap.ToHashMap(), state.To); Assert.Equal( HashMap>( ("foo", Change.Removed(3)), ("biz", Change.Removed(7))), state.Changes); } [Fact] public void MapInPlaceInvokesChange() { var hashMap = AtomHashMap( ("foo", 3), ("bar", 42), ("biz", 7)); var initialValue = hashMap.ToHashMap(); HashMapPatch state = default; hashMap.Change += v => state = v; hashMap.MapInPlace(i => i * 3); Assert.Equal(initialValue, state.From); Assert.Equal(hashMap.ToHashMap(), state.To); Assert.Equal( HashMap>( ("foo", Change.Mapped(3, 9)), ("bar", Change.Mapped(42, 126)), ("biz", Change.Mapped(7, 21))), state.Changes); } [Fact] public void AddInvokesChange() { var hashMap = AtomHashMap( ("foo", 3), ("bar", 42)); var initialValue = hashMap.ToHashMap(); HashMapPatch state = default; hashMap.Change += v => state = v; hashMap.Add("biz", 7); Assert.Equal(initialValue, state.From); Assert.Equal(hashMap.ToHashMap(), state.To); Assert.Equal( HashMap>( ("biz", Change.Added(7))), state.Changes); } [Fact] public void TryAddInvokesChange() { var hashMap = AtomHashMap( ("foo", 3), ("bar", 42)); var initialValue = hashMap.ToHashMap(); HashMapPatch state = default; hashMap.Change += v => state = v; hashMap.TryAdd("biz", 7); Assert.Equal(initialValue, state.From); Assert.Equal(hashMap.ToHashMap(), state.To); Assert.Equal(HashMap>(("biz", Change.Added(7))), state.Changes); } [Fact] public void AddOrUpdateInvokesChange() { var hashMap = AtomHashMap( ("foo", 3), ("bar", 42)); var initialValue = hashMap.ToHashMap(); HashMapPatch state = default; hashMap.Change += v => state = v; hashMap.AddOrUpdate("biz", 7); Assert.Equal(initialValue, state.From); Assert.Equal(hashMap.ToHashMap(), state.To); Assert.Equal( HashMap>( ("biz", Change.Added(7))), state.Changes); } [Fact] public void AddRangeInvokesChange() { var hashMap = AtomHashMap( ("foo", 3), ("bar", 42)); var initialValue = hashMap.ToHashMap(); HashMapPatch state = default; hashMap.Change += v => state = v; hashMap.AddRange(Seq(("biz", 7), ("baz", 9))); Assert.Equal(initialValue, state.From); Assert.Equal(hashMap.ToHashMap(), state.To); Assert.Equal( HashMap>( ("biz", Change.Added(7)), ("baz", Change.Added(9))), state.Changes); } [Fact] public void TryAddRangeInvokesChange() { var hashMap = AtomHashMap( ("foo", 3), ("bar", 42)); var initialValue = hashMap.ToHashMap(); HashMapPatch state = default; hashMap.Change += v => state = v; hashMap.TryAddRange(Seq(("biz", 7), ("baz", 9))); Assert.Equal(initialValue, state.From); Assert.Equal(hashMap.ToHashMap(), state.To); Assert.Equal( HashMap>( ("biz", Change.Added(7)), ("baz", Change.Added(9))), state.Changes); } [Fact] public void AddOrUpdateRangeInvokesChange() { var hashMap = AtomHashMap( ("foo", 3), ("bar", 42)); var initialValue = hashMap.ToHashMap(); HashMapPatch state = default; hashMap.Change += v => state = v; hashMap.AddOrUpdateRange(Seq(("biz", 7), ("baz", 9))); Assert.Equal(initialValue, state.From); Assert.Equal(hashMap.ToHashMap(), state.To); Assert.Equal( HashMap>( ("biz", Change.Added(7)), ("baz", Change.Added(9))), state.Changes); } [Fact] public void RemoveInvokesChange() { var hashMap = AtomHashMap( ("foo", 3), ("bar", 42)); var initialValue = hashMap.ToHashMap(); HashMapPatch state = default; hashMap.Change += v => state = v; hashMap.Remove("bar"); Assert.Equal(initialValue, state.From); Assert.Equal(hashMap.ToHashMap(), state.To); Assert.Equal( HashMap>( ("bar", Change.Removed(42))), state.Changes); } [Fact] public void RemoveRangeInvokesChange() { var hashMap = AtomHashMap( ("foo", 3), ("bar", 42), ("biz", 7)); var initialValue = hashMap.ToHashMap(); HashMapPatch state = default; hashMap.Change += v => state = v; hashMap.RemoveRange(Seq("bar", "biz")); Assert.Equal(initialValue, state.From); Assert.Equal(hashMap.ToHashMap(), state.To); Assert.Equal( HashMap>( ("bar", Change.Removed(42)), ("biz", Change.Removed(7))), state.Changes); } [Fact] public void FindOrAddInvokesChange() { var hashMap = AtomHashMap( ("foo", 3), ("bar", 42)); var initialValue = hashMap.ToHashMap(); HashMapPatch state = default; hashMap.Change += v => state = v; hashMap.FindOrAdd("biz", () => 7); Assert.Equal(initialValue, state.From); Assert.Equal(hashMap.ToHashMap(), state.To); Assert.Equal( HashMap>( ("biz", Change.Added(7))), state.Changes); } [Fact] public void FindOrAddConstantInvokesChange() { var hashMap = AtomHashMap( ("foo", 3), ("bar", 42)); var initialValue = hashMap.ToHashMap(); HashMapPatch state = default; hashMap.Change += v => state = v; hashMap.FindOrAdd("biz", 7); Assert.Equal(initialValue, state.From); Assert.Equal(hashMap.ToHashMap(), state.To); Assert.Equal( HashMap>( ("biz", Change.Added(7))), state.Changes); } [Fact] public void FindOrMaybeAddInvokesChange() { var hashMap = AtomHashMap( ("foo", 3), ("bar", 42)); var initialValue = hashMap.ToHashMap(); HashMapPatch state = default; hashMap.Change += v => state = v; hashMap.FindOrMaybeAdd("biz", () => Some(7)); Assert.Equal(initialValue, state.From); Assert.Equal(hashMap.ToHashMap(), state.To); Assert.Equal( HashMap>( ("biz", Change.Added(7))), state.Changes); } [Fact] public void FindOrMaybeAddConstantInvokesChange() { var hashMap = AtomHashMap( ("foo", 3), ("bar", 42)); var initialValue = hashMap.ToHashMap(); HashMapPatch state = default; hashMap.Change += v => state = v; hashMap.FindOrMaybeAdd("biz", Some(7)); Assert.Equal(initialValue, state.From); Assert.Equal(hashMap.ToHashMap(), state.To); Assert.Equal( HashMap>( ("biz", Change.Added(7))), state.Changes); } [Fact] public void SetItemsInvokesChange() { var hashMap = AtomHashMap( ("foo", 3), ("bar", 42)); var initialValue = hashMap.ToHashMap(); HashMapPatch state = default; hashMap.Change += v => state = v; hashMap.SetItems(Seq(("foo", 80), ("bar", 17))); Assert.Equal(initialValue, state.From); Assert.Equal(hashMap.ToHashMap(), state.To); Assert.Equal( HashMap>( ("foo", Change.Mapped(3, 80)), ("bar", Change.Mapped(42, 17))), state.Changes); } [Fact] public void TrySetItemsInvokesChange() { var hashMap = AtomHashMap( ("foo", 3), ("bar", 42)); var initialValue = hashMap.ToHashMap(); HashMapPatch state = default; hashMap.Change += v => state = v; hashMap.TrySetItems(Seq(("foo", 80), ("bar", 17), ("biz", 33))); Assert.Equal(initialValue, state.From); Assert.Equal(hashMap.ToHashMap(), state.To); Assert.Equal( HashMap>( ("foo", Change.Mapped(3, 80)), ("bar", Change.Mapped(42, 17))), state.Changes); } [Fact] public void SetItemFuncInvokesChange() { var hashMap = AtomHashMap( ("foo", 3), ("bar", 42)); var initialValue = hashMap.ToHashMap(); HashMapPatch state = default; hashMap.Change += v => state = v; hashMap.SetItem("foo", i => i * 2); Assert.Equal(initialValue, state.From); Assert.Equal(hashMap.ToHashMap(), state.To); Assert.Equal( HashMap>( ("foo", Change.Mapped(3, 6))), state.Changes); } [Fact] public void TrySetItemInvokesChange() { var hashMap = AtomHashMap( ("foo", 3), ("bar", 42)); var initialValue = hashMap.ToHashMap(); HashMapPatch state = default; hashMap.Change += v => state = v; hashMap.TrySetItem("foo", 80); Assert.Equal(initialValue, state.From); Assert.Equal(hashMap.ToHashMap(), state.To); Assert.Equal( HashMap>( ("foo", Change.Mapped(3, 80))), state.Changes); } [Fact] public void TrySetItemFuncInvokesChange() { var hashMap = AtomHashMap( ("foo", 3), ("bar", 42)); var initialValue = hashMap.ToHashMap(); HashMapPatch state = default; hashMap.Change += v => state = v; hashMap.TrySetItem("foo", i => i * 2); Assert.Equal(initialValue, state.From); Assert.Equal(hashMap.ToHashMap(), state.To); Assert.Equal( HashMap>( ("foo", Change.Mapped(3, 6))), state.Changes); } [Fact] public void ClearInvokesChange() { var hashMap = AtomHashMap( ("foo", 3), ("bar", 42)); var initialValue = hashMap.ToHashMap(); HashMapPatch state = default; hashMap.Change += v => state = v; hashMap.Clear(); Assert.Equal(initialValue, state.From); Assert.Equal(hashMap.ToHashMap(), state.To); Assert.Equal( HashMap>( ("foo", Change.Removed(3)), ("bar", Change.Removed(42))), state.Changes); } [Fact] public void AppendInvokesChange() { var hashMap = AtomHashMap( ("foo", 3), ("bar", 42)); var toAppend = HashMap(("foo", 7), ("biz", 7), ("baz", 9)); var initialValue = hashMap.ToHashMap(); HashMapPatch state = default; hashMap.Change += v => state = v; hashMap.Append(toAppend); Assert.Equal(initialValue, state.From); Assert.Equal(hashMap.ToHashMap(), state.To); Assert.Equal(initialValue.Combine(toAppend), hashMap.ToHashMap()); Assert.True( HashMap>( ("biz", Change.Added(7)), ("baz", Change.Added(9))) == state.Changes); } [Fact] public void AppendAtomInvokesChange() { var hashMap = AtomHashMap(("foo", 3), ("bar", 42)); var toAppend = AtomHashMap(("foo", 7), ("biz", 7), ("baz", 9)); var initialValue = hashMap.ToHashMap(); HashMapPatch state = default; hashMap.Change += v => state = v; hashMap.Append(toAppend); Assert.Equal(initialValue, state.From); Assert.Equal(hashMap.ToHashMap(), state.To); Assert.Equal( initialValue.Combine(toAppend.ToHashMap()), hashMap.ToHashMap()); Assert.True( HashMap>( ("biz", Change.Added(7)), ("baz", Change.Added(9))) == state.Changes); } [Fact] public void SubtractInvokesChange() { var hashMap = AtomHashMap( ("foo", 3), ("bar", 42), ("biz", 7), ("baz", 9)); var toSubtract = HashMap( ("foo", 7), ("biz", 7), ("baz", 9)); var initialValue = hashMap.ToHashMap(); HashMapPatch state = default; hashMap.Change += v => state = v; hashMap.Subtract(toSubtract); Assert.Equal(initialValue, state.From); Assert.Equal(hashMap.ToHashMap(), state.To); Assert.Equal( initialValue.Subtract(toSubtract), hashMap.ToHashMap()); Assert.Equal( HashMap>( ("foo", Change.Removed(3)), ("biz", Change.Removed(7)), ("baz", Change.Removed(9))), state.Changes); } [Fact] public void SubtractAtomInvokesChange() { var hashMap = AtomHashMap( ("foo", 3), ("bar", 42), ("biz", 7), ("baz", 9)); var toSubtract = AtomHashMap( ("foo", 7), ("biz", 7), ("baz", 9)); var initialValue = hashMap.ToHashMap(); HashMapPatch state = default; hashMap.Change += v => state = v; hashMap.Subtract(toSubtract); Assert.Equal(initialValue, state.From); Assert.Equal(hashMap.ToHashMap(), state.To); Assert.Equal( initialValue.Subtract(toSubtract.ToHashMap()), hashMap.ToHashMap()); Assert.Equal( HashMap>( ("foo", Change.Removed(3)), ("biz", Change.Removed(7)), ("baz", Change.Removed(9))), state.Changes); } [Fact] public void IntersectInvokesChange() { var hashMap = AtomHashMap( ("foo", 3), ("bar", 42), ("biz", 7), ("baz", 9)); var toIntersect = HashMap( ("foo", 7), ("biz", 7), ("baz", 9), ("bin", 0)); var initialValue = hashMap.ToHashMap(); HashMapPatch state = default; hashMap.Change += v => state = v; hashMap.Intersect(toIntersect); Assert.Equal(initialValue, state.From); Assert.Equal(hashMap.ToHashMap(), state.To); Assert.Equal( initialValue.Intersect(toIntersect), hashMap.ToHashMap()); Assert.Equal( HashMap>( ("bar", Change.Removed(42))), state.Changes); } [Fact] public void IntersectAtomInvokesChange() { var hashMap = AtomHashMap( ("foo", 3), ("bar", 42), ("biz", 7), ("baz", 9)); var toIntersect = AtomHashMap( ("foo", 7), ("biz", 7), ("baz", 9), ("bin", 0)); var initialValue = hashMap.ToHashMap(); HashMapPatch state = default; hashMap.Change += v => state = v; hashMap.Intersect(toIntersect); Assert.Equal(initialValue, state.From); Assert.Equal(hashMap.ToHashMap(), state.To); Assert.Equal( initialValue.Intersect(toIntersect.ToHashMap()), hashMap.ToHashMap()); Assert.Equal( HashMap>( ("bar", Change.Removed(42))), state.Changes); } [Fact] public void ExceptInvokesChange() { var hashMap = AtomHashMap( ("foo", 3), ("bar", 42), ("biz", 7), ("baz", 9)); var toExcept = HashMap( ("foo", 7), ("biz", 7), ("baz", 9), ("bin", 0)); var initialValue = hashMap.ToHashMap(); HashMapPatch state = default; hashMap.Change += v => state = v; hashMap.Except(toExcept); Assert.Equal(initialValue, state.From); Assert.Equal(hashMap.ToHashMap(), state.To); Assert.Equal( initialValue.Except(toExcept), hashMap.ToHashMap()); Assert.Equal( HashMap>( ("foo", Change.Removed(3)), ("biz", Change.Removed(7)), ("baz", Change.Removed(9))), state.Changes); } [Fact] public void ExceptKeysInvokesChange() { var hashMap = AtomHashMap( ("foo", 3), ("bar", 42), ("biz", 7), ("baz", 9)); var toExcept = Seq("biz", "baz"); var initialValue = hashMap.ToHashMap(); HashMapPatch state = default; hashMap.Change += v => state = v; hashMap.Except(toExcept); Assert.Equal(initialValue, state.From); Assert.Equal(hashMap.ToHashMap(), state.To); Assert.Equal( initialValue.Except(toExcept), hashMap.ToHashMap()); Assert.Equal( HashMap>( ("biz", Change.Removed(7)), ("baz", Change.Removed(9))), state.Changes); } [Fact] public void SymmetricExceptInvokesChange() { var hashMap = AtomHashMap( ("foo", 3), ("bar", 42)); var toExcept = AtomHashMap( ("foo", 3), ("biz", 7), ("baz", 9)); var initialValue = hashMap.ToHashMap(); HashMapPatch state = default; hashMap.Change += v => state = v; hashMap.SymmetricExcept(toExcept); Assert.Equal(initialValue, state.From); Assert.Equal(hashMap.ToHashMap(), state.To); Assert.Equal( initialValue.SymmetricExcept(toExcept), hashMap.ToHashMap()); Assert.Equal( HashMap>( ("foo", Change.Removed(3)), ("biz", Change.Added(7)), ("baz", Change.Added(9))), state.Changes); } [Fact] public void UnionInvokesChange() { var hashMap = AtomHashMap(("foo", 3), ("bar", 42)); var toUnion = AtomHashMap(("foo", 7), ("biz", 7), ("baz", 9)); var initialValue = hashMap.ToHashMap(); HashMapPatch state = default; hashMap.Change += v => state = v; hashMap.Union(toUnion); Assert.Equal(initialValue, state.From); Assert.Equal(hashMap.ToHashMap(), state.To); Assert.Equal( initialValue.Union(toUnion), hashMap.ToHashMap()); Assert.Equal( HashMap>( ("biz", Change.Added(7)), ("baz", Change.Added(9))), state.Changes); } [Fact] public void Union_TakeRight_InvokesChange() { var hashMap = AtomHashMap(("foo", 3), ("bar", 42)); var toUnion = AtomHashMap(("foo", 7), ("biz", 7), ("baz", 9)); var initialValue = hashMap.ToHashMap(); HashMapPatch state = default; hashMap.Change += v => state = v; hashMap.Union(toUnion, Merge: (_, _, r) => r); Assert.Equal(initialValue, state.From); Assert.Equal(hashMap.ToHashMap(), state.To); Assert.Equal( initialValue.Union(toUnion, Merge: (_, _, r) => r), hashMap.ToHashMap()); Assert.Equal( HashMap>( ("foo", Change.Mapped(3, 7)), ("biz", Change.Added(7)), ("baz", Change.Added(9))), state.Changes); } [Fact] public void Union_TakeLeft_InvokesChange() { var hashMap = AtomHashMap(("foo", 3), ("bar", 42)); var toUnion = AtomHashMap(("foo", 7), ("biz", 7), ("baz", 9)); var initialValue = hashMap.ToHashMap(); HashMapPatch state = default; hashMap.Change += v => state = v; hashMap.Union(toUnion, Merge: (_, l, _) => l); Assert.Equal(initialValue, state.From); Assert.Equal(hashMap.ToHashMap(), state.To); Assert.Equal( initialValue.Union(toUnion, Merge: (_, l, _) => l), hashMap.ToHashMap()); Assert.Equal( HashMap>( ("biz", Change.Added(7)), ("baz", Change.Added(9))), state.Changes); } } ================================================ FILE: LanguageExt.Tests/AtomHashMapTests.cs ================================================ using static LanguageExt.Prelude; using Xunit; namespace LanguageExt.Tests; public class AtomHashMapTests { [Fact] public void SwapInvokesChange() { var hashMap = AtomHashMap(("foo", 3), ("bar", 42)); var initialValue = hashMap.ToHashMap(); HashMapPatch state = default; hashMap.Change += v => state = v; hashMap.Swap(m => m.Add("biz", 99)); Assert.Equal(initialValue, state.From); Assert.Equal(hashMap.ToHashMap(), state.To); Assert.Equal( HashMap(("biz", Change.Added(99))), state.Changes); } [Fact] public void SwapKeyInvokesChange() { var hashMap = AtomHashMap(("foo", 3), ("bar", 42)); var initialValue = hashMap.ToHashMap(); HashMapPatch state = default; hashMap.Change += v => state = v; hashMap.SwapKey("foo", i => i + 1); Assert.Equal(initialValue, state.From); Assert.Equal(hashMap.ToHashMap(), state.To); Assert.Equal( HashMap(("foo", Change.Mapped(3, 4))), state.Changes); } [Fact] public void SwapKeyOptionalInvokesChange() { var hashMap = AtomHashMap(("foo", 3), ("bar", 42)); var initialValue = hashMap.ToHashMap(); HashMapPatch state = default; hashMap.Change += v => state = v; hashMap.SwapKey("foo", i => None); Assert.Equal(initialValue, state.From); Assert.Equal(hashMap.ToHashMap(), state.To); Assert.Equal( HashMap(("foo", Change.Removed(3))), state.Changes); } [Fact] public void FilterInPlaceInvokesChange() { var hashMap = AtomHashMap(("foo", 3), ("bar", 42), ("biz", 7)); var initialValue = hashMap.ToHashMap(); HashMapPatch state = default; hashMap.Change += v => state = v; hashMap.FilterInPlace(i => i % 2 == 0); Assert.Equal(initialValue, state.From); Assert.Equal(hashMap.ToHashMap(), state.To); Assert.Equal( HashMap( ("foo", Change.Removed(3)), ("biz", Change.Removed(7))), state.Changes); } [Fact] public void FilterInPlaceWithKeyInvokesChange() { var hashMap = AtomHashMap(("foo", 3), ("bar", 42), ("biz", 7)); var initialValue = hashMap.ToHashMap(); HashMapPatch state = default; hashMap.Change += v => state = v; hashMap.FilterInPlace((k, i) => k[0] == 'b' && i % 2 == 0); Assert.Equal(initialValue, state.From); Assert.Equal(hashMap.ToHashMap(), state.To); Assert.Equal( HashMap( ("foo", Change.Removed(3)), ("biz", Change.Removed(7))), state.Changes); } [Fact] public void MapInPlaceInvokesChange() { var hashMap = AtomHashMap(("foo", 3), ("bar", 42), ("biz", 7)); var initialValue = hashMap.ToHashMap(); HashMapPatch state = default; hashMap.Change += v => state = v; hashMap.MapInPlace(i => i * 3); Assert.Equal(initialValue, state.From); Assert.Equal(hashMap.ToHashMap(), state.To); Assert.Equal( HashMap( ("foo", Change.Mapped(3, 9)), ("bar", Change.Mapped(42, 126)), ("biz", Change.Mapped(7, 21))), state.Changes); } [Fact] public void AddInvokesChange() { var hashMap = AtomHashMap(("foo", 3), ("bar", 42)); var initialValue = hashMap.ToHashMap(); HashMapPatch state = default; hashMap.Change += v => state = v; hashMap.Add("biz", 7); Assert.Equal(initialValue, state.From); Assert.Equal(hashMap.ToHashMap(), state.To); Assert.Equal( HashMap( ("biz", Change.Added(7))), state.Changes); } [Fact] public void TryAddInvokesChange() { var hashMap = AtomHashMap(("foo", 3), ("bar", 42)); var initialValue = hashMap.ToHashMap(); HashMapPatch state = default; hashMap.Change += v => state = v; hashMap.TryAdd("biz", 7); Assert.Equal(initialValue, state.From); Assert.Equal(hashMap.ToHashMap(), state.To); Assert.Equal( HashMap( ("biz", Change.Added(7))), state.Changes); } [Fact] public void AddOrUpdateInvokesChange() { var hashMap = AtomHashMap(("foo", 3), ("bar", 42)); var initialValue = hashMap.ToHashMap(); HashMapPatch state = default; hashMap.Change += v => state = v; hashMap.AddOrUpdate("biz", 7); Assert.Equal(initialValue, state.From); Assert.Equal(hashMap.ToHashMap(), state.To); Assert.Equal( HashMap( ("biz", Change.Added(7))), state.Changes); } [Fact] public void AddRangeInvokesChange() { var hashMap = AtomHashMap(("foo", 3), ("bar", 42)); var initialValue = hashMap.ToHashMap(); HashMapPatch state = default; hashMap.Change += v => state = v; hashMap.AddRange(Seq(("biz", 7), ("baz", 9))); Assert.Equal(initialValue, state.From); Assert.Equal(hashMap.ToHashMap(), state.To); Assert.Equal( HashMap( ("biz", Change.Added(7)), ("baz", Change.Added(9))), state.Changes); } [Fact] public void TryAddRangeInvokesChange() { var hashMap = AtomHashMap(("foo", 3), ("bar", 42)); var initialValue = hashMap.ToHashMap(); HashMapPatch state = default; hashMap.Change += v => state = v; hashMap.TryAddRange(Seq(("biz", 7), ("baz", 9))); Assert.Equal(initialValue, state.From); Assert.Equal(hashMap.ToHashMap(), state.To); Assert.Equal( HashMap( ("biz", Change.Added(7)), ("baz", Change.Added(9))), state.Changes); } [Fact] public void AddOrUpdateRangeInvokesChange() { var hashMap = AtomHashMap(("foo", 3), ("bar", 42)); var initialValue = hashMap.ToHashMap(); HashMapPatch state = default; hashMap.Change += v => state = v; hashMap.AddOrUpdateRange(Seq(("biz", 7), ("baz", 9))); Assert.Equal(initialValue, state.From); Assert.Equal(hashMap.ToHashMap(), state.To); Assert.Equal( HashMap( ("biz", Change.Added(7)), ("baz", Change.Added(9))), state.Changes); } [Fact] public void RemoveInvokesChange() { var hashMap = AtomHashMap(("foo", 3), ("bar", 42)); var initialValue = hashMap.ToHashMap(); HashMapPatch state = default; hashMap.Change += v => state = v; hashMap.Remove("bar"); Assert.Equal(initialValue, state.From); Assert.Equal(hashMap.ToHashMap(), state.To); Assert.Equal( HashMap( ("bar", Change.Removed(42))), state.Changes); } [Fact] public void RemoveRangeInvokesChange() { var hashMap = AtomHashMap(("foo", 3), ("bar", 42), ("biz", 7)); var initialValue = hashMap.ToHashMap(); HashMapPatch state = default; hashMap.Change += v => state = v; hashMap.RemoveRange(Seq("bar", "biz")); Assert.Equal(initialValue, state.From); Assert.Equal(hashMap.ToHashMap(), state.To); Assert.Equal( HashMap( ("bar", Change.Removed(42)), ("biz", Change.Removed(7))), state.Changes); } [Fact] public void FindOrAddInvokesChange() { var hashMap = AtomHashMap(("foo", 3), ("bar", 42)); var initialValue = hashMap.ToHashMap(); HashMapPatch state = default; hashMap.Change += v => state = v; hashMap.FindOrAdd("biz", () => 7); Assert.Equal(initialValue, state.From); Assert.Equal(hashMap.ToHashMap(), state.To); Assert.Equal( HashMap( ("biz", Change.Added(7))), state.Changes); } [Fact] public void FindOrAddConstantInvokesChange() { var hashMap = AtomHashMap(("foo", 3), ("bar", 42)); var initialValue = hashMap.ToHashMap(); HashMapPatch state = default; hashMap.Change += v => state = v; hashMap.FindOrAdd("biz", 7); Assert.Equal(initialValue, state.From); Assert.Equal(hashMap.ToHashMap(), state.To); Assert.Equal( HashMap( ("biz", Change.Added(7))), state.Changes); } [Fact] public void FindOrMaybeAddInvokesChange() { var hashMap = AtomHashMap(("foo", 3), ("bar", 42)); var initialValue = hashMap.ToHashMap(); HashMapPatch state = default; hashMap.Change += v => state = v; hashMap.FindOrMaybeAdd("biz", () => Some(7)); Assert.Equal(initialValue, state.From); Assert.Equal(hashMap.ToHashMap(), state.To); Assert.Equal( HashMap( ("biz", Change.Added(7))), state.Changes); } [Fact] public void FindOrMaybeAddConstantInvokesChange() { var hashMap = AtomHashMap(("foo", 3), ("bar", 42)); var initialValue = hashMap.ToHashMap(); HashMapPatch state = default; hashMap.Change += v => state = v; hashMap.FindOrMaybeAdd("biz", Some(7)); Assert.Equal(initialValue, state.From); Assert.Equal(hashMap.ToHashMap(), state.To); Assert.Equal( HashMap( ("biz", Change.Added(7))), state.Changes); } [Fact] public void SetItemsInvokesChange() { var hashMap = AtomHashMap(("foo", 3), ("bar", 42)); var initialValue = hashMap.ToHashMap(); HashMapPatch state = default; hashMap.Change += v => state = v; hashMap.SetItems(Seq(("foo", 80), ("bar", 17))); Assert.Equal(initialValue, state.From); Assert.Equal(hashMap.ToHashMap(), state.To); Assert.Equal( HashMap( ("foo", Change.Mapped(3, 80)), ("bar", Change.Mapped(42, 17))), state.Changes); } [Fact] public void TrySetItemsInvokesChange() { var hashMap = AtomHashMap(("foo", 3), ("bar", 42)); var initialValue = hashMap.ToHashMap(); HashMapPatch state = default; hashMap.Change += v => state = v; hashMap.TrySetItems(Seq(("foo", 80), ("bar", 17), ("biz", 33))); Assert.Equal(initialValue, state.From); Assert.Equal(hashMap.ToHashMap(), state.To); Assert.Equal( HashMap( ("foo", Change.Mapped(3, 80)), ("bar", Change.Mapped(42, 17))), state.Changes); } [Fact] public void SetItemFuncInvokesChange() { var hashMap = AtomHashMap(("foo", 3), ("bar", 42)); var initialValue = hashMap.ToHashMap(); HashMapPatch state = default; hashMap.Change += v => state = v; hashMap.SetItem("foo", i => i * 2); Assert.Equal(initialValue, state.From); Assert.Equal(hashMap.ToHashMap(), state.To); Assert.Equal( HashMap( ("foo", Change.Mapped(3, 6))), state.Changes); } [Fact] public void TrySetItemInvokesChange() { var hashMap = AtomHashMap(("foo", 3), ("bar", 42)); var initialValue = hashMap.ToHashMap(); HashMapPatch state = default; hashMap.Change += v => state = v; hashMap.TrySetItem("foo", 80); Assert.Equal(initialValue, state.From); Assert.Equal(hashMap.ToHashMap(), state.To); Assert.Equal( HashMap( ("foo", Change.Mapped(3, 80))), state.Changes); } [Fact] public void TrySetItemFuncInvokesChange() { var hashMap = AtomHashMap(("foo", 3), ("bar", 42)); var initialValue = hashMap.ToHashMap(); HashMapPatch state = default; hashMap.Change += v => state = v; hashMap.TrySetItem("foo", i => i * 2); Assert.Equal(initialValue, state.From); Assert.Equal(hashMap.ToHashMap(), state.To); Assert.Equal( HashMap( ("foo", Change.Mapped(3, 6))), state.Changes); } [Fact] public void ClearInvokesChange() { var hashMap = AtomHashMap(("foo", 3), ("bar", 42)); var initialValue = hashMap.ToHashMap(); HashMapPatch state = default; hashMap.Change += v => state = v; hashMap.Clear(); Assert.Equal(initialValue, state.From); Assert.Equal(hashMap.ToHashMap(), state.To); Assert.Equal( HashMap( ("foo", Change.Removed(3)), ("bar", Change.Removed(42))), state.Changes); } [Fact] public void AppendInvokesChange() { var hashMap = AtomHashMap(("foo", 3), ("bar", 42)); var toAppend = HashMap(("foo", 7), ("biz", 7), ("baz", 9)); var initialValue = hashMap.ToHashMap(); HashMapPatch state = default; hashMap.Change += v => state = v; hashMap.Append(toAppend); Assert.Equal(initialValue, state.From); Assert.Equal(hashMap.ToHashMap(), state.To); Assert.Equal(initialValue.Combine(toAppend), hashMap.ToHashMap()); Assert.Equal( HashMap( ("biz", Change.Added(7)), ("baz", Change.Added(9))), state.Changes); } [Fact] public void AppendAtomInvokesChange() { var hashMap = AtomHashMap(("foo", 3), ("bar", 42)); var toAppend = AtomHashMap(("foo", 7), ("biz", 7), ("baz", 9)); var initialValue = hashMap.ToHashMap(); HashMapPatch state = default; hashMap.Change += v => state = v; hashMap.Append(toAppend); Assert.Equal(initialValue, state.From); Assert.Equal(hashMap.ToHashMap(), state.To); Assert.Equal( initialValue.Combine(toAppend.ToHashMap()), hashMap.ToHashMap()); Assert.Equal( HashMap( ("biz", Change.Added(7)), ("baz", Change.Added(9))), state.Changes); } [Fact] public void SubtractInvokesChange() { var hashMap = AtomHashMap( ("foo", 3), ("bar", 42), ("biz", 7), ("baz", 9)); var toSubtract = HashMap(("foo", 7), ("biz", 7), ("baz", 9)); var initialValue = hashMap.ToHashMap(); HashMapPatch state = default; hashMap.Change += v => state = v; hashMap.Subtract(toSubtract); Assert.Equal(initialValue, state.From); Assert.Equal(hashMap.ToHashMap(), state.To); Assert.Equal( initialValue.Subtract(toSubtract), hashMap.ToHashMap()); Assert.Equal( HashMap( ("foo", Change.Removed(3)), ("biz", Change.Removed(7)), ("baz", Change.Removed(9))), state.Changes); } [Fact] public void SubtractAtomInvokesChange() { var hashMap = AtomHashMap( ("foo", 3), ("bar", 42), ("biz", 7), ("baz", 9)); var toSubtract = AtomHashMap(("foo", 7), ("biz", 7), ("baz", 9)); var initialValue = hashMap.ToHashMap(); HashMapPatch state = default; hashMap.Change += v => state = v; hashMap.Subtract(toSubtract); Assert.Equal(initialValue, state.From); Assert.Equal(hashMap.ToHashMap(), state.To); Assert.Equal( initialValue.Subtract(toSubtract.ToHashMap()), hashMap.ToHashMap()); Assert.Equal( HashMap( ("foo", Change.Removed(3)), ("biz", Change.Removed(7)), ("baz", Change.Removed(9))), state.Changes); } [Fact] public void IntersectInvokesChange() { var hashMap = AtomHashMap( ("foo", 3), ("bar", 42), ("biz", 7), ("baz", 9)); var toIntersect = HashMap( ("foo", 7), ("biz", 7), ("baz", 9), ("bin", 0)); var initialValue = hashMap.ToHashMap(); HashMapPatch state = default; hashMap.Change += v => state = v; hashMap.Intersect(toIntersect); Assert.Equal(initialValue, state.From); Assert.Equal(hashMap.ToHashMap(), state.To); Assert.Equal( initialValue.Intersect(toIntersect), hashMap.ToHashMap()); Assert.Equal( HashMap( ("bar", Change.Removed(42))), state.Changes); } [Fact] public void IntersectAtomInvokesChange() { var hashMap = AtomHashMap( ("foo", 3), ("bar", 42), ("biz", 7), ("baz", 9)); var toIntersect = AtomHashMap( ("foo", 7), ("biz", 7), ("baz", 9), ("bin", 0)); var initialValue = hashMap.ToHashMap(); HashMapPatch state = default; hashMap.Change += v => state = v; hashMap.Intersect(toIntersect); Assert.Equal(initialValue, state.From); Assert.Equal(hashMap.ToHashMap(), state.To); Assert.Equal( initialValue.Intersect(toIntersect.ToHashMap()), hashMap.ToHashMap()); Assert.Equal( HashMap( ("bar", Change.Removed(42))), state.Changes); } [Fact] public void ExceptInvokesChange() { var hashMap = AtomHashMap( ("foo", 3), ("bar", 42), ("biz", 7), ("baz", 9)); var toExcept = HashMap( ("foo", 7), ("biz", 7), ("baz", 9), ("bin", 0)); var initialValue = hashMap.ToHashMap(); HashMapPatch state = default; hashMap.Change += v => state = v; hashMap.Except(toExcept); Assert.Equal(initialValue, state.From); Assert.Equal(hashMap.ToHashMap(), state.To); Assert.Equal( initialValue.Except(toExcept), hashMap.ToHashMap()); Assert.Equal( HashMap( ("foo", Change.Removed(3)), ("biz", Change.Removed(7)), ("baz", Change.Removed(9))), state.Changes); } [Fact] public void ExceptKeysInvokesChange() { var hashMap = AtomHashMap( ("foo", 3), ("bar", 42), ("biz", 7), ("baz", 9)); var toExcept = Seq("biz", "baz"); var initialValue = hashMap.ToHashMap(); HashMapPatch state = default; hashMap.Change += v => state = v; hashMap.Except(toExcept); Assert.Equal(initialValue, state.From); Assert.Equal(hashMap.ToHashMap(), state.To); Assert.Equal( initialValue.Except(toExcept), hashMap.ToHashMap()); Assert.Equal( HashMap( ("biz", Change.Removed(7)), ("baz", Change.Removed(9))), state.Changes); } [Fact] public void SymmetricExceptInvokesChange() { var hashMap = AtomHashMap(("foo", 3), ("bar", 42)); var toExcept = AtomHashMap(("foo", 3), ("biz", 7), ("baz", 9)); var expected = HashMap(("bar", 42), ("biz", 7), ("baz", 9)); var initialValue = hashMap.ToHashMap(); HashMapPatch state = default; hashMap.Change += v => state = v; hashMap.SymmetricExcept(toExcept); Assert.Equal(initialValue, state.From); Assert.Equal(hashMap.ToHashMap(), expected); Assert.Equal( HashMap( ("foo", Change.Removed(3)), ("biz", Change.Added(7)), ("baz", Change.Added(9))), state.Changes); } [Fact] public void UnionInvokesChange() { var hashMap = AtomHashMap(("foo", 3), ("bar", 42)); var toUnion = AtomHashMap(("foo", 7), ("biz", 7), ("baz", 9)); var initialValue = hashMap.ToHashMap(); HashMapPatch state = default; hashMap.Change += v => state = v; hashMap.Union(toUnion); Assert.Equal(initialValue, state.From); Assert.Equal(hashMap.ToHashMap(), state.To); Assert.Equal( initialValue.Union(toUnion), hashMap.ToHashMap()); Assert.Equal( HashMap( ("biz", Change.Added(7)), ("baz", Change.Added(9))), state.Changes); } [Fact] public void Union_TakeRight_InvokesChange() { var hashMap = AtomHashMap(("foo", 3), ("bar", 42)); var toUnion = AtomHashMap(("foo", 7), ("biz", 7), ("baz", 9)); var initialValue = hashMap.ToHashMap(); HashMapPatch state = default; hashMap.Change += v => state = v; hashMap.Union(toUnion, Merge: (_, _, r) => r); Assert.Equal(initialValue, state.From); Assert.Equal(hashMap.ToHashMap(), state.To); Assert.Equal( initialValue.Union(toUnion, Merge: (_, _, r) => r), hashMap.ToHashMap()); Assert.Equal( HashMap( ("foo", Change.Mapped(3, 7)), ("biz", Change.Added(7)), ("baz", Change.Added(9))), state.Changes); } [Fact] public void Union_TakeLeft_InvokesChange() { var hashMap = AtomHashMap(("foo", 3), ("bar", 42)); var toUnion = AtomHashMap(("foo", 7), ("biz", 7), ("baz", 9)); var initialValue = hashMap.ToHashMap(); HashMapPatch state = default; hashMap.Change += v => state = v; hashMap.Union(toUnion, Merge: (_, l, _) => l); Assert.Equal(initialValue, state.From); Assert.Equal(hashMap.ToHashMap(), state.To); Assert.Equal( initialValue.Union(toUnion, Merge: (_, l, _) => l), hashMap.ToHashMap()); Assert.Equal( HashMap( ("biz", Change.Added(7)), ("baz", Change.Added(9))), state.Changes); } [Theory] [InlineData(-2)] [InlineData(0b10000011001)] [InlineData(0b1001100111001)] [InlineData(0b1001110000010)] [InlineData(4114)] public void AppendInvokesChangeInt(int conflictKey) { var hashMap = AtomHashMap((conflictKey, 3), (2, 42)); var toAppend = HashMap((conflictKey, 6), (5, 7), (25, 9)); var initialValue = hashMap.ToHashMap(); HashMapPatch state = default!; hashMap.Change += v => state = v; hashMap.Append(toAppend); Assert.Equal(initialValue, state.From); Assert.Equal(hashMap.ToHashMap(), state.To); Assert.Equal(initialValue.Combine(toAppend), hashMap.ToHashMap()); Assert.Equal(3, hashMap[conflictKey]); Assert.Equal( HashMap( (5, Change.Added(7)), (25, Change.Added(9))), state.Changes); } } ================================================ FILE: LanguageExt.Tests/AtomTests.cs ================================================ using System.Linq; using static LanguageExt.Prelude; using Xunit; using System.Diagnostics; namespace LanguageExt.Tests; public class AtomTests { [Fact] public void ConstructAndSwap() { var atom = Atom(Set("A", "B", "C")); atom.Swap(old => old.Add("D")); atom.Swap(old => old.Add("E")); atom.Swap(old => old.Add("F")); Debug.Assert(atom == Set("A", "B", "C", "D", "E", "F")); } [Fact] public void AtomSeqEnumeration() { var xs = Seq(1,2,3,4); var atom = AtomSeq(xs); Assert.Equal(atom.Sum(), xs.Sum()); } } ================================================ FILE: LanguageExt.Tests/ChoiceTests.cs ================================================ using System; using static LanguageExt.Prelude; using Xunit; using System.Threading.Tasks; using LanguageExt.Common; namespace LanguageExt.Tests; public class ChoiceTests { [Fact] public async Task TaskFirstChoice() { var ma = 123.AsTask(); var mb = new Exception().AsFailedTask(); var mc = new Exception().AsFailedTask(); var res = choice(ma, mb, mc); Assert.True(await res == 123); } [Fact] public async Task TaskMiddleChoice() { var ma = new Exception().AsFailedTask(); var mb = 123.AsTask(); var mc = new Exception().AsFailedTask(); var res = choice(ma, mb, mc); Assert.True(await res == 123); } [Fact] public async Task TaskLastChoice() { var ma = new Exception().AsFailedTask(); var mb = new Exception().AsFailedTask(); var mc = 123.AsTask(); var res = choice(ma, mb, mc); Assert.True(await res == 123); } [Fact] public async Task TaskNoChoice() { var ma = new Exception().AsFailedTask(); var mb = new Exception().AsFailedTask(); var mc = new Exception().AsFailedTask(); var res = choice(ma, mb, mc); await Assert.ThrowsAsync(async () => await res); } } ================================================ FILE: LanguageExt.Tests/CollectionOrderingTests.cs ================================================ using Xunit; using static LanguageExt.Prelude; namespace LanguageExt.Tests; public class CollectionOrderingTests { [Fact] public void TestSetOrdering1() { var x = Set(1, 2, 3, 4, 5); var y = Set(1, 2, 3, 4, 5); Assert.True(x.CompareTo(y) == 0); Assert.False(x < y); Assert.True(x <= y); Assert.False(x > y); Assert.True(x >= y); } [Fact] public void TestSetOrdering2() { var x = Set(1, 2, 3, 4, 5); var y = Set(1, 2, 3, 4, 6); Assert.True(x.CompareTo(y) < 0); Assert.True(x.CompareTo(y) <= 0); Assert.True(x < y); Assert.True(x <= y); Assert.False(x > y); Assert.False(x >= y); } [Fact] public void TestSetOrdering3() { var x = Set(1, 2, 3, 4, 6); var y = Set(1, 2, 3, 4, 5); Assert.True(x.CompareTo(y) > 0); Assert.True(x.CompareTo(y) >= 0); Assert.False(x < y); Assert.False(x <= y); Assert.True(x > y); Assert.True(x >= y); } [Fact] public void TestSetOrdering4() { var x = Set(1, 2, 3, 4, 5); var y = Set(1, 2, 3, 4); Assert.True(x.CompareTo(y) > 0); Assert.True(x.CompareTo(y) >= 0); Assert.False(x < y); Assert.False(x <= y); Assert.True(x > y); Assert.True(x >= y); } [Fact] public void TestSetOrdering5() { var x = Set(1, 2, 3, 4); var y = Set(1, 2, 3, 4, 5); Assert.True(x.CompareTo(y) < 0); Assert.True(x.CompareTo(y) <= 0); Assert.True(x < y); Assert.True(x <= y); Assert.False(x > y); Assert.False(x >= y); } [Fact] public void TestListOrdering1() { var x = List(1, 2, 3, 4, 5); var y = List(1, 2, 3, 4, 5); Assert.True(x.CompareTo(y) == 0); Assert.False(x < y); Assert.True(x <= y); Assert.False(x > y); Assert.True(x >= y); } [Fact] public void TestListOrdering2() { var x = List(1, 2, 3, 4, 5); var y = List(1, 2, 3, 4, 6); Assert.True(x.CompareTo(y) < 0); Assert.True(x.CompareTo(y) <= 0); Assert.True(x < y); Assert.True(x <= y); Assert.False(x > y); Assert.False(x >= y); } [Fact] public void TestListOrdering3() { var x = List(1, 2, 3, 4, 6); var y = List(1, 2, 3, 4, 5); Assert.True(x.CompareTo(y) > 0); Assert.True(x.CompareTo(y) >= 0); Assert.False(x < y); Assert.False(x <= y); Assert.True(x > y); Assert.True(x >= y); } [Fact] public void TestListOrdering4() { var x = List(1, 2, 3, 4, 5); var y = List(1, 2, 3, 4); Assert.True(x.CompareTo(y) > 0); Assert.True(x.CompareTo(y) >= 0); Assert.False(x < y); Assert.False(x <= y); Assert.True(x > y); Assert.True(x >= y); } [Fact] public void TestListOrdering5() { var x = List(1, 2, 3, 4); var y = List(1, 2, 3, 4, 5); Assert.True(x.CompareTo(y) < 0); Assert.True(x.CompareTo(y) <= 0); Assert.True(x < y); Assert.True(x <= y); Assert.False(x > y); Assert.False(x >= y); } [Fact] public void TestMapOrdering1() { var x = Map((1, 'a'), (2, 'a'), (3, 'a'), (4, 'a'), (5, 'a')); var y = Map((1, 'a'), (2, 'a'), (3, 'a'), (4, 'a'), (5, 'a')); Assert.True(x.CompareTo(y) == 0); Assert.False(x < y); Assert.True(x <= y); Assert.False(x > y); Assert.True(x >= y); } [Fact] public void TestMapOrdering2() { var x = Map((1, 'a'), (2, 'a'), (3, 'a'), (4, 'a'), (5, 'a')); var y = Map((1, 'a'), (2, 'a'), (3, 'a'), (4, 'a'), (6, 'a')); Assert.True(x.CompareTo(y) < 0); Assert.True(x.CompareTo(y) <= 0); Assert.True(x < y); Assert.True(x <= y); Assert.False(x > y); Assert.False(x >= y); } [Fact] public void TestMapOrdering3() { var x = Map((1, 'a'), (2, 'a'), (3, 'a'), (4, 'a'), (6, 'a')); var y = Map((1, 'a'), (2, 'a'), (3, 'a'), (4, 'a'), (5, 'a')); Assert.True(x.CompareTo(y) > 0); Assert.True(x.CompareTo(y) >= 0); Assert.False(x < y); Assert.False(x <= y); Assert.True(x > y); Assert.True(x >= y); } [Fact] public void TestMapOrdering4() { var x = Map((1, 'a'), (2, 'a'), (3, 'a'), (4, 'a'), (5, 'a')); var y = Map((1, 'a'), (2, 'a'), (3, 'a'), (4, 'a')); Assert.True(x.CompareTo(y) > 0); Assert.True(x.CompareTo(y) >= 0); Assert.False(x < y); Assert.False(x <= y); Assert.True(x > y); Assert.True(x >= y); } [Fact] public void TestMapOrdering5() { var x = Map((1, 'a'), (2, 'a'), (3, 'a'), (4, 'a')); var y = Map((1, 'a'), (2, 'a'), (3, 'a'), (4, 'a'), (5, 'a')); Assert.True(x.CompareTo(y) < 0); Assert.True(x.CompareTo(y) <= 0); Assert.True(x < y); Assert.True(x <= y); Assert.False(x > y); Assert.False(x >= y); } } ================================================ FILE: LanguageExt.Tests/CollectionToStringTests.cs ================================================ using System.Linq; using Xunit; using static LanguageExt.Prelude; namespace LanguageExt.Tests; public class CollectionToStringTests { readonly static int[] zeroToFour = Range(0, 5).ToArray(); readonly static int[] zeroToFourtyNine = Range(0, 50).ToArray(); readonly static int[] zeroToFiftyNine = Range(0, 60).ToArray(); readonly static string zeroToFourString = "[0, 1, 2, 3, 4]"; readonly static string zeroToFourtyNineString = "[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49]"; readonly static string zeroToFiftyNineString = "[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49 ... 10 more]"; readonly static (string, int)[] zeroToFourkeyValue = new[] { ("A", 0), ("B", 1), ("C", 2), ("D", 3), ("E", 4) }; readonly static string zeroToFourKeyValueString = "[(A: 0), (B: 1), (C: 2), (D: 3), (E: 4)]"; [Fact] public void ArrShortToString() { var items = toArray(zeroToFour); Assert.True(items.ToString() == zeroToFourString); } [Fact] public void ArrMedToString() { var items = toArray(zeroToFourtyNine); Assert.True(items.ToString() == zeroToFourtyNineString); } [Fact] public void ArrLongToString() { var items = toArray(zeroToFiftyNine); Assert.True(items.ToString() == zeroToFiftyNineString); } [Fact] public void LstShortToString() { var items = toList(zeroToFour); Assert.True(items.ToString() == zeroToFourString); } [Fact] public void LstMedToString() { var items = toList(zeroToFourtyNine); Assert.True(items.ToString() == zeroToFourtyNineString); } [Fact] public void LstLongToString() { var items = toList(zeroToFiftyNine); Assert.True(items.ToString() == zeroToFiftyNineString); } [Fact] public void SeqShortToString() { var items = toSeq(zeroToFour); Assert.True(items.ToString() == zeroToFourString); } [Fact] public void SeqMedToString() { var items = toSeq(zeroToFourtyNine); Assert.True(items.ToString() == zeroToFourtyNineString); } [Fact] public void SeqLongToString() { var items = toSeq(zeroToFiftyNine); Assert.True(items.ToString() == zeroToFiftyNineString); } [Fact] public void SetShortToString() { var items = toSet(zeroToFour); Assert.True(items.ToString() == zeroToFourString); } [Fact] public void SetMedToString() { var items = toSet(zeroToFourtyNine); Assert.True(items.ToString() == zeroToFourtyNineString); } [Fact] public void SetLongToString() { var items = toSet(zeroToFiftyNine); Assert.True(items.ToString() == zeroToFiftyNineString); } [Fact] public void MapToString() { var items = toMap(zeroToFourkeyValue); Assert.True(items.ToString() == zeroToFourKeyValueString); } } ================================================ FILE: LanguageExt.Tests/CombinatorsTests.cs ================================================ using Xunit; using static LanguageExt.CombinatorsDynamic; namespace LanguageExt.Tests; public class CombinatorsTests { /// /// Derive the Identity combinator /// [Fact] public void DeriveIdiotBird() { var I = S(K)(K); Assert.True(I(10) == 10); Assert.True(I(5) == 5); } /// /// Derive the Bind combinator /// [Fact] public void DeriveBluebird() { var B = S(K(S))(K); } [Fact] public void DeriveCardinalBird() { //C = S(BBS)(KK) = S(S(KS)K(S(KS)K)S)(KK) var B = S(K(S))(K); var C = S(B(B)(S))(K(K)); } } ================================================ FILE: LanguageExt.Tests/CompositionTests.cs ================================================ using System; using Xunit; using static LanguageExt.Prelude; namespace LanguageExt.Tests; public class CompositionTests { private readonly Func f; private readonly Func g; private readonly Func h; public CompositionTests() { f = () => "Bob"; g = (string name) => $"Hello, {name}"; h = (string s) => s.Length; } [Fact] public void Sanity() { string expected = "Hello, Bob"; Assert.Equal(g(f()), expected); Assert.Equal(h(g(f())), expected.Length); } [Fact] public void BackComposeFuncWithNoArgFunc() { Assert.Equal(g.BackCompose(f)(), g(f())); } [Fact] public void ComposeNoArgFuncWithFunc() { Assert.Equal(f.Compose(g)(), g(f())); } [Fact] public void BackComposeActionWithNoArgFunc() { string result; var g = act((string name) => { result = this.g(name); }); result = null; g.BackCompose(f)(); Assert.Equal(result, this.g(f())); } [Fact] public void ComposeNoArgFuncWithAction() { string result; var g = act((string name) => { result = this.g(name); }); result = null; f.Compose(g)(); Assert.Equal(result, this.g(f())); } [Fact] public void BackComposeFuncWithFunc() { Assert.Equal(h.BackCompose(g)(f()), h(g(f()))); } [Fact] public void ComposeFuncWithFunc() { Assert.Equal(g.Compose(h)(f()), h(g(f()))); } [Fact] public void BackComposeActionWithFunc() { int? result; var h = act((string s) => { result = this.h(s); }); result = null; h.BackCompose(g)(f()); Assert.Equal(result, this.h(g(f()))); } [Fact] public void ComposeFuncWithAction() { int? result; var h = act((string s) => { result = this.h(s); }); result = null; g.Compose(h)(f()); Assert.Equal(result, this.h(g(f()))); } } ================================================ FILE: LanguageExt.Tests/DefaultValueChecks/AbstractDefaultValueCheckTests.cs ================================================ using Xunit; namespace LanguageExt.Tests.DefaultValueChecks { public abstract class AbstractDefaultValueCheckTests { protected abstract bool ExpectedWhenDefaultValue { get; } protected abstract bool DefaultValueCheck(T value); private bool ExpectedWhenNotDefaultValue => !ExpectedWhenDefaultValue; [Fact] public void DefaultValueCheck_DefaultValueObject_AsExpectedWhenDefaultValue() { object value = null; var actual = DefaultValueCheck(value); Assert.Equal(ExpectedWhenDefaultValue, actual); } [Fact] public void DefaultValueCheck_DefaultConstructorObject_AsExpectedWhenNotDefaultValue() { object value = new object(); var actual = DefaultValueCheck(value); Assert.Equal(ExpectedWhenNotDefaultValue, actual); } [Fact] public void DefaultValueCheck_DefaultValueString_AsExpectedWhenDefaultValue() { string value = null; var actual = DefaultValueCheck(value); Assert.Equal(ExpectedWhenDefaultValue, actual); } [Fact] public void DefaultValueCheck_HelloString_AsExpectedWhenNotDefaultValue() { string value = "hello"; var actual = DefaultValueCheck(value); Assert.Equal(ExpectedWhenNotDefaultValue, actual); } [Fact] public void DefaultValueCheck_DefaultValueInt_AsExpectedWhenDefaultValue() { int value = 0; var actual = DefaultValueCheck(value); Assert.Equal(ExpectedWhenDefaultValue, actual); } [Fact] public void DefaultValueCheck_NonDefaultValueInt_AsExpectedWhenNotDefaultValue() { int value = 100; var actual = DefaultValueCheck(value); Assert.Equal(ExpectedWhenNotDefaultValue, actual); } [Fact] public void DefaultValueCheck_DefaultValueNullableByte_AsExpectedWhenDefaultValue() { byte? value = null; var actual = DefaultValueCheck(value); Assert.Equal(ExpectedWhenDefaultValue, actual); } [Fact] public void DefaultValueCheck_ZeroNullableByte_AsExpectedWhenNotDefaultValue() { byte? value = 0; var actual = DefaultValueCheck(value); Assert.Equal(ExpectedWhenNotDefaultValue, actual); } [Fact] public void DefaultValueCheck_DefaultConstructorEnum_AsExpectedWhenDefaultValue() { FooEnum value = new FooEnum(); var actual = DefaultValueCheck(value); Assert.Equal(ExpectedWhenDefaultValue, actual); } [Fact] public void DefaultValueCheck_FirstEnumOptionWithValueOne_AsExpectedWhenNotDefaultValue() { FooEnum value = FooEnum.FooEnumState; var actual = DefaultValueCheck(value); Assert.Equal(ExpectedWhenNotDefaultValue, actual); } private enum FooEnum { FooEnumState = 1 } } } ================================================ FILE: LanguageExt.Tests/DefaultValueChecks/isDefaultPreludeTests.cs ================================================ namespace LanguageExt.Tests.DefaultValueChecks { public class isDefaultPreludeTests : AbstractDefaultValueCheckTests { protected override bool ExpectedWhenDefaultValue => true; protected override bool DefaultValueCheck(T value) => Prelude.isDefault(value); } } ================================================ FILE: LanguageExt.Tests/DefaultValueChecks/notDefaultPreludeTests.cs ================================================ namespace LanguageExt.Tests.DefaultValueChecks { public class notDefaultPreludeTests : AbstractDefaultValueCheckTests { protected override bool ExpectedWhenDefaultValue => false; protected override bool DefaultValueCheck(T value) => Prelude.notDefault(value); } } ================================================ FILE: LanguageExt.Tests/DelayTests.cs ================================================ using System; using System.Threading; using static LanguageExt.PreludeRx; using Xunit; namespace LanguageExt.Tests; public class DelayTests { [Fact] public void DelayTest1() { var span = TimeSpan.FromMilliseconds(500); var till = DateTime.Now.Add(span); var v = 0; delay(() => 1, span).Subscribe(x => v = x); while( DateTime.Now < till ) { Assert.True(v == 0); Thread.Sleep(10); } while (DateTime.Now < till.AddMilliseconds(200)) { Thread.Sleep(10); } Assert.True(v == 1); } } ================================================ FILE: LanguageExt.Tests/DistinctTests.cs ================================================ using Xunit; using LanguageExt.ClassInstances; using static LanguageExt.Prelude; namespace LanguageExt.Tests; public class DistinctTests { [Fact] void SeqDistinctIgnoreCase() { var items = Seq("Test", "other", "test"); Assert.Equal(items, items.Distinct()); Assert.Equal(items.Take(3), items.Distinct(_ => _, fun(EqStringOrdinal.Equals))); Assert.Equal(items.Take(2), items.Distinct(_ => _, fun(EqStringOrdinalIgnoreCase.Equals))); } } ================================================ FILE: LanguageExt.Tests/Divisible.cs ================================================ using Xunit; using LanguageExt.ClassInstances; using static LanguageExt.Prelude; namespace LanguageExt.Tests; public class Divisible { [Fact] public void OptionalNumericDivide() { var x = Some(20); var y = Some(10); var z = divide(x, y); Assert.True(z == 2); } } ================================================ FILE: LanguageExt.Tests/EitherApply.cs ================================================ using System; using LanguageExt.Traits; using Xunit; using static LanguageExt.Prelude; namespace LanguageExt.Tests; public class EitherApply { Func add = (a, b) => a + b; [Fact] public void ApplyRightArgs() { var either = Right>(add) .Apply(Right(3)) .Apply(Right(4)); Assert.Equal(Right(7), either); } [Fact] public void ApplyRightArgsF() { var either = apply( apply( Right>(add), Right(3) ), Right(4)); Assert.Equal(Right(7), either); } [Fact] public void ApplyRightArgsF2() { var either = map(add, Right(3)).Apply(Right(4)); Assert.Equal(Right(7), either); } [Fact] public void ApplyLeftArgs() { var opt = Some(add); var none = Option.None; var four = Some(4); var res = apply(opt, none).Apply(four); Assert.Equal(None, res); var either = Right>(add) .Apply(Left("left")) .Apply(Right(4)); Assert.Equal(Left("left"), either); } [Fact] public void ApplyLeftArgsF() { var either = apply(map(add, Left("left")), Right(4)); Assert.Equal(Left("left"), either); } [Fact] public void ApplyLeftArgsF2() { var either = map(add, Left("left")).Apply(Right(4)); Assert.Equal(Left("left"), either); } [Fact] public void ApplicativeLawHolds() { var first = Right>(add) .Apply(Right(3)) .Apply(Right(4)); var second = add.Map(Right(3)) .Apply(Right(4)); Assert.Equal(first, second); } [Fact] public void ApplicativeLawHoldsF() { var first = apply(map(add, Right(3)), Right(4)); var second = add.Map(Right(3)).Apply(Right(4)); Assert.Equal(first, second); } [Fact] public void ApplicativeLawHoldsF2() { var first = map(add, Right(3)).Apply(Right(4)); var second = add.Map(Right(3)).Apply(Right(4)); Assert.Equal(first, second); } } ================================================ FILE: LanguageExt.Tests/EitherCoalesceTests.cs ================================================ using Xunit; namespace LanguageExt.Tests; public class EitherCoalesceTests { [Fact] public void EitherCoalesceTest1() { Either either = 123; var value = either || 456; Assert.True(value == 123); } [Fact] public void EitherCoalesceTest2() { Either either = "Hello"; var value = either || 456; Assert.True(value == 456); } [Fact] public void EitherCoalesceTest3() { Either either1 = "Hello"; Either either2 = "World"; var value = either1 || either2 || 456; Assert.True(value == 456); } } ================================================ FILE: LanguageExt.Tests/EitherTests.cs ================================================ using Xunit; using static LanguageExt.Prelude; namespace LanguageExt.Tests; public class EitherTests { [Fact] public void RightGeneratorTestsObject() { Either either = Right(123); either.Match( Right: i => Assert.True(i == 123), Left: _ => Assert.Fail("Shouldn't get here") ); int c = either.Match( Right: i => i + 1, Left: _ => 0 ); Assert.True(c == 124); } [Fact] public void SomeGeneratorTestsFunction() { var either = Right(123); either.Match(Right: i => Assert.True(i == 123), Left: _ => Assert.Fail("Shouldn't get here")); var c = either.Match(Right: i => i + 1, Left: _ => 0); Assert.True(c == 124); } [Fact] public void LeftGeneratorTestsObject() { var either = ItsLeft; either.Match( Right: r => Assert.Fail("Shouldn't get here"), Left: l => Assert.True(l == "Left") ); int c = either.Match( Right: r => r + 1, Left: l => 0 ); Assert.True(c == 0); } [Fact] public void LeftGeneratorTestsFunction() { var either = ItsLeft; either.Match(Right: r => Assert.Fail("Shouldn't get here"), Left: l => Assert.True(l == "Left") ); var c = either.Match(Right: r => r + 1, Left: l => 0 ); Assert.True(c == 0); } [Fact] public void SomeLinqTest() => (from x in Two from y in Four from z in Six select x + y + z) .Match( Right: r => Assert.True(r == 12), Left: _ => Assert.Fail("Shouldn't get here")); [Fact] public void LeftLinqTest() => (from x in Two from y in Four from _ in ItsLeft from z in Six select x + y + z) .Match( l => Assert.True(l == "Left"), _ => Assert.Fail("Shouldn't get here")); [Fact] public void EitherFluentSomeNoneTest() { int res1 = GetValue(true) .Right(r => r + 10) .Left (l => l.Length); int res2 = GetValue(false) .Right(r => r + 10) .Left (l => l.Length); Assert.True(res1 == 1010); Assert.True(res2 == 4); } private Either GetValue(bool select) { if (select) { return 1000; } else { return "Left"; } } [Fact] public void EitherLinqTest1() => (from x in Right(2) from _ in Left("error") from z in Right(5) select x + z) .Match(Right: _ => Assert.Fail("Shouldn't get here"), Left: _ => Assert.True(true)); [Fact] public void EitherLinqTest2() => (from x in Right(2) from y in Right(123) from z in Right(5) select x + y + z) .Match(Right: r => Assert.True(r == 130), Left: _ => Assert.True(false)); [Fact] public void EitherCoalesce() { var x = Right(1) || Right(2) || Left("error"); } [Fact] public void EitherInfer1() => AddEithers(Right(10), Left("error")); void EitherInfer2(bool v) { Either x = v ? Right(10) : Left("error"); } public Either AddEithers(Either x, Either y) => from a in x from b in y select a + b; private Either ImplicitConversion() => 1000; private Either ItsLeft => "Left"; private Either Two => 2; private Either Four => 4; private Either Six => 6; } ================================================ FILE: LanguageExt.Tests/EnumerableTTests.cs ================================================ using System.Collections; using System.Linq; using LanguageExt.ClassInstances; using Xunit; namespace LanguageExt.Tests; public class EnumerableTTests { [Fact] public void WrappedListTest() { var lst = List(List(1, 2, 3, 4, 5), List(1, 2, 3, 4, 5), List(1, 2, 3, 4, 5)); var res = lst.KindT, int>().FoldT(0, (s, v) => s + v); var mlst = lst.KindT, int>().MapT(x => x * 2); var mres = mlst.FoldT(0, (s, v) => s + v); Assert.True(res == 45, "Expected 45 got " + res); Assert.True(mres == 90, "Expected 90 got " + res); Assert.True(lst.KindT, int>().CountT() == 15, "(lst) Expected 15 got " + lst.KindT, int>().CountT()); Assert.True(mlst.CountT() == 15, "(mlst) Expected 15 got " + mlst.CountT()); lst = List>(); res = lst.KindT, int>().FoldT(0, (s, v) => s + v); Assert.True(res == 0, "Fold results, expected 0 got " + res); Assert.True(lst.KindT, int>().CountT() == 0, "Empty count, expected 0 got " + res); } [Fact] public void ChooseTest() { var input = List( Some(1), Some(2), Some(3), None, Some(4), None, Some(5)); var actual = IterableExtensions.AsIterable(input).Choose(x => x).ToList(); var expected = List(1, 2, 3, 4, 5); var toString = fun((IEnumerable items) => string.Join(", ", items)); Assert.True(EqEnumerable.Equals(actual, expected), $"Expected {toString(expected)} but was {toString(actual)}"); } } ================================================ FILE: LanguageExt.Tests/EqualityTests.cs ================================================ using Xunit; using System; using System.Linq; using LanguageExt.Traits; using LanguageExt.ClassInstances; namespace LanguageExt.Tests; public class EqualityTests { [Fact] public void EqualityTest1() { var optional = Some(123); if (optional == 123) { Assert.True(true); } else { Assert.False(true); } } [Fact] public void EqualityTest2() { Option optional = None; if (optional == None) { Assert.True(true); } else { Assert.False(true); } } [Fact] public void EqualityTest3() { var optional = Some(123); if (optional == None) { Assert.False(true); } else { Assert.True(true); } } [Fact] public void EqualityTest4() { Option optional = None; if (optional == Some(123)) { Assert.False(true); } else { Assert.True(true); } } [Fact] public void NonEqualityTest1() { var optional = Some(123); if (optional != 123) { Assert.False(true); } else { Assert.True(true); } } [Fact] public void NonEqualityTest2() { Option optional = None; if (optional != None) { Assert.False(true); } else { Assert.True(true); } } /// /// Test for issue #64 /// It just needs to complete without throwing an exception to be tested /// https://github.com/louthy/language-ext/issues/64 /// [Fact] public void EitherEqualityComparerTest() { var results = List>(); var firsterror = results.FirstOrDefault(i => i.IsLeft); if (IsDefault(firsterror)) // <-- here i get exception { } } public static bool IsDefault(T obj) => EqDefault.Equals(obj, default); [Fact] public static void OptionMonadEqualityTests1() { var optionx = Some(123); var optiony = Some(123); var optionr = IsEqual(optionx, optiony); Assert.True(optionr); Assert.True(optionx == optiony); } [Fact] public static void OptionMonadEqualityTests2() { var optionx = Some("ABC"); var optiony = Some("abc"); var optionr = IsEqual(optionx, optiony); Assert.True(optionr); Assert.True(optionx != optiony); } [Fact] public static void EitherMonadEqualityTests1() { var optionx = Right(123); var optiony = Right(123); var optionr = IsEqual, int>(optionx, optiony); Assert.True(optionr); Assert.True(optionx == optiony); } [Fact] public static void EitherMonadEqualityTests2() { var optionx = Right("ABC"); var optiony = Right("abc"); var optionr = IsEqual, string>(optionx, optiony); Assert.True(optionr); Assert.True(optionx != optiony); } public static bool IsEqual(K mx, K my) where EqA : Eq where M : Monad, Foldable => (from x in mx from y in my select EqA.Equals(x, y)) .ForAll(x => x); } public class EqualityTestsWithStaticProperties { public class Foo : Record { public static Foo Default { get; } static Foo() => Default = new Foo( 10,20,List(1,2) ); public Foo(int age, int s, Lst numbers) { Age = age; String = s; Numbers = numbers; } public int Age { get; } public int String { get; } public Lst Numbers { get; } } [Fact] public void EqualRecordsShouldBeEqual() { var a = new Foo( 10, 20, List( 0, 1, 2 ) ); var b = new Foo( 10, 20, List( 0, 1, 2 ) ); Assert.Equal( b, a ); } [Fact] public void NonEqualRecordsShouldBeNotEqual() { var a = new Foo( 10, 20, List( 0, 1, 2 ) ); var b = new Foo( 10, 20, List( 0, -1, 2 ) ); Assert.NotEqual( b, a ); } } ================================================ FILE: LanguageExt.Tests/ErrorTests.cs ================================================ using System; using System.Runtime.Serialization; using Xunit; using LanguageExt.Common; using Newtonsoft.Json; using static LanguageExt.Prelude; namespace LanguageExt.Tests; public class ErrorTests { [Fact] public void EqualityTest() { var ea = Error.New(100, "Hello"); var eb = Error.New(100, "Hello"); Assert.True(ea == eb); } [Fact] public void NonEqualityTest() { var ea = Error.New(100, "Hello"); var eb = Error.New(100, "World"); Assert.False(ea == eb); } [Fact] public void IsTest() { var ea = Error.New(100, "Hello"); var eb = Error.New(100, "World"); Assert.True(ea.Is(ea)); Assert.True(eb.Is(eb)); Assert.True(ea.Is(eb)); Assert.True(eb.Is(ea)); } [Fact] public void IsExpectedTest() { var ea = Error.New(100, "Hello"); Assert.True(ea.IsExpected); Assert.True( ea switch { var ex when ex.IsExpected => true, var ex => false }); } [Fact] public void ExpectedSerialisationTest() { var settings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Objects }; var ea = Error.New(123, "Err"); var json = JsonConvert.SerializeObject(ea, settings); var eb = JsonConvert.DeserializeObject(json, settings); Assert.True(eb.IsExpected); Assert.False(eb.IsExceptional); Assert.True(ea == eb); Assert.True(ea.Is(eb)); Assert.True(eb.Is(ea)); } [Fact] public void ExceptionalSerialisationTest() { var settings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Objects }; var ea = Error.New(new Exception("Hello, World")); var json = JsonConvert.SerializeObject(ea, settings); var eb = JsonConvert.DeserializeObject(json, settings); Assert.False(eb.IsExpected); Assert.True(eb.IsExceptional); Assert.True(ea.Is(eb)); Assert.True (eb.Is(ea)); } public record BespokeError([property: DataMember] bool MyData) : Expected("Expected something bespoke", 100, None); [Fact] public void BespokeExpectedSerialisationTest() { var settings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Objects }; var ea = new BespokeError(true); var json = JsonConvert.SerializeObject(ea, settings); var eb = JsonConvert.DeserializeObject(json, settings); Assert.True(eb.IsExpected); Assert.False(eb.IsExceptional); Assert.True(ea == eb); Assert.True(ea.Is(eb)); Assert.True(eb.Is(ea)); } [Fact] public void ManyExpectedSerialisationTest() { var settings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Objects }; var ea = Error.New(123, "Err 1"); var eb = Error.New(345, "Err 2"); var ec = Error.New(678, "Err 3"); var json = JsonConvert.SerializeObject(ea + eb + ec, settings); var es = JsonConvert.DeserializeObject(json, settings); Assert.False(es.IsEmpty); Assert.True(es.Count == 3); Assert.True(es.IsExpected); Assert.False(eb.IsExceptional); Assert.True(es == ea + eb + ec); Assert.True(es.Is(ea + eb + ec)); Assert.True((ea + eb + ec).Is(es)); } [Fact] public void MonoidLawsTest() { var ea = Error.New(123, "Err 1"); var eb = Error.New(345, "Err 2"); var ec = Error.New(678, "Err 3"); Assert.True((ea + eb) + ec == ea + (eb + ec), "Associativity"); Assert.True(Error.Empty + ea == ea, "Left Identity"); Assert.True(ea + Error.Empty == ea, "Right Identity"); } } ================================================ FILE: LanguageExt.Tests/FSharp/FSharpTests.cs ================================================ using Xunit; namespace LanguageExt.Tests.FSharp; public class FSharpTests { [Theory] [InlineData("Error")] [InlineData("")] public void ErrorResult_to_Either(string error) { var result = Microsoft.FSharp.Core.FSharpResult.NewError(error); var either = LanguageExt.FSharp.fs(result); either.Match(Right: _ => Assert.Fail("Shouldn't get here"), Left: l => Assert.True(l == error)); } [Fact] public void OKResult_to_Either() { var result = Microsoft.FSharp.Core.FSharpResult.NewOk(123); var either = LanguageExt.FSharp.fs(result); either.Match(Right: r => Assert.True(r == 123), Left: _ => Assert.Fail("Shouldn't get here")); } [Fact] public void Either_to_ErrorResult() { var either = Either.Left("Error"); var result = LanguageExt.FSharp.fs(either); Assert.True(result.IsError); Assert.True(result.ErrorValue == "Error"); } [Fact] public void Either_to_OkResult() { var either = Either.Right(123); var result = LanguageExt.FSharp.fs(either); Assert.True(result.IsOk); Assert.True(result.ResultValue == 123); } } ================================================ FILE: LanguageExt.Tests/FunTests.cs ================================================ using System; using Xunit; namespace LanguageExt.Tests; public class FunTests { [Fact] public void LambdaInferTests() { var fn1 = fun( () => 123 ); var fn2 = fun( (int a) => 123 + a ); var fn3 = fun( (int a, int b) => 123 + a + b ); var fn4 = fun( (int a, int b, int c) => 123 + a + b + c); var fn5 = fun( (int a, int b, int c, int d) => 123 + a + b + c + d); var fn6 = fun( (int a, int b, int c, int d, int e) => 123 + a + b + c + d + e); var fn7 = fun( (int a, int b, int c, int d, int e, int f) => 123 + a + b + c + d + e + f); var fn8 = fun( (int a, int b, int c, int d, int e, int f, int g) => 123 + a + b + c + d + e + f + g); var fnac1 = fun( () => { } ); var fnac2 = fun( (int a) => Console.WriteLine(123 + a) ); var fnac3 = fun( (int a, int b) => Console.WriteLine(123 + a + b)); var fnac4 = fun( (int a, int b, int c) => Console.WriteLine(123 + a + b + c)); var fnac5 = fun( (int a, int b, int c, int d) => Console.WriteLine(123 + a + b + c + d)); var fnac6 = fun( (int a, int b, int c, int d, int e) => Console.WriteLine(123 + a + b + c + d + e)); var fnac7 = fun( (int a, int b, int c, int d, int e, int f) => Console.WriteLine(123 + a + b + c + d + e + f)); var fnac8 = fun( (int a, int b, int c, int d, int e, int f, int g) => Console.WriteLine(123 + a + b + c + d + e + f + g)); var ac1 = act(() => { }); var ac2 = act((int a) => Console.WriteLine(123 + a)); var ac3 = act((int a, int b) => Console.WriteLine(123 + a + b)); var ac4 = act((int a, int b, int c) => Console.WriteLine(123 + a + b + c)); var ac5 = act((int a, int b, int c, int d) => Console.WriteLine(123 + a + b + c + d)); var ac6 = act((int a, int b, int c, int d, int e) => Console.WriteLine(123 + a + b + c + d + e)); var ac7 = act((int a, int b, int c, int d, int e, int f) => Console.WriteLine(123 + a + b + c + d + e + f)); var ac8 = act((int a, int b, int c, int d, int e, int f, int g) => Console.WriteLine(123 + a + b + c + d + e + f + g)); } } ================================================ FILE: LanguageExt.Tests/GlobalSuppressions.cs ================================================  // This file is used by Code Analysis to maintain SuppressMessage // attributes that are applied to this project. // Project-level suppressions either have no target or are given // a specific target and scoped to a namespace, type, member, etc. ================================================ FILE: LanguageExt.Tests/GlobalUsings.cs ================================================ global using LanguageExt; global using LanguageExt.Traits; global using static LanguageExt.Prelude; ================================================ FILE: LanguageExt.Tests/HashMapTests.cs ================================================ //using LanguageExt.Trans; using static LanguageExt.HashMap; using Xunit; using System; using System.Linq; using LanguageExt.ClassInstances; namespace LanguageExt.Tests; public class HashMapTests { [Fact] public void HashMapGeneratorTest() { var m1 = HashMap(); m1 = add(m1, 100, "hello"); Assert.True(m1.Count == 1 && containsKey(m1,100)); } [Fact] public void MapGeneratorAndMatchTest() { var m2 = HashMap( (1, "a"), (2, "b"), (3, "c") ); m2 = add(m2, 100, "world"); var res = match( m2, 100, v => v, () => "failed" ); Assert.True(res == "world"); } [Fact] public void MapSetTest() { var m1 = HashMap( (1, "a"), (2, "b"), (3, "c") ); var m2 = setItem(m1, 1, "x"); match( m1, 1, Some: v => Assert.True(v == "a"), None: () => Assert.False(true) ); match( find(m2, 1), Some: v => Assert.True(v == "x"), None: () => Assert.False(true) ); } [Fact] public void MapAddInOrderTest() { var m = HashMap((1, 1)); m.Find(1).IfNone(() => failwith("Broken")); m = HashMap((1, 1), (2, 2)); m.Find(1).IfNone(() => failwith("Broken")); m.Find(2).IfNone(() => failwith("Broken")); m = HashMap((1, 1), (2, 2), (3, 3)); m.Find(1).IfNone(() => failwith("Broken")); m.Find(2).IfNone(() => failwith("Broken")); m.Find(3).IfNone(() => failwith("Broken")); m = HashMap((1, 1), (2, 2), (3, 3), (4, 4)); m.Find(1).IfNone(() => failwith("Broken")); m.Find(2).IfNone(() => failwith("Broken")); m.Find(3).IfNone(() => failwith("Broken")); m.Find(4).IfNone(() => failwith("Broken")); m = HashMap((1, 1), (2, 2), (3, 3), (4, 4), (5, 5)); m.Find(1).IfNone(() => failwith("Broken")); m.Find(2).IfNone(() => failwith("Broken")); m.Find(3).IfNone(() => failwith("Broken")); m.Find(4).IfNone(() => failwith("Broken")); m.Find(5).IfNone(() => failwith("Broken")); } [Fact] public void MapAddInReverseOrderTest() { var m = HashMap((2, 2), (1, 1)); m.Find(1).IfNone(() => failwith("Broken")); m.Find(2).IfNone(() => failwith("Broken")); m = HashMap((3, 3), (2, 2), (1, 1)); m.Find(1).IfNone(() => failwith("Broken")); m.Find(2).IfNone(() => failwith("Broken")); m.Find(3).IfNone(() => failwith("Broken")); m = HashMap((4, 4), (3, 3), (2, 2), (1, 1)); m.Find(1).IfNone(() => failwith("Broken")); m.Find(2).IfNone(() => failwith("Broken")); m.Find(3).IfNone(() => failwith("Broken")); m.Find(4).IfNone(() => failwith("Broken")); m = HashMap((5, 5), (4, 4), (3, 3), (2, 2), (1, 1)); m.Find(1).IfNone(() => failwith("Broken")); m.Find(2).IfNone(() => failwith("Broken")); m.Find(3).IfNone(() => failwith("Broken")); m.Find(4).IfNone(() => failwith("Broken")); m.Find(5).IfNone(() => failwith("Broken")); } [Fact] public void MapAddInMixedOrderTest() { var m = HashMap((5, 5), (1, 1), (3, 3), (2, 2), (4, 4)); m.Find(1).IfNone(() => failwith("Broken")); m.Find(2).IfNone(() => failwith("Broken")); m.Find(3).IfNone(() => failwith("Broken")); m.Find(4).IfNone(() => failwith("Broken")); m.Find(5).IfNone(() => failwith("Broken")); m = HashMap((1, 1), (3, 3), (5, 5), (2, 2), (4, 4)); m.Find(1).IfNone(() => failwith("Broken")); m.Find(2).IfNone(() => failwith("Broken")); m.Find(3).IfNone(() => failwith("Broken")); m.Find(4).IfNone(() => failwith("Broken")); m.Find(5).IfNone(() => failwith("Broken")); } [Fact] public void MapRemoveTest() { var m = HashMap((1, "a"), (2, "b"), (3, "c"), (4, "d"), (5, "e")); m.Find(1).IfNone(() => failwith("Broken 1")); m.Find(2).IfNone(() => failwith("Broken 2")); m.Find(3).IfNone(() => failwith("Broken 3")); m.Find(4).IfNone(() => failwith("Broken 4")); m.Find(5).IfNone(() => failwith("Broken 5")); Assert.True(m.Count == 5); m = remove(m,4); Assert.True(m.Count == 4); Assert.True(m.Find(4).IsNone); m.Find(1).IfNone(() => failwith("Broken 1")); m.Find(2).IfNone(() => failwith("Broken 2")); m.Find(3).IfNone(() => failwith("Broken 3")); m.Find(5).IfNone(() => failwith("Broken 5")); m = remove(m, 1); Assert.True(m.Count == 3); Assert.True(m.Find(1).IsNone); m.Find(2).IfNone(() => failwith("Broken 2")); m.Find(3).IfNone(() => failwith("Broken 3")); m.Find(5).IfNone(() => failwith("Broken 5")); m = remove(m, 2); Assert.True(m.Count == 2); Assert.True(m.Find(2).IsNone); m.Find(3).IfNone(() => failwith("Broken 3")); m.Find(5).IfNone(() => failwith("Broken 5")); m = remove(m, 3); Assert.True(m.Count == 1); Assert.True(m.Find(3).IsNone); m.Find(5).IfNone(() => failwith("Broken 5")); m = remove(m, 5); Assert.True(m.Count == 0); Assert.True(m.Find(5).IsNone); } [Fact] public void MassAddRemoveTest() { int max = 100000; var items = IterableExtensions.AsIterable(Range(1, max)) .Map( _ => (Key: Guid.NewGuid(), Value: Guid.NewGuid())) .ToSeq(); var m = HashMap().AddRange(items); Assert.True(m.Count == max); foreach (var item in items) { Assert.True(m.ContainsKey(item.Key)); m = m.Remove(item.Key); Assert.False(m.ContainsKey(item.Key)); max--; Assert.True(m.Count == max); } } [Fact] public void HashMapSetTest() { var map = HashMap(("one", 1), ("two",2), ("three", 3)); var map2 = map.SetItem("One", -1); Assert.Equal(3, map2.Count); Assert.Equal(-1, map2["one"]); Assert.DoesNotContain("one", map2.Keys.AsEnumerable()); // make sure key got replaced, too Assert.Contains("One", map2.Keys.AsEnumerable()); // make sure key got replaced, too Assert.Throws(() => map.SetItem("four", identity)); } [Fact] public void EqualsTest() { Assert.True(HashMap().Equals(HashMap())); Assert.False(HashMap((1, 2)).Equals(HashMap())); Assert.False(HashMap().Equals(HashMap((1, 2)))); Assert.True(HashMap((1, 2)).Equals(HashMap((1, 2)))); Assert.False(HashMap((1, 2), (3, 4)).Equals(HashMap((1, 2)))); Assert.False(HashMap((1, 2)).Equals(HashMap((1, 2), (3, 4)))); Assert.True(HashMap((1, 2), (3, 4)).Equals(HashMap((1, 2), (3, 4)))); Assert.True(HashMap((3, 4), (1, 2)).Equals(HashMap((1, 2), (3, 4)))); Assert.True(HashMap((3, 4), (1, 2)).Equals(HashMap((3, 4), (1, 2)))); } [Fact] public void FetchBack() { var init = Seq(69, 1477); var rmv = Seq(69); var map = toHashMap(init.Zip(Enumerable.Repeat(1, int.MaxValue))); Assert.True(map.ContainsKey(1477)); // false Assert.True(map.Find(1477).IsSome); // false var minus = map.RemoveRange(rmv); Assert.True(minus.Keys.Find(i => i == 1477).IsSome); // true Assert.True(minus.ContainsKey(1477)); // false Assert.True(minus.Find(1477).IsSome); // false var boom = minus[1477]; // throws } [Fact] public void HashMapRemoveTest() { var values = new[] { 1175691501, 613261927, 178639586, 745392133, 1071314707, 464997766, 746033505, 2055266377, 9321519, 2085595311 }; var items = toHashMap(values.Zip(values)); foreach(var value in values) { items = items.Remove(value); Assert.True(!items.Contains(value)); } } [Fact] public void itemLensGetShouldGetExistingValue() { var expected = "3"; var map = HashMap((1, "1"), (2, "2"), (3, "3"), (4, "4"), (5, "5")); var actual = HashMap.item(3).Get(map); Assert.Equal(expected, actual); } [Fact] public void itemLensGetShouldThrowExceptionForNonExistingValue() { Assert.Throws(() => { var map = HashMap((1, "1"), (2, "2"), (3, "3"), (4, "4"), (5, "5")); var actual = HashMap.item(10).Get(map); }); } [Fact] public void itemOrNoneLensGetShouldGetExistingValue() { var expected = "3"; var map = HashMap((1, "1"), (2, "2"), (3, "3"), (4, "4"), (5, "5")); var actual = HashMap.itemOrNone(3).Get(map); Assert.Equal(expected, actual); } [Fact] public void itemOrNoneLensGetShouldReturnNoneForNonExistingValue() { var expected = Option.None; var map = HashMap((1, "1"), (2, "2"), (3, "3"), (4, "4"), (5, "5")); var actual = HashMap.itemOrNone(10).Get(map); Assert.Equal(expected, actual); } } ================================================ FILE: LanguageExt.Tests/HashSetTests.cs ================================================ using Xunit; using System; using Newtonsoft.Json; using static LanguageExt.HashSet; using LanguageExt.ClassInstances; namespace LanguageExt.Tests; public class HashSetTests { [Fact] public void HashSetHasItemsTest() => Assert.False(HashSet(1, 2, 3).IsEmpty); [Fact] public void HashSetGeneratorTest() { var m1 = HashSet(); m1 = add(m1, "hello"); Assert.True(m1.Count == 1 && contains(m1, "hello")); } [Fact] public void HashSetGeneratorAndMatchTest() { var m2 = HashSet("a", "b", "c"); m2 = add(m2, "world"); var res = find(m2, "world").Match( v => v, () => "failed" ); Assert.True(res == "world"); } [Fact] public void HashSetAddInOrderTest() { var m = HashSet(1); m.Find(1).IfNone(() => failwith("Broken")); m = HashSet(1, 2); m.Find(1).IfNone(() => failwith("Broken")); m.Find(2).IfNone(() => failwith("Broken")); m = HashSet(1, 2, 3); m.Find(1).IfNone(() => failwith("Broken")); m.Find(2).IfNone(() => failwith("Broken")); m.Find(3).IfNone(() => failwith("Broken")); m = HashSet(1, 2, 3, 4); m.Find(1).IfNone(() => failwith("Broken")); m.Find(2).IfNone(() => failwith("Broken")); m.Find(3).IfNone(() => failwith("Broken")); m.Find(4).IfNone(() => failwith("Broken")); m = HashSet(1, 2, 3, 4, 5); m.Find(1).IfNone(() => failwith("Broken")); m.Find(2).IfNone(() => failwith("Broken")); m.Find(3).IfNone(() => failwith("Broken")); m.Find(4).IfNone(() => failwith("Broken")); m.Find(5).IfNone(() => failwith("Broken")); } [Fact] public void HashSetAddInReverseOrderTest() { var m = HashSet(1); m.Find(1).IfNone(() => failwith("Broken")); m = HashSet(2, 1); m.Find(1).IfNone(() => failwith("Broken")); m.Find(2).IfNone(() => failwith("Broken")); m = HashSet(3, 2, 1); m.Find(1).IfNone(() => failwith("Broken")); m.Find(2).IfNone(() => failwith("Broken")); m.Find(3).IfNone(() => failwith("Broken")); m = HashSet(4, 3, 2, 1); m.Find(1).IfNone(() => failwith("Broken")); m.Find(2).IfNone(() => failwith("Broken")); m.Find(3).IfNone(() => failwith("Broken")); m.Find(4).IfNone(() => failwith("Broken")); m = HashSet(5, 4, 3, 2, 1); m.Find(1).IfNone(() => failwith("Broken")); m.Find(2).IfNone(() => failwith("Broken")); m.Find(3).IfNone(() => failwith("Broken")); m.Find(4).IfNone(() => failwith("Broken")); m.Find(5).IfNone(() => failwith("Broken")); } [Fact] public void HashSetAddInMixedOrderTest() { var m = HashSet(5, 1, 3, 2, 4); m.Find(1).IfNone(() => failwith("Broken")); m.Find(2).IfNone(() => failwith("Broken")); m.Find(3).IfNone(() => failwith("Broken")); m.Find(4).IfNone(() => failwith("Broken")); m.Find(5).IfNone(() => failwith("Broken")); m = HashSet(1, 3, 5, 2, 4); m.Find(1).IfNone(() => failwith("Broken")); m.Find(2).IfNone(() => failwith("Broken")); m.Find(3).IfNone(() => failwith("Broken")); m.Find(4).IfNone(() => failwith("Broken")); m.Find(5).IfNone(() => failwith("Broken")); } [Fact] public void HashSetRemoveTest() { var m = HashSet("a", "b", "c", "d", "e"); m.Find("a").IfNone(() => failwith("Broken 1")); m.Find("b").IfNone(() => failwith("Broken 2")); m.Find("c").IfNone(() => failwith("Broken 3")); m.Find("d").IfNone(() => failwith("Broken 4")); m.Find("e").IfNone(() => failwith("Broken 5")); Assert.True(m.Count == 5); m = remove(m, "d"); Assert.True(m.Count == 4); Assert.True(m.Find("d").IsNone); m.Find("a").IfNone(() => failwith("Broken 1")); m.Find("b").IfNone(() => failwith("Broken 2")); m.Find("c").IfNone(() => failwith("Broken 3")); m.Find("e").IfNone(() => failwith("Broken 5")); m = remove(m, "a"); Assert.True(m.Count == 3); Assert.True(m.Find("a").IsNone); m.Find("b").IfNone(() => failwith("Broken 2")); m.Find("c").IfNone(() => failwith("Broken 3")); m.Find("e").IfNone(() => failwith("Broken 5")); m = remove(m, "b"); Assert.True(m.Count == 2); Assert.True(m.Find("b").IsNone); m.Find("c").IfNone(() => failwith("Broken 3")); m.Find("e").IfNone(() => failwith("Broken 5")); m = remove(m, "c"); Assert.True(m.Count == 1); Assert.True(m.Find("c").IsNone); m.Find("e").IfNone(() => failwith("Broken 5")); m = remove(m, "e"); Assert.True(m.Count == 0); Assert.True(m.Find("e").IsNone); } [Fact] public void HashSetKeyTypeTests() { var set = HashSet("one", "two", "three"); Assert.True(set.Contains("one")); Assert.True(set.Contains("ONE")); Assert.True(set.Contains("two")); Assert.True(set.Contains("Two")); Assert.True(set.Contains("three")); Assert.True(set.Contains("thREE")); } [Fact] public void HashSetSetTest() { var set = HashSet("one", "two", "three"); var set2 = set.SetItem("One"); Assert.Equal(3, set2.Count); Assert.False(set2.ToSeq().Contains("one")); Assert.True(set2.ToSeq().Contains("One")); Assert.Throws(() => set.SetItem("four")); } [Fact] public void EqualsTest() { Assert.False(HashSet(1, 2, 3).Equals(HashSet())); Assert.False(HashSet().Equals(HashSet(1, 2, 3))); Assert.True(HashSet().Equals(HashSet())); Assert.True(HashSet(1).Equals(HashSet(1))); Assert.True(HashSet(1, 2).Equals(HashSet(1, 2))); Assert.False(HashSet(1, 2).Equals(HashSet(1, 2, 3))); Assert.False(HashSet(1, 2, 3).Equals(HashSet(1, 2))); } [Fact] public void HashSet_WithDefaultSettings_SerializationTest() { var source = HashSet(123, 456); var json = JsonConvert.SerializeObject(source); var result = JsonConvert.DeserializeObject>(json); Assert.Equal(source, result); } [Fact] public void HashSet_WithEchoCustomSettings_SerializationTest() { var settings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All, TypeNameAssemblyFormatHandling = TypeNameAssemblyFormatHandling.Simple, MissingMemberHandling = MissingMemberHandling.Ignore }; var source = HashSet(123, 456); var json = JsonConvert.SerializeObject(source, settings); var result = JsonConvert.DeserializeObject>(json, settings); Assert.Equal(source, result); } } ================================================ FILE: LanguageExt.Tests/IOTests/ApplyTests.cs ================================================ using System; using System.Threading.Tasks; using LanguageExt.Common; using Xunit; namespace LanguageExt.Tests.IOTests; public class IO_ApplyTests { [Fact] public void Apply_ShouldApplyFunctionToValue() { // Arrange var ioValue = IO.pure(5); // IO computation with a value of 5 var ioFunction = IO.pure((int x) => x * 2); // IO computation with a function (x => x * 2) // Act var appliedIO = ioFunction.Apply(ioValue); // Apply function to value var result = appliedIO.Run(); // Perform the computation to get the result // Assert Assert.Equal(10, result); // Ensure the function application happened correctly } [Fact] public void Apply_ShouldBubbleUpFailure() { // Arrange var failError = Error.New("Failed IO"); var failedIO = IO.fail(failError); // Failing IO computation var ioFunction = IO.pure((int x) => x * 2); // IO computation with a function // Act & Assert // Since the IO computation failed, the result must throw the expected error var ex = Assert.ThrowsAny(() => ioFunction.Apply(failedIO).Run()); Assert.Equal(failError, ex.ToError()); // Check that the bubbled-up error matches } [Fact] public void Apply_ShouldNotRunFunctionIfFunctionIsFailed() { // Arrange var failError = Error.New("Failed Function"); var ioValue = IO.pure(5); var failedFunction = IO.fail>(failError); // Act & Assert // The failed function should result in a failure even if the value is valid var ex = Assert.ThrowsAny(() => failedFunction.Apply(ioValue).Run()); Assert.Equal(failError, ex.ToError()); // Check the error matches the failed function's error } [Fact] public async Task Apply_WithAsyncAndSyncTermsShouldRunInParallel() { var af = Atom(0); var aa = Atom(0); // Create a task that takes longer to return than `fa`. This relies on `fa` completing first // so that aa.Value has been set. This proves that the applicative parallel behaviour is working var ff = IO.liftAsync(() => Task.Delay(100) .ToUnit() .Map(_ => af.Swap(x => x + aa.Value)) .Map(_ => new Func(x => x * af.Value))); var fa = IO.liftAsync(() => Task.Delay(10) .ToUnit() .Map(_ => aa.Swap(x => x + 5))); var fr = ff.Apply(fa); var r = await fr.RunAsync(); Assert.Equal(25, r); } [Fact] public async Task Apply_WithAsyncAndSyncTermsShouldApplyFunctionToValue() { var af = Atom(0); var aa = Atom(0); // Create a task that takes longer to return than `fa`. This relies on `fa` completing first // so that aa.Value has been set. This proves that the applicative parallel behaviour is working var ff = IO.liftAsync(() => Task.Delay(100) .ToUnit() .Map(_ => af.Swap(x => x + aa.Value)) .Map(_ => new Func(x => x * af.Value))); var fa = IO.lift(() => aa.Swap(x => x + 5)); var fr = ff.Apply(fa); var r = await fr.RunAsync(); Assert.Equal(25, r); } [Fact] public async Task Apply_WithSyncAndAsyncTermsShouldApplyFunctionToValue() { var ff = IO.liftAsync(() => Task.Delay(100) .ToUnit() .Map(_ => new Func(x => x * 5))); var fa = IO.pure(5); var fr = ff.Apply(fa); var r = await fr.RunAsync(); Assert.Equal(25, r); } } ================================================ FILE: LanguageExt.Tests/IOTests/FoldTests.cs ================================================ using System; using Xunit; using LanguageExt.Common; namespace LanguageExt.Tests.IOTests; public class IOFoldTests { [Fact] public void Fold_AccumulatesStateAccurately() { // Arrange var computation = IO.pure(10); // Successful IO returning 10 var initialState = 0; Func sumFolder = (state, value) => state + value; // Act var foldedIO = computation.Fold(Schedule.Never, initialState, sumFolder).Run(); // Assert Assert.Equal(10, foldedIO); } [Fact] public void Fold_InitialStateIsReturnedIfNoEffect() { // Arrange var computation = IO.Empty; // No effect in the IO var initialState = 5; Func sumFolder = (state, value) => state + value; // Assert Assert.ThrowsAny(() => computation.Fold(Schedule.Never, initialState, sumFolder).Run()); } [Fact] public void Fold_ComputationFailurePreservesState() { // Arrange var computation = IO.fail(Error.New("Error occurred")); // Simulating a failure var initialState = 5; Func sumFolder = (state, value) => state + value; // Assert Assert.ThrowsAny(() => computation.Fold(Schedule.Never, initialState, sumFolder).Run()); } [Fact] public void Fold_ComplexTransformationAppliedCorrectly() { // Arrange var computation = IO.pure(7); // Successful IO var initialState = "Start: "; Func folder = (state, value) => state + value; // Act var foldedIO = computation.Fold(Schedule.Never, initialState, folder).Run(); // Assert Assert.Equal("Start: 7", foldedIO); // State should reflect the transformation } [Fact] public void FoldUntil_StopsOnMatchingPredicate() { // Arrange var computation = IO.pure(10); // IO succeeds with result 10 var initialState = 0; Func sumFolder = (state, value) => state + value; Func predicate = state => state > 5; // Stop folding if state > 5 // Act var foldedIO = computation.FoldUntil(initialState, sumFolder, stateIs: predicate).Run(); // Assert Assert.Equal(10, foldedIO); // Predicate stops calculation } [Fact] public void FoldWhile_StatePredicatesEnforcedCorrectly() { // Arrange var computation = IO.pure(20); var initialState = 5; Func folder = (state, value) => state + value; Func continueWhile = state => state < 30; // Continue only if state < 30 // Act var result = computation.FoldWhile(initialState, folder, stateIs: continueWhile).Run(); // Assert Assert.Equal(45, result); // Folding happens as state < 30 } [Fact] public void FoldWhile_StopsWhenStateConditionIsNotMet() { // Arrange var computation = IO.pure(50); var initialState = 10; Func folder = (state, value) => state + value; Func continuePredicate = state => state < 30; // Stop if state >= 30 // Act var foldedIO = computation.FoldWhile(initialState, folder, stateIs: continuePredicate).Run(); // Assert Assert.Equal(60, foldedIO); // Predicate stops further updates } [Fact] public void FoldWhile_HandlesEmptyCorrectly() { // Arrange var computation = IO.empty(); // No effect occurs var initialState = 10; Func folder = (state, value) => state + value; Func continuePredicate = state => state < 30; // Assert Assert.ThrowsAny(() => computation.FoldWhile(initialState, folder, stateIs: continuePredicate).Run()); } [Fact] public void FoldUntil_CompletesOnConditionMet() { // Arrange var computation = IO.pure(10); var initialState = 5; Func folder = (state, value) => state + value; Func stopCondition = state => state >= 15; // Stop if >= 15 // Act var foldedResult = computation.FoldUntil(Schedule.Forever, initialState, folder, stateIs: stopCondition).Run(); // Assert Assert.Equal(15, foldedResult); // Stops just as condition is met } [Fact] public void FoldUntil_HandlesErrorsWithoutStateChange() { // Arrange var computation = IO.fail(Error.New("An error")); var initialState = 42; Func folder = (state, value) => state - value; // Just a demonstration Func stopCondition = state => state < 0; // Assert Assert.ThrowsAny(() => computation.FoldUntil(initialState, folder, stateIs: stopCondition).Run()); } } ================================================ FILE: LanguageExt.Tests/IOTests/GeneralTests.cs ================================================ using LanguageExt.Common; using System; using System.Threading.Tasks; using Xunit; namespace LanguageExt.Tests.IOTests; public class IO_GeneralTests { [Fact] public void Pure_ShouldReturnCorrectValue() { // Arrange var value = 42; var io = IO.pure(value); // Act var result = io.Run(); // Assert Assert.Equal(value, result); } [Fact] public void Fail_ShouldThrowError() { // Arrange var error = Error.New("Test error"); var io = IO.fail(error); // Act & Assert var ex = Assert.ThrowsAny(() => io.Run()); Assert.Equal(error, ex.ToError()); } [Fact] public void Map_ShouldTransformValue() { // Arrange var io = IO.pure(5); // Act var result = io.Map(x => x * 2).Run(); // Assert Assert.Equal(10, result); } [Fact] public void Bind_ShouldChainComputations() { // Arrange var io = IO.pure(10); // Act var result = io.Bind(x => IO.pure(x + 20)).Run(); // Assert Assert.Equal(30, result); } [Fact] public void Catch_ShouldHandleErrorsGracefully() { // Arrange var io = IO.fail(Error.New("Test error")); // Act var result = io.IfFail(42).Run(); // Assert Assert.Equal(42, result); } [Fact] public void Select_ShouldMapValue_WhenUsingLINQ() { // Arrange var io = IO.pure(4); // Act var result = from x in io select x * 2; // Assert Assert.Equal(8, result.Run()); } [Fact] public void SelectMany_ShouldChainOperations_WhenUsingLINQ() { // Arrange var io1 = IO.pure(5); var io2 = IO.pure(3); // Act var result = from x in io1 from y in io2 select x + y; // Assert Assert.Equal(8, result.Run()); } [Fact] public void Retry_ShouldRetryOnFailure_AndSucceedEventually() { // Arrange var count = 0; var io = IO.lift(() => { if (count++ < 2) throw new Exception("Retry test"); return 42; }); // Act var result = io.Retry().Run(); // Assert Assert.Equal(42, result); Assert.Equal(3, count); // 2 failures + 1 success } [Fact] public void Repeat_ShouldRepeatUntilConditionMet() { // Arrange var counter = 0; var io = IO.lift(() => ++counter); // Act var result = io.RepeatUntil(x => x == 5).Run(); // Assert Assert.Equal(5, result); Assert.Equal(5, counter); // IO should repeat until the predicate is satisfied } [Fact] public void Timeout_ShouldThrowException_WhenTimeLimitExceeded() { // Arrange var io = IO.lift(() => { Task.Delay(1000).Wait(); return 42; }); // Act & Assert Assert.ThrowsAny(() => io.Timeout(TimeSpan.FromMilliseconds(100)).Run()); } } ================================================ FILE: LanguageExt.Tests/IOTests/MapFailTests.cs ================================================ using System; using Xunit; using LanguageExt.Common; namespace LanguageExt.Tests.IOTests; public class IO_MapFailTests { [Fact] public void MapFail_ShouldNotAlterSuccessValue() { // Arrange var ioValue = IO.pure(10); // Successful computation Func errorMapper = error => Error.New("Mapped Error"); // Transformation function (not used here) // Act var result = ioValue.MapFail(errorMapper).Run(); // Run the computation // Assert Assert.Equal(10, result); // Ensure the success value is unchanged } [Fact] public void MapFail_ShouldTransformFailure() { // Arrange var initialError = Error.New("Initial Error"); var failedIO = IO.fail(initialError); // Failing computation // Transformation function to map the error Func errorMapper = error => Error.New($"{error.Message} -> Mapped Error"); // Act & Assert var ex = Assert.ThrowsAny(() => failedIO.MapFail(errorMapper).Run()); // Assert Assert.NotNull(ex); Assert.Equal("Initial Error -> Mapped Error", ex.ToError().Message); // Verify the error was transformed } [Fact] public void MapFail_ShouldHandleNestedErrors() { // Arrange var nestedError = Error.New("Nested Error"); var failedIO = IO.fail(nestedError); // Transformation function that appends extra context to the error Func errorMapper = error => Error.New($"{error.Message}, with additional info"); // Act & Assert var ex = Assert.ThrowsAny(() => failedIO.MapFail(errorMapper).Run()); // Assert Assert.NotNull(ex); Assert.Equal("Nested Error, with additional info", ex.ToError().Message); // Verify error transformation } [Fact] public void MapFail_ShouldNotSwallowExceptionsOnMappingFailure() { // Arrange var initialError = Error.New("Original Error"); var failedIO = IO.fail(initialError); // Failing computation // Transformation function that throws an exception Func errorMapper = error => throw new InvalidOperationException("Transformation failed"); // Act & Assert var ex = Assert.Throws(() => failedIO.MapFail(errorMapper).Run()); // Assert Assert.Equal("Transformation failed", ex.Message); // Verify the exception from the mapping function } } ================================================ FILE: LanguageExt.Tests/IOTests/RepeatTests.cs ================================================ namespace LanguageExt.Tests.IOTests; using Xunit; using LanguageExt; public class IO_RepeatTests { IO CreateIncrementingIO(Atom callCount) { callCount.Swap(_ => 0); return IO.lift(() => callCount.Swap(x => x + 1)); } [Fact] public void RepeatWhile_ShouldRepeatUntilConditionFails() { // Arrange var callCount = Atom(0); var io = CreateIncrementingIO(callCount); // Act var repeatedIo = io.RepeatWhile(value => value < 5); var result = repeatedIo.Run(); // Assert Assert.Equal(5, result); // The final value when the condition `value < 5` is no longer true Assert.Equal(5, callCount); // Ensure the IO ran 5 times } [Fact] public void RepeatUntil_ShouldStopWhenConditionIsMet() { // Arrange var callCount = Atom(0); var io = CreateIncrementingIO(callCount); // Act var repeatedIo = io.RepeatUntil(value => value >= 5); var result = repeatedIo.Run(); // Assert Assert.Equal(5, result); // The value at which the repetition stops (condition met) Assert.Equal(5, callCount); // Ensure the IO ran 5 times } [Fact] public void RepeatWhile_WithTimeSeriesSchedule_ShouldRepeatWhileConditionIsMet() { // Arrange var callCount = Atom(0); var io = CreateIncrementingIO(callCount); var schedule = Schedule.TimeSeries(new Duration(10), new Duration(15), new Duration(20)); // Act var repeatedIo = io.RepeatWhile(schedule, value => value < 5); var result = repeatedIo.Run(); // Assert Assert.Equal(4, result); // The final value when the condition `value < 5` is no longer true Assert.Equal(4, callCount); // Make sure the IO executed 4 times } [Fact] public void RepeatUntil_WithForeverSchedule_ShouldRepeatUntilConditionIsMet() { // Arrange var callCount = Atom(0); var io = CreateIncrementingIO(callCount); var schedule = Schedule.Forever; // Act var repeatedIo = io.RepeatUntil(schedule, value => value >= 5); var result = repeatedIo.Run(); // Assert Assert.Equal(5, result); // The value at which the repetitions stop Assert.Equal(5, callCount); // Ensure the IO ran exactly 5 times } [Fact] public void RepeatWhile_FixedIntervalSchedule_ShouldFollowScheduleDelays() { // Arrange var callCount = Atom(0); var io = CreateIncrementingIO(callCount); var schedule = Schedule.fixedInterval(new Duration(1000)); // Act var repeatedIo = io.RepeatWhile(schedule, value => value < 3); var result = repeatedIo.Run(); // Assert Assert.Equal(3, result); // Stops after the value reaches 3 Assert.Equal(3, callCount); // Ensure the IO executed 3 times } } ================================================ FILE: LanguageExt.Tests/IOTests/RetryTests.cs ================================================ using System; using System.Threading.Tasks; using Xunit; namespace LanguageExt.Tests.IOTests; public class IO_RetryTests { [Fact] public void Retry_SuccessOnFirstAttempt_ShouldReturnResultImmediately() { var io = IO.lift(() => 42); var schedule = Schedule.Never; var retryableIO = io.Retry(schedule); var result = retryableIO.Run(); Assert.Equal(42, result); } [Fact] public void Retry_FailingThenSuccess_ShouldRetryAndReturnSuccess() { var attempt = 0; var io = IO.lift(() => { attempt++; if (attempt < 3) throw new Exception($"Failure {attempt}"); return 42; }); var schedule = Schedule.recurs(2); var retryableIO = io.Retry(schedule); var result = retryableIO.Run(); Assert.Equal(42, result); Assert.Equal(3, attempt); } [Fact] public void Retry_ExceedsMaxRetry_ShouldThrowLastError() { var io = IO.lift(() => throw new Exception("Persistent failure")); var schedule = Schedule.recurs(2); var retryableIO = io.Retry(schedule); Assert.ThrowsAny(() => retryableIO.Run()); } [Fact] public void RetryWhile_ShouldContinueWhilePredicateIsTrue() { var attempt = 0; var io = IO.lift(() => { attempt++; throw new Exception($"Failure {attempt}"); }); var retryableIO = io.RetryWhile(error => error.Message.Contains("Failure 1") || error.Message.Contains("Failure 2")); Assert.ThrowsAny(() => retryableIO.Run()); Assert.Equal(3, attempt); } [Fact] public void RetryUntil_ShouldStopWhenPredicateReturnsTrue() { var attempt = 0; var io = IO.lift(() => { attempt++; throw new Exception($"Failure {attempt}"); }); var retryableIO = io.RetryUntil(error => error.Message.Contains("Failure 2")); Assert.ThrowsAny(() => retryableIO.Run()); Assert.Equal(2, attempt); } [Fact] public void RetryWithSchedule_Spaced_ShouldRespectIntervalsAndStopAfterScheduleExpires() { var attempt = 0; var io = IO.lift(() => { attempt++; throw new Exception($"Failure {attempt}"); }); var schedule = Schedule.spaced(new Duration(100)).Take(3); var retryableIO = io.Retry(schedule); Assert.ThrowsAny(() => retryableIO.Run()); Assert.Equal(4, attempt); } [Fact] public void RetryWithSchedule_ExponentialBackoff_ShouldRetryWithIncreasingIntervals() { var attempt = 0; var io = IO.lift(() => { attempt++; throw new Exception($"Failure {attempt}"); }); var schedule = Schedule.exponential(new Duration(100)).Take(3); var retryableIO = io.Retry(schedule); Assert.ThrowsAny(() => retryableIO.Run()); Assert.Equal(4, attempt); } } ================================================ FILE: LanguageExt.Tests/IOTests/TailRecursionTests.cs ================================================ using System; using Xunit; namespace LanguageExt.Tests.IOTests; public class TailRecursionTests { private record TestIO(IO io) : K { public A Run() => io.Run(); }; private class TestIO : Deriving.MonadIO { public static K Transform(K fa) => ((TestIO)fa).io; public static K CoTransform(K fa) => new TestIO(fa.As()); } private readonly Atom State = Atom(0); [Fact] public void TailRecursionInApplication_WhenPlainIO_ShouldNotThrow() { IO innerLoop(int remaining) => from _1 in increment() from _2 in tail(when(remaining > 0, innerLoop(remaining - 1)).As()) select unit; var app = from _1 in reset() from _2 in innerLoop(3) from result in multiply() select result; var actual = app.Run(); Assert.Equal(40, actual); } private IO reset() => State.SwapIO(_ => 0); private IO increment() => State.SwapIO(x => x + 1); private IO multiply() => State.SwapIO(x => x * 10); [Fact] public void TailRecursionInApplication_WhenIOHKT_ShouldNotThrow() { IO innerLoop(int remaining) => from _1 in increment() from _2 in tail(when(remaining > 0, innerLoop(remaining - 1)).As()).Kind() select unit; var app = from _1 in reset() from _2 in innerLoop(3) from result in multiply() select result; var actual = app.Run(); Assert.Equal(40, actual); } [Fact] public void TailRecursionInApplication_WhenLiftIO_ShouldNotThrow() { TestIO innerLoop(int remaining) => (TestIO) from _1 in new TestIO(increment()) from _2 in tail(when(remaining > 0, innerLoop(remaining - 1).io).As()) select unit; var app = (TestIO) from _1 in reset() from _2 in innerLoop(3) from result in multiply() select result; var actual = app.Run(); Assert.Equal(40, actual); } [Fact] public void TailRecursionUsedImproperly_WhenPlainIO_ShouldThrow() { IO loop(int remaining) => from _ in unitIO from _1 in tail(when(remaining > 0, loop(remaining - 1)).As()) from _2 in unitIO select unit; Assert.Throws(() => loop(3).Run()); } [Fact] public void TailRecursionUsedImproperly_WhenIOHKT_ShouldThrow() { IO loop(int remaining) => from _ in unitIO from _1 in tail(when(remaining > 0, loop(remaining - 1)).As()).Kind() from _2 in unitIO select unit; Assert.Throws(() => loop(3).Run()); } [Fact] public void TailRecursionUsedImproperly_WhenLiftIO_ShouldThrow() { TestIO loop(int remaining) => (TestIO) from _ in new TestIO(unitIO) from _1 in tail(when(remaining > 0, loop(remaining - 1).io).As()) from _2 in unitIO select unit; Assert.Throws(() => loop(3).Run()); } } ================================================ FILE: LanguageExt.Tests/IssuesTests.cs ================================================ using Xunit; using System; using System.Linq; using Newtonsoft.Json; using System.Threading.Tasks; using System.Runtime.Serialization; using LanguageExt.Common; using static LanguageExt.Prelude; namespace LanguageExt.Tests { public class IssuesTests { /// /// https://github.com/louthy/language-ext/issues/207 /// public void Issue207() => Initialization .Bind(createUserMapping) .Bind(addUser) .Run(); public EitherT Issue207_2() => from us in Initialization from mu in createUserMapping(us) from id in addUser(mu) select id; static EitherT Initialization => EitherT.Right(new ADUser("test user")); static Either createUserMapping(ADUser user) => Right(new UserMapping(user + " mapped")); static EitherT addUser(UserMapping user) => EitherT.Right(user.ToString().Length); static IO addUser2(UserMapping user) => IO.lift(() => user.ToString().Length); static IO createUserMapping2(ADUser user) => IO.lift(() => new UserMapping(user + " mapped")); public IO Issue207_5() => from us in IO.lift(() => throw new Exception("fail")) from mu in createUserMapping2(us) from id in addUser2(mu) select id; //https://github.com/louthy/language-ext/issues/242 [Fact] public void Issue208() { var r = from a in Left(new Exception("error 1")) from b in Right(1) select a + b; } [Fact] public void Issue346() { var list = 1.Cons().ToList(); } [Fact] public void Issue675() { var l1 = List(1, 2, 3); var l2 = List(4, 5, 6); var a = l1.AddRange(l2); // Count 6, [1,2,3,4,5,6] var b = l1.AddRange(l2); // Count 5, [1,2,4,5,6] var c = l1.AddRange(l2); // Count 8, [1,2,4,5,6,4,5,6] var d = l1.AddRange(l2); // Count 7, [1,2,4,5,4,5,6] var e = l1.AddRange(l2); // Count 6, [1,2,4,4,5,6] Assert.True(a == b); Assert.True(a == c); Assert.True(a == d); Assert.True(a == e); } } public record ADUser(string u); public record UserMapping(string u); } // https://github.com/louthy/language-ext/issues/245 public class TopHatTests { public class TopHat : Record { public TopHat(int id, Option id2) { Id = id; Id2 = id2; } TopHat(SerializationInfo info, StreamingContext context) : base(info, context) { } public int Id { get; set; } public Option Id2 { get; set; } } OptionT SumOptionAsync() => liftIO(async _ => { var first = await Task.FromResult(1); var second = await Task.FromResult(2); return first + second; }); [Fact] public void TopHatSerialisationTest() { var t1 = new TopHat(1, 1416); var t3 = new TopHat(1, 1413); var str = JsonConvert.SerializeObject(t1); var t2 = JsonConvert.DeserializeObject(str); Assert.True(t2 == t1); Assert.True(t3 != t1); } } //https://github.com/louthy/language-ext/issues/242 namespace Core.Tests { using static ExternalSystem; public class ExternalOptionsAndEithersTests { [Fact] public void what_i_desire_EitherAsync() { EitherT GetPixelE(PixelId id) => GetPixel(id).ToEither(new Error("pixel not found")); var program = from pixel in GetPixelE(new PixelId("wkrp")) from id in GenerateLinkId(pixel.Value) from resource in ScrapeUrl("http://google.com") select resource; program.Match( Right: _ => Assert.Fail("this should not pass"), Left: e => Assert.Equal("pixel not found", e.Value) ); } } static class ExternalSystem { public record Error(string Value); public static OptionT GetPixel(PixelId id) => OptionT.None; public static EitherT GenerateLinkId(PixelId pixelId) => Right($"{pixelId}-1234"); public static EitherT ScrapeUrl(string url) => Right(new WebResource(200)); public record WebResource(int Value); public record PixelId(string Value); public record Pixel(PixelId Value); } } namespace Issues { public record CollectorId(int Value); public record TenantId(int Value); public record UserId(int Value); public record Instant(int Value); public class Collector : Record, ISerializable { public CollectorId Id { get; } public string Name { get; } public TenantId CurrentTenant { get; } public UserId AssignedBy { get; } public Instant InstantAssigned { get; } public Collector(CollectorId id, string name, TenantId tenant, UserId assignedBy, Instant dateAssigned) { Id = id; Name = name; CurrentTenant = tenant; AssignedBy = assignedBy; InstantAssigned = dateAssigned; } Collector(SerializationInfo info, StreamingContext ctx) : base(info, ctx) { } } public class GitterTests { [Fact] public void TestSerial() { var x = new Collector(new CollectorId(1), "nick", new TenantId(2), new UserId(3), new Instant(4)); var y = new Collector(new CollectorId(1), "nick", new TenantId(2), new UserId(3), new Instant(4)); var z1 = x == y; var z2 = x.Equals(y); var z3 = x.Equals((object)y); var r = JsonConvert.SerializeObject(x); var r2 = JsonConvert.DeserializeObject(r); } } public static class Issue251 { public record Error(string Value); public class ErrorException(Error error) : Exception(error.Value) { public readonly Error Error = error; } public static OptionT AsOptionT(this Either> ma) => ma.Match( Right: liftIO, Left: e => lift(() => throw new ErrorException(e))); public static OptionT AsTryOption(this Either> ma) => ma.Match( Right: liftIO, Left: e => lift(() => throw new ErrorException(e))); public static OptionT AsOptionT(this Task>> ma) => ma.Map(either => either.AsTryOption()).Flatten(); public static Error AsError(this Exception ex) => ex is ErrorException err ? err.Error : new Error(ex.Message); } public class Issue263 { public readonly Func fire = i => unit; public void Test() => ignore(act(fire)); } public class Issue261 { [Fact] public void Test1() { var ma = Writer.pure, int>(100); var mb = Writer.pure, int>(200); var mc = from x in ma from y in mb from _1 in tell(Seq("Hello")) from _2 in tell(Seq("World")) from _3 in tell(Seq($"the result is {x + y}")) select x + y; var r = mc.Run(); Assert.True(r.Value == 300); Assert.True(r.Output == Seq("Hello", "World", "the result is 300")); } [Fact] public void Test2() { var ma = Writer.pure, int>(100); var mb = Writer.pure, int>(200); var mc = from x in ma from y in mb from _1 in tell(List("Hello")) from _2 in tell(List("World")) from _3 in tell(List($"the result is {x + y}")) select x + y; var r = mc.Run(); Assert.True(r.Value == 300); Assert.True(r.Output == List("Hello", "World", "the result is 300")); } [Fact] public void Test3() { var ma = (100, Seq()); var mb = (200, Seq()); var mc = from x in Writer.write(ma) from y in Writer.write(mb) from _1 in tell(Seq("Hello")) from _2 in tell(Seq("World")) from _3 in tell(Seq($"the result is {x + y}")) select x + y; var r = mc.Run(); Assert.True(r.Value == 300); Assert.True(r.Output == Seq("Hello", "World", "the result is 300")); } } public class Issue376 { public static EitherT Op1() => Pure(1); public static EitherT Op2() => IO.pure(2); public static EitherT Op3() => Fail("error"); public static EitherT Calculate(int x, int y, int z) => Pure(x + y + z); public static int Test() => (from x in Op1() from y in Op2() from z in Op3() from w in Calculate(x, y, z) select w) .IfLeft(0) .As().Run(); } public class Issue376_2 { static async Task> Op1() => await 1.AsTask(); static async Task> Op2() => await 2.AsTask(); static async Task> Op3() => await "error".AsTask(); static async Task> Calculate(int x, int y, int z) => await Task.FromResult(x + y + z); public static IO> Test() => (from x in Op1().ToIO() from y in Op2().ToIO() from z in Op3().ToIO() from w in Calculate(x, y, z).ToIO() select w) .Run().As(); } public class Issue376_3 { static async Task> Op1() => await 1.AsTask(); static async Task> Op2() => await 2.AsTask(); static async Task> Op3() => await Option.None.AsTask(); static async Task> Calculate(int x, int y, int z) => await Task.FromResult(x + y + z); public static IO> Test() => (from x in Op1().ToIO() from y in Op2().ToIO() from z in Op3().ToIO() from w in Calculate(x, y, z).ToIO() select w).Run().As(); } public class Issue533 { [Fact] public void Test() { var someData = Enumerable .Range(0, 30000) .Select(_ => Guid.NewGuid().ToString()) .ToArray(); var result = someData .Select(Some) .AsIterable() .Traverse(x => x) .Map(x => x.ToArray()) .As(); } } // https://stackoverflow.com/questions/54609459/languageext-eitherasyn-with-aggegrate-bind-with-validation public class StackOverflow_54609459 { public class Error { } public class HostResponse { } public class Response { } public class Command { public readonly string Name; static Either>> GetCommand( Map>> commandMap, Command hostCommand) => commandMap.Find(hostCommand.Name) .ToEither(new Error()); internal static EitherT ExecuteCommand( Func> command, Command cmd) => command(cmd.Name); static Either Validate( Map>> commandMap, Command hostCommand) => commandMap.Find(hostCommand.Name) .Map(_ => unit) .ToEither(new Error()); public static EitherT> ExecuteAllAsync( Map>> commandMap, Seq hostCommands) => hostCommands.Map(cmd => from _ in Validate(commandMap, cmd).ToIO() from f in GetCommand(commandMap, cmd).ToIO() from r in ExecuteCommand(f, cmd) select r) .Traverse(x => x).As(); } } public class Issue1340 { public sealed record CustomExpected(string Message, int Code, string Another) : Expected(Message, Code); [Fact] public void Test() { // Arrange var expected = new CustomExpected("Name", 100, "This is loss"); Eff effect = liftEff(() => expected); // Act Fin fin = effect.Run(); // Assert fin.Match(_ => Assert.True(false), error => { Assert.Equal(error.Code, expected.Code); Assert.Equal(error.Message, expected.Message); var fail = (CustomExpected)error; Assert.Equal(fail.Another, expected.Another); }); } } } ================================================ FILE: LanguageExt.Tests/IteratorTests/IteratorTests.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Xunit; namespace LanguageExt.Tests.IteratorTests; public class IteratorTests { [Fact] public void Iterator_BasicBehavior_Test() { // Arrange var numbers = new List { 1, 2, 3, 4 }; var iterator = numbers.GetIterator(); // Act and Assert Assert.False(iterator.IsEmpty); // Iterator should not be empty Assert.Equal(1, iterator.Head); // Verify the first element (Head) Assert.Equal(2, iterator.Tail.Head); // Navigate to the Tail and check the next Head Assert.Equal(3, iterator.Tail.Tail.Head); // Keep checking subsequent elements Assert.Equal(4, iterator.Tail.Tail.Tail.Head); Assert.True(iterator.Tail.Tail.Tail.Tail.IsEmpty); // Check if the iterator ends correctly } [Fact] public async Task Iterator_ThreadSafety_Test() { // Arrange var numbers = Enumerable.Range(1, 1000).ToList(); var iterator = numbers.GetIterator(); // Act var tasks = Enumerable.Range(0, 5).Select( _ => Task.Run(() => { var current = iterator; while (!current.IsEmpty) { ignore(current.Head); // Access the head current = current.Tail; // Access the tail } })).ToArray(); // Assert await Task.WhenAll(tasks); // Ensure all tasks complete without throwing exceptions } [Fact] public void Iterator_Nil_Test() { // Arrange var iterator = Iterator.Nil.Default; // Act and Assert Assert.True(iterator.IsEmpty); // Nil should be empty Assert.Throws(() => iterator.Head); // Accessing the Head should throw Assert.Same(iterator, iterator.Tail); // The Tail of Nil should be itself } [Fact] public void Iterator_MultipleEnumerationPrevention_Test() { // Arrange var enumerator = "Lazy Evaluation".GetIterator(); // Act var head1 = enumerator.Head; var tail1 = enumerator.Tail; // Forces evaluation // Attempt to re-access the same enumerator var head2 = enumerator.Head; // Should be the same value, from cache // Assert Assert.Equal(head1, head2); // Items should not be re-enumerated } [Fact] public void Iterator_EmptyEnumerable_Test() { // Arrange var iterator = Enumerable.Empty().GetIterator(); // Act and Assert Assert.True(iterator.IsEmpty); // Empty iterator should be empty Assert.Throws(() => iterator.Head); Assert.Equal(iterator, iterator.Tail); // Tail of an empty iterator should be itself } [Fact] public void Iterator_ThreadSafety_Test_WithContentCheck() { // Arrange var threads = 5; var numbers = Enumerable.Range(1, 1000).ToList(); var iterator = numbers.GetIterator(); var resultingLists = new List>(); for (var i = 0; i < threads; i++) { resultingLists.Add(new List()); } // Act Parallel.ForEach(Enumerable.Range(0, 5), ix => { var current = iterator; while (!current.IsEmpty) { lock (resultingLists[ix]) { resultingLists[ix].Add(current.Head); // Safely add the emitted value into the list } current = current.Tail; } }); // Assert // The final list length should equal the source list's length (sequentially added values) foreach (var resultList in resultingLists) { Assert.True(Enumerable.SequenceEqual(numbers, resultList)); } } } ================================================ FILE: LanguageExt.Tests/LanguageExt.Tests.csproj ================================================ 3.0.0.0 3.0.0.0 default net10.0 enable ================================================ FILE: LanguageExt.Tests/LensTests.cs ================================================ /* TODO: Restore when we have SourceGen using System; using Xunit; using static LanguageExt.Prelude; namespace LanguageExt.Tests { public class LensTests { [Fact] public void LensMutate() { var car = new Car("Maserati", "Ghibli", 20000); var editor = new Editor("Joe Bloggs", 50000, car); var book = new Book("Editors of the World", "Sarah Bloggs", editor); var bookEditorCarMileage = lens(Book.editor, Editor.car, Car.mileage); var mileage = bookEditorCarMileage.Get(book); var book2 = bookEditorCarMileage.Set(25000, book); Assert.True(book2.Editor.Car.Mileage == 25000); } [Fact] public void CollectionsMutate() { var person = new Person("Paul", "Louth", Map( (1, new Appt(1, DateTime.Parse("1/1/2010"), ApptState.NotArrived)), (2, new Appt(2, DateTime.Parse("2/1/2010"), ApptState.NotArrived)), (3, new Appt(3, DateTime.Parse("3/1/2010"), ApptState.NotArrived)))); Lens arrive(int id) => lens(Person.appts, Map.item(id), Appt.state); var person2 = arrive(2).Set(ApptState.Arrived, person); Assert.True(person2.Appts[2].State == ApptState.Arrived); } } [WithLens] public partial class Person : Record { public readonly string Name; public readonly string Surname; public readonly Map Appts; public Person(string name, string surname, Map appts) { Name = name; Surname = surname; Appts = appts; } } [WithLens] public partial class Appt : Record { public readonly int Id; public readonly DateTime StartDate; public readonly ApptState State; public Appt(int id, DateTime startDate, ApptState state) { Id = id; StartDate = startDate; State = state; } } public enum ApptState { NotArrived, Arrived, DNA, Cancelled } [WithLens] public partial class Car : Record { public readonly string Make; public readonly string Model; public readonly int Mileage; public Car(string make, string model, int mileage) { Make = make; Model = model; Mileage = mileage; } } [WithLens] public partial class Editor : Record { public readonly string Name; public readonly int Salary; public readonly Car Car; public Editor(string name, int salary, Car car) { Name = name; Salary = salary; Car = car; } } [WithLens] public partial class Book : Record { public readonly string Name; public readonly string Author; public readonly Editor Editor; public Book(string name, string author, Editor editor) { Name = name; Author = author; Editor = editor; } } } */ ================================================ FILE: LanguageExt.Tests/LinqTests.cs ================================================ using System; using System.Linq; using LanguageExt.UnsafeValueAccess; using Xunit; namespace LanguageExt.Tests; public class LinqTests { [Fact] public void EnumerableString() { var opt = Some("pre "); var list = Some(new[] { "hello", "world" }.AsEnumerable()); var res = from a in opt.ToIterable() from x in list.ToIterable() from y in x select a + y; Assert.True(res.Head.ValueUnsafe() == "pre hello"); Assert.True(res.Tail.Head.ValueUnsafe() == "pre world"); opt = None; res = from a in opt.ToIterable() from x in list.ToIterable() from y in x select a + y; Assert.True(!res.Any()); } [Fact] public void MixedLinq() { var oa = Some(1); var lb = List(2, 3, 4, 5); var r1 = from a in oa.Map(x => List(x)) // a : int from b in Some(lb) // b : int select a + b; Assert.True(r1 == Some(List(1, 2, 3, 4, 5))); } [Fact] public void WithOptionSomeList() { var res = from v in GetOptionValue(true).ToIterable() from r in Range(1, 10) select v * r; var res2 = res.ToList(); Assert.True(res2.Count() == 10); Assert.True(res2[0] == 10); Assert.True(res2[9] == 100); } [Fact] public void WithOptionNoneList() { var res = from v in GetOptionValue(false).ToIterable() from r in Range(1, 10) select v * r; Assert.True(!res.Any()); } [Fact] public void WithEitherRightList() { var res = from v in toSeq(GetEitherValue(true)) from r in Range(1, 10) select v * r; var res2 = res.ToList(); Assert.True(res.Count() == 10); Assert.True(res2[0] == 10); Assert.True(res2[9] == 100); } [Fact] public void WithEitherLeftList() { var res = from v in toSeq(GetEitherValue(false)) from r in Range(1, 10) select v * r; Assert.True(res.Count() == 0); } [Fact] public void WhereArrayTest() { var res1 = from v in Array(1, 2, 3, 4, 5) where v < 3 select v; Assert.True(res1.Count == 2); } [Fact] public void WhereOptionTest() { var res1 = from v in GetOptionValue(true) where v == 10 select v; Assert.True(res1.IfNone(0) == 10); var res2 = from v in GetOptionValue(false) where v == 10 select v; Assert.True(res2.IfNone(0) == 0); } [Fact] public void OptionAndOrTest() { Option optional1 = None; Option optional2 = Some(10); Option optional3 = Some(20); var res = from x in optional1 || optional2 from y in optional3 select x + y; Assert.True(res == Some(30)); } private Option GetOptionValue(bool select) => select ? Some(10) : None; private Either GetEitherValue(bool select) { if (select) return 10; else return "left"; } [Fact] public void OptionLst1() { var list = List(1, 2, 3, 4); var opt = Some(5); var res = from y in opt.ToList() from x in list select x + y; } [Fact] public void OptionNoneTest1() { var res1 = from x in None from y in Some(123) from z in Some(456) select y + z; var res2 = from y in Some(123) from x in None from z in Some(456) select y + z; var res3 = from y in Some(123) from x in None from z in Some(456) select y + z; } } ================================================ FILE: LanguageExt.Tests/ListMatchingTests.cs ================================================ using System.Collections.Generic; using Xunit; namespace LanguageExt.Tests; public class ListMatchingTests { [Fact] public void RecursiveMatchSumTest() { var list0 = List(); var list1 = List(10); var list5 = List(10,20,30,40,50); Assert.True(Sum(list0) == 0); Assert.True(Sum(list1) == 10); Assert.True(Sum(list5) == 150); } public int Sum(IEnumerable list) => match(list, () => 0, x => x, (x, xs) => x + Sum(xs)); [Fact] public void RecursiveMatchMultiplyTest() { var list0 = List(); var list1 = List(10); var list5 = List(10, 20, 30, 40, 50); Assert.True(Multiply(list0) == 0); Assert.True(Multiply(list1) == 10); Assert.True(Multiply(list5) == 12000000); } public int Multiply(IEnumerable list) => list.Match( () => 0, x => x, (x, xs) => x * Multiply(xs)); [Fact] public void AnotherRecursiveMatchSumTest() { var list0 = List(); var list1 = List(10); var list5 = List(10, 20, 30, 40, 50); Assert.True(AnotherSum(list0) == 0); Assert.True(AnotherSum(list1) == 10); Assert.True(AnotherSum(list5) == 150); } public int AnotherSum(IEnumerable list) => match(list, () => 0, (x, xs) => x + AnotherSum(xs)); [Fact] public void AnotherRecursiveMatchMultiplyTest() { var list0 = List(); var list1 = List(10); var list5 = List(10, 20, 30, 40, 50); Assert.True(AnotherMultiply(list0) == 1); Assert.True(AnotherMultiply(list1) == 10); Assert.True(AnotherMultiply(list5) == 12000000); } public int AnotherMultiply(IEnumerable list) => list.Match( () => 1, (x, xs) => x * AnotherMultiply(xs)); } ================================================ FILE: LanguageExt.Tests/ListTests.cs ================================================ using Xunit; using System; using System.Linq; using static LanguageExt.List; namespace LanguageExt.Tests; public class ListTests { [Fact] public void ConsTest1() { var test = 1.Cons(2.Cons(3.Cons(4.Cons(5.Cons(empty()))))); var array = test.ToArray(); Assert.True(array[0] == 1); Assert.True(array[1] == 2); Assert.True(array[2] == 3); Assert.True(array[3] == 4); Assert.True(array[4] == 5); } [Fact] public void ListConstruct() { var test = List(1, 2, 3, 4, 5); var array = test.ToArray(); Assert.True(array[0] == 1); Assert.True(array[1] == 2); Assert.True(array[2] == 3); Assert.True(array[3] == 4); Assert.True(array[4] == 5); } [Fact] public void MapTest() { // Generates 10,20,30,40,50 var input = List(1, 2, 3, 4, 5); var output1 = map(input, x => x * 10); // Generates 30,40,50 var output2 = filter(output1, x => x > 20); // Generates 120 var output3 = fold(output2, 0, (x, s) => s + x); Assert.True(output3 == 120); } [Fact] public void ReduceTest() { // Generates 10,20,30,40,50 var input = List(1, 2, 3, 4, 5); var output1 = map(input, x => x * 10); // Generates 30,40,50 var output2 = filter(output1, x => x > 20); // Generates 120 var output3 = reduce(output2, (x, s) => s + x); Assert.True(output3 == 120); } [Fact] public void MapTestFluent() { var res = List(1, 2, 3, 4, 5) .Map(x => x * 10) .Filter(x => x > 20) .Fold(0, (x, s) => s + x); Assert.True(res == 120); } [Fact] public void ReduceTestFluent() { var res = List(1, 2, 3, 4, 5) .Map(x => x * 10) .Filter(x => x > 20) .Reduce((x, s) => s + x); Assert.True(res == 120); } [Fact] public void RangeTest1() { var r = Range(0, 10).AsIterable(); for (int i = 0; i < 10; i++) { Assert.True(r.First() == i); r = r.Skip(1); } } [Fact] public void RangeTest2() { var r = Range(0, 100, 10).AsIterable(); for (int i = 0; i < 10; i+=10) { Assert.True(r.First() == i); r = r.Skip(1); } } [Fact] public void RangeTest4() { var r = Range('a', 'f'); Assert.True(String.Join("", r) == "abcdef"); } [Fact] public void RangeTest5() { var r = Range('f', 'a'); Assert.True(String.Join("", r) == "fedcba"); } [Fact] public void RepeatTest() { var r = repeat("Hello", 10); foreach (var item in r) { Assert.True(item == "Hello"); } } [Fact] public void GenerateTest() { var r = generate(10, i => "Hello " + i ); for (int i = 0; i < 10; i++) { Assert.True(r.First() == "Hello " +i); r = r.Skip(1); } } [Fact] public void UnfoldTest() { var test = List(0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181); var fibs = take(unfold((0, 1), tup => map(tup, (a, b) => Some((a, (b, a + b))))), 20); Assert.True( test.SequenceEqual(fibs) ); } [Fact] public void UnfoldTupleTest() { var test = List(0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181); var fibs = take( unfold( (0, 1), (a, b) => Some((a, b, a + b)) ), 20); Assert.True(test.SequenceEqual(fibs)); } [Fact] public void UnfoldSingleTest() { var e = new Exception("Outer", new Exception("Inner")); var list = unfold(e, (state) => state == null ? None : Optional(state.InnerException) ); var res = list.ToList(); Assert.True(res[0].Message == "Outer" && res[1].Message == "Inner"); } [Fact] public void ReverseListTest1() { var list = List(1, 2, 3, 4, 5); var rev = list.Rev(); Assert.True(rev[0] == 5); Assert.True(rev[4] == 1); } [Fact] public void ReverseListTest2() { var list = List(1, 2, 3, 4, 5); var rev = list.Rev(); Assert.True(rev.IndexOf(1) == 4, "Should have been 4, actually is: " + rev.IndexOf(1)); Assert.True(rev.IndexOf(5) == 0, "Should have been 0, actually is: " + rev.IndexOf(5)); } [Fact] public void ReverseListTest3() { var list = List(1, 1, 2, 2, 2); var rev = list.Rev(); Assert.True(rev.LastIndexOf(1) == 4, "Should have been 4, actually is: " + rev.LastIndexOf(1)); Assert.True(rev.LastIndexOf(2) == 2, "Should have been 2, actually is: " + rev.LastIndexOf(5)); } [Fact] public void OpEqualTest() { var goodOnes = List( (List(1, 2, 3), List(1, 2, 3)), (Lst.Empty, Lst.Empty) ); var badOnes = List( (List(1, 2, 3), List(1, 2, 4)), (List(1, 2, 3), Lst.Empty) ); goodOnes.Iter(t => t.Iter((fst, snd) => { Assert.True(fst == snd, $"'{fst}' == '{snd}'"); Assert.False(fst != snd, $"'{fst}' != '{snd}'"); })); badOnes.Iter(t => t.Iter((fst, snd) => { Assert.True(fst != snd, $"'{fst}' != '{snd}'"); Assert.False(fst == snd, $"'{fst}' == '{snd}'"); })); } [Fact] public void IterSimpleTest() { var embeddedSideEffectResult = 0; var expression = from dummy in Some(unit).ToIterable() from i in List(2, 3, 5) let _ = fun(() => embeddedSideEffectResult += i)() select i; Assert.Equal(0, embeddedSideEffectResult); var sideEffectByAction = 0; expression.Iter(i => sideEffectByAction += i * i); Assert.Equal(2 + 3 + 5, embeddedSideEffectResult); Assert.Equal(2 * 2 + 3 * 3 + 5 * 5, sideEffectByAction); } [Fact] public void IterPositionalTest() { var embeddedSideEffectResult = 0; var expression = from dummy in Some(unit).ToIterable() from i in List(2, 3, 5) let _ = fun(() => embeddedSideEffectResult += i)() select i; Assert.Equal(0, embeddedSideEffectResult); var sideEffectByAction = 0; expression.Iter((pos, i) => sideEffectByAction += i * pos); Assert.Equal(2 + 3 + 5, embeddedSideEffectResult); Assert.Equal(2 * 0 + 3 * 1 + 5 * 2, sideEffectByAction); } [Fact] public void ConsumeTest() { var embeddedSideEffectResult = 0; System.Collections.Generic.IEnumerable expression = from dummy in Some(unit).ToIterable() from i in List(2, 3, 5) let _ = fun(() => embeddedSideEffectResult += i)() select i; Assert.Equal(0, embeddedSideEffectResult); expression.Consume(); Assert.Equal(2 + 3 + 5, embeddedSideEffectResult); } [Fact] public void SkipLastTest1() { var list = List(1, 2, 3, 4, 5); var skipped = list.SkipLast().AsIterable().ToLst(); Assert.True(skipped == List(1, 2, 3, 4)); } [Fact] public void SkipLastTest2() { var list = List(); var skipped = list.SkipLast().AsIterable().ToLst(); Assert.True(skipped == list); } [Fact] public void SkipLastTest3() { var list = List(1, 2, 3, 4, 5); var skipped = list.SkipLast(2).AsIterable().ToLst(); Assert.True(skipped == List(1, 2, 3)); } [Fact] public void SkipLastTest4() { var list = List(); var skipped = list.SkipLast(2).AsIterable().ToLst(); Assert.True(skipped == list); } [Fact] public void SetItemTest() { Lst lint = new Lst(); lint = lint.Insert(0, 0).Insert(1, 1).Insert(2, 2).Insert(3, 3); Assert.True(lint[0] == 0); Assert.True(lint[1] == 1); Assert.True(lint[2] == 2); Assert.True(lint[3] == 3); lint = lint.SetItem(2, 500); Assert.True(lint[0] == 0); Assert.True(lint[1] == 1); Assert.True(lint[2] == 500); Assert.True(lint[3] == 3); } [Fact] public void RemoveAllTest() { var test = List(1, 2, 3, 4, 5); Assert.True(test.RemoveAll(x => x % 2 == 0) == List(1, 3, 5)); } [Fact] public void RemoveAtInsertTest() { Lst lint = new Lst(); lint = lint.Insert(0, 0).Insert(1, 1).Insert(2, 2).Insert(3, 3); Assert.True(lint[0] == 0); Assert.True(lint[1] == 1); Assert.True(lint[2] == 2); Assert.True(lint[3] == 3); lint = lint.RemoveAt(2); Assert.True(lint[0] == 0); Assert.True(lint[1] == 1); Assert.True(lint[2] == 3); lint = lint.Insert(2, 500); Assert.True(lint[0] == 0); Assert.True(lint[1] == 1); Assert.True(lint[2] == 500); Assert.True(lint[3] == 3); } [Fact] public void RemoveRange() { var list = List(1, 2, 3, 4); Assert.Equal(list.RemoveRange(2, 2), List(1, 2)); Assert.Throws(() => list.RemoveRange(2, 3)); } [Fact] public void SetItemManyTest() { var range = IterableExtensions.AsIterable(Range(0, 100)).ToLst(); for (int i = 0; i < 100; i++) { range = range.SetItem(i, i * 2); Assert.True(range[i] == i * 2); for(var b = 0; b < i; b++) { Assert.True(range[b] == b * 2); } for (var a = i + 1; a < 100; a++) { Assert.True(range[a] == a); } } } [Fact] public void RemoveAtInsertManyTest() { var range = IterableExtensions.AsIterable(Range(0, 100)).ToLst(); for (int i = 0; i < 100; i++) { range = range.RemoveAt(i); Assert.True(range.Count == 99); range = range.Insert(i, i * 2); Assert.True(range[i] == i * 2); for (var b = 0; b < i; b++) { Assert.True(range[b] == b * 2); } for (var a = i + 1; a < 100; a++) { Assert.True(range[a] == a); } } } [Fact] public void EqualsTest() { Assert.False(List(1, 2, 3).Equals(List())); Assert.False(List().Equals(List(1, 2, 3))); Assert.True(List().Equals(List())); Assert.True(List(1).Equals(List(1))); Assert.True(List(1, 2).Equals(List(1, 2))); Assert.False(List(1, 2).Equals(List(1, 2, 3))); Assert.False(List(1, 2, 3).Equals(List(1, 2))); } [Fact] public void ListShouldRemoveByReference() { var o0 = new Object(); var o1 = new Object(); var o2 = new Object(); var l = List(o0, o1); l = l.Remove(o2); Assert.Equal(2, l.Count); l = l.Remove(o0); Assert.Equal(1, l.Count); l = l.Remove(o1); Assert.Equal(0, l.Count); } [Fact] public void ListShouldRemoveByReferenceForReverseLists() { var o0 = new Object(); var o1 = new Object(); var o2 = new Object(); var l = List(o0, o1).Reverse(); l = l.Remove(o2); Assert.Equal(2, l.Count); l = l.Remove(o0); Assert.Equal(1, l.Count); l = l.Remove(o1); Assert.Equal(0, l.Count); } [Fact] public void FoldTest() { var input = List(1, 2, 3, 4, 5); var output1 = fold(input, "", (s, x) => s + x.ToString()); Assert.Equal("12345", output1); } [Fact] public void FoldBackTest() { var input = List(1, 2, 3, 4, 5); var output1 = foldBack(input, "", (s, x) => s + x.ToString()); Assert.Equal("54321", output1); } [Fact] public void FoldWhileTest() { var input = List(10, 20, 30, 40, 50); var output1 = foldWhile(input, "", (s, x) => s + x.ToString(), x => x < 40); Assert.Equal("102030", output1); var output2 = foldWhile(input, "", (s, x) => s + x.ToString(), (string s) => s.Length < 6); Assert.Equal("102030", output2); var output3 = foldWhile(input, 0, (s, x) => s + x, preditem: x => x < 40); Assert.Equal(60, output3); var output4 = foldWhile(input, 0, (s, x) => s + x, predstate: s => s < 60); Assert.Equal(60, output4); } [Fact] public void FoldBackWhileTest() { var input = List(10, 20, 30, 40, 50); var output1 = foldBackWhile(input, "", (s, x) => s + x.ToString(), x => x >= 40); Assert.Equal("5040", output1); var output2 = foldBackWhile(input, "", (s, x) => s + x.ToString(), (string s) => s.Length < 4); Assert.Equal("5040", output2); var output3 = foldBackWhile(input, 0, (s, x) => s + x, preditem: x => x >= 40); Assert.Equal(90, output3); var output4 = foldBackWhile(input, 0, (s, x) => s + x, predstate: s => s < 90); Assert.Equal(90, output4); } [Fact] public void FoldUntilTest() { var input = List(10, 20, 30, 40, 50); var output1 = foldUntil(input, "", (s, x) => s + x.ToString(), x => x >= 40); Assert.Equal("102030", output1); var output2 = foldUntil(input, "", (s, x) => s + x.ToString(), (string s) => s.Length >= 6); Assert.Equal("102030", output2); var output3 = foldUntil(input, 0, (s, x) => s + x, preditem: x => x >= 40); Assert.Equal(60, output3); var output4 = foldUntil(input, 0, (s, x) => s + x, predstate: s => s >= 60); Assert.Equal(60, output4); } [Fact] public void FoldBackUntilTest() { var input = List(10, 20, 30, 40, 50); var output1 = foldBackUntil(input, "", (s, x) => s + x.ToString(), x => x < 40); Assert.Equal("5040", output1); var output2 = foldBackUntil(input, "", (s, x) => s + x.ToString(), (string s) => s.Length >= 4); Assert.Equal("5040", output2); var output3 = foldBackUntil(input, 0, (s, x) => s + x, preditem: x => x < 40); Assert.Equal(90, output3); var output4 = foldBackUntil(input, 0, (s, x) => s + x, predstate: s => s >= 90); Assert.Equal(90, output4); } [Fact] public void itemLensGetShouldGetExistingValue() { var expected = "3"; var list = List("0","1", "2", "3", "4", "5"); var actual = Lst.item(3).Get(list); Assert.Equal(expected, actual); } [Fact] public void itemLensGetShouldThrowExceptionForNonExistingValue() { Assert.Throws(() => { var list = List("0", "1", "2", "3", "4", "5"); var actual = Lst.item(10).Get(list); }); } [Fact] public void itemOrNoneLensGetShouldGetExistingValue() { var expected = "3"; var list = List("0", "1", "2", "3", "4", "5"); var actual = Lst.itemOrNone(3).Get(list); Assert.Equal(expected, actual); } [Fact] public void itemOrNoneLensGetShouldReturnNoneForNonExistingValue() { var expected = Option.None; var list = List("0", "1", "2", "3", "4", "5"); var actual = Lst.itemOrNone(10).Get(list); Assert.Equal(expected, actual); } } ================================================ FILE: LanguageExt.Tests/MapTests.cs ================================================ using static LanguageExt.Map; using Xunit; using System; using System.Linq; using LanguageExt.ClassInstances; namespace LanguageExt.Tests; public class MapTests { [Fact] public void MapGeneratorTest() { var m1 = Map(); m1 = add(m1, 100, "hello"); Assert.True(m1.Count == 1 && containsKey(m1,100)); } [Fact] public void MapGeneratorAndMatchTest() { Map m2 = [(1, "a"), (2, "b"), (3, "c")]; m2 = add(m2, 100, "world"); var res = match( m2, 100, v => v, () => "failed" ); Assert.True(res == "world"); } [Fact] public void MapSetTest() { var m1 = Map( (1, "a"), (2, "b"), (3, "c") ); var m2 = setItem(m1, 1, "x"); match( m1, 1, Some: v => Assert.True(v == "a"), None: () => Assert.False(true) ); match( find(m2, 1), Some: v => Assert.True(v == "x"), None: () => Assert.False(true) ); Assert.Throws(() => setItem(m1, 4, "y")); } [Fact] public void MapOrdSetTest() { var m1 = Map(("one", 1), ("two",2), ("three", 3)); var m2 = m1.SetItem("One", -1); Assert.Equal(3, m2.Count); Assert.Equal(-1, m2["one"]); Assert.DoesNotContain("one", m2.Keys.AsEnumerable()); // make sure key got replaced, too Assert.Contains("One", m2.Keys.AsEnumerable()); // make sure key got replaced, too Assert.Throws(() => m1.SetItem("four", identity)); var m3 = m1.TrySetItem("four", 0).Add("five", 0).TrySetItem("Five", 5); Assert.Equal(5, m3["fiVe"]); Assert.DoesNotContain("four", m3.Keys.AsEnumerable()); Assert.DoesNotContain("five", m3.Keys.AsEnumerable()); Assert.Contains("Five", m3.Keys.AsEnumerable()); } [Fact] public void MapAddInOrderTest() { var m = Map((1, 1)); m.Find(1).IfNone(() => failwith("Broken")); m = Map((1, 1), (2, 2)); m.Find(1).IfNone(() => failwith("Broken")); m.Find(2).IfNone(() => failwith("Broken")); m = Map((1, 1), (2, 2), (3, 3)); m.Find(1).IfNone(() => failwith("Broken")); m.Find(2).IfNone(() => failwith("Broken")); m.Find(3).IfNone(() => failwith("Broken")); m = Map((1, 1), (2, 2), (3, 3), (4, 4)); m.Find(1).IfNone(() => failwith("Broken")); m.Find(2).IfNone(() => failwith("Broken")); m.Find(3).IfNone(() => failwith("Broken")); m.Find(4).IfNone(() => failwith("Broken")); m = Map((1, 1), (2, 2), (3, 3), (4, 4), (5, 5)); m.Find(1).IfNone(() => failwith("Broken")); m.Find(2).IfNone(() => failwith("Broken")); m.Find(3).IfNone(() => failwith("Broken")); m.Find(4).IfNone(() => failwith("Broken")); m.Find(5).IfNone(() => failwith("Broken")); } [Fact] public void MapAddInReverseOrderTest() { var m = Map((2, 2), (1, 1)); m.Find(1).IfNone(() => failwith("Broken")); m.Find(2).IfNone(() => failwith("Broken")); m = Map((3, 3), (2, 2), (1, 1)); m.Find(1).IfNone(() => failwith("Broken")); m.Find(2).IfNone(() => failwith("Broken")); m.Find(3).IfNone(() => failwith("Broken")); m = Map((4, 4), (3, 3), (2, 2), (1, 1)); m.Find(1).IfNone(() => failwith("Broken")); m.Find(2).IfNone(() => failwith("Broken")); m.Find(3).IfNone(() => failwith("Broken")); m.Find(4).IfNone(() => failwith("Broken")); m = Map((5, 5), (4, 4), (3, 3), (2, 2), (1, 1)); m.Find(1).IfNone(() => failwith("Broken")); m.Find(2).IfNone(() => failwith("Broken")); m.Find(3).IfNone(() => failwith("Broken")); m.Find(4).IfNone(() => failwith("Broken")); m.Find(5).IfNone(() => failwith("Broken")); } [Fact] public void MapAddInMixedOrderTest() { var m = Map((5, 5), (1, 1), (3, 3), (2, 2), (4, 4)); m.Find(1).IfNone(() => failwith("Broken")); m.Find(2).IfNone(() => failwith("Broken")); m.Find(3).IfNone(() => failwith("Broken")); m.Find(4).IfNone(() => failwith("Broken")); m.Find(5).IfNone(() => failwith("Broken")); m = Map((1, 1), (3, 3), (5, 5), (2, 2), (4, 4)); m.Find(1).IfNone(() => failwith("Broken")); m.Find(2).IfNone(() => failwith("Broken")); m.Find(3).IfNone(() => failwith("Broken")); m.Find(4).IfNone(() => failwith("Broken")); m.Find(5).IfNone(() => failwith("Broken")); } [Fact] public void MapRemoveTest() { var m = Map((1, "a"), (2, "b"), (3, "c"), (4, "d"), (5, "e")); m.Find(1).IfNone(() => failwith("Broken 1")); m.Find(2).IfNone(() => failwith("Broken 2")); m.Find(3).IfNone(() => failwith("Broken 3")); m.Find(4).IfNone(() => failwith("Broken 4")); m.Find(5).IfNone(() => failwith("Broken 5")); Assert.True(m.Count == 5); m = remove(m,4); Assert.True(m.Count == 4); Assert.True(m.Find(4).IsNone); m.Find(1).IfNone(() => failwith("Broken 1")); m.Find(2).IfNone(() => failwith("Broken 2")); m.Find(3).IfNone(() => failwith("Broken 3")); m.Find(5).IfNone(() => failwith("Broken 5")); m = remove(m, 1); Assert.True(m.Count == 3); Assert.True(m.Find(1).IsNone); m.Find(2).IfNone(() => failwith("Broken 2")); m.Find(3).IfNone(() => failwith("Broken 3")); m.Find(5).IfNone(() => failwith("Broken 5")); m = remove(m, 2); Assert.True(m.Count == 2); Assert.True(m.Find(2).IsNone); m.Find(3).IfNone(() => failwith("Broken 3")); m.Find(5).IfNone(() => failwith("Broken 5")); m = remove(m, 3); Assert.True(m.Count == 1); Assert.True(m.Find(3).IsNone); m.Find(5).IfNone(() => failwith("Broken 5")); m = remove(m, 5); Assert.True(m.Count == 0); Assert.True(m.Find(5).IsNone); } [Fact] public void MassAddRemoveTest() { int max = 100000; var items = LanguageExt.List.map(Range(1, max), _ => (Guid.NewGuid(), Guid.NewGuid())) .ToDictionary(kv => kv.Item1, kv => kv.Item2); var m = Map().AddRange(items); Assert.True(m.Count == max); foreach (var item in items) { Assert.True(m.ContainsKey(item.Key)); m = m.Remove(item.Key); Assert.False(m.ContainsKey(item.Key)); max--; Assert.True(m.Count == max); } } [Fact] public void MapOrdSumTest() { var m1 = Map(("one", 1), ("two",2)); var m2 = Map(("three", 3)); var sum = m1 + m2; Assert.Equal(sum, m1.AddRange(m2)); Assert.Equal(m2, sum.Clear() + m2); } [Fact] public void MapOptionTest() { var m = Map<(Option, Option), string>(); m = m.AddOrUpdate((Some(1), Some(1)), "Some Some"); m = m.AddOrUpdate((None, Some(1)), "None Some"); m = m.AddOrUpdate((Some(1), None), "Some None"); m = m.AddOrUpdate((None, None), "None None"); Assert.True(m[(Some(1), Some(1))] == "Some Some"); Assert.True(m[(None, Some(1))] == "None Some"); Assert.True(m[(Some(1), None)] == "Some None"); Assert.True(m[(None, None)] == "None None"); Assert.True(m.Count == 4); m = m.Filter(v => v.EndsWith("None", StringComparison.Ordinal)); Assert.True(m.Count == 2); } [Fact] public void MapValuesTest() { var m = Map((1, 1), (2, 2), (3, 3), (4, 4), (5, 5)); var vs = toSeq(m.Values); Assert.True(vs.Head == 1); Assert.True(vs.Tail.Head == 2); Assert.True(vs.Tail.Tail.Head == 3); Assert.True(vs.Tail.Tail.Tail.Head == 4); Assert.True(vs.Tail.Tail.Tail.Tail.Head == 5); Assert.True(vs.Count == 5); } [Fact] public void MapKeysTest() { var m = Map((1, 1), (2, 2), (3, 3), (4, 4), (5, 5)); var vs = toSeq(m.Keys); Assert.True(vs.Head == 1); Assert.True(vs.Tail.Head == 2); Assert.True(vs.Tail.Tail.Head == 3); Assert.True(vs.Tail.Tail.Tail.Head == 4); Assert.True(vs.Tail.Tail.Tail.Tail.Head == 5); Assert.True(vs.Count == 5); } [Fact] public void MapUnionTest1() { var x = Map((1, 1), (2, 2), (3, 3)); var y = Map((1, 1), (2, 2), (3, 3)); var z = union(x, y, (k, l, r) => l + r); Assert.True(z == Map((1, 2), (2, 4), (3, 6))); } [Fact] public void MapUnionTest2() { var x = Map((1, 1), (2, 2), (3, 3)); var y = Map((4, 4), (5, 5), (6, 6)); var z = union(x, y, (k, l, r) => l + r); Assert.True(z == Map((1, 1), (2, 2), (3, 3), (4, 4), (5, 5), (6, 6))); } [Fact] public void MapIntesectTest1() { var x = Map( (2, 2), (3, 3)); var y = Map((1, 1), (2, 2) ); var z = intersect(x, y, (k, l, r) => l + r); Assert.True(z == Map((2, 4))); } [Fact] public void MapExceptTest() { var x = Map((1, 1), (2, 2), (3, 3)); var y = Map((1, 1)); var z = except(x, y); Assert.True(z == Map((2, 2), (3, 3))); } [Fact] public void MapSymmetricExceptTest() { var x = Map((1, 1), (2, 2), (3, 3)); var y = Map((1, 1), (3, 3)); var z = symmetricExcept(x, y); Assert.True(z == Map((2, 2))); } [Fact] public void EqualsTest() { var emp = Map(); Assert.True(emp.Equals(emp)); Assert.False(Map((1, 2)).Equals(emp)); Assert.False(emp.Equals(Map((1, 2)))); Assert.True(Map((1, 2)).Equals(Map((1, 2)))); Assert.False(Map((1, 2)).Equals(Map((1, 3)))); Assert.False(Map((1, 2), (3, 4)).Equals(Map((1, 2)))); Assert.False(Map((1, 2)).Equals(Map((1, 2), (3, 4)))); Assert.True(Map((1, 2), (3, 4)).Equals(Map((1, 2), (3, 4)))); Assert.True(Map((3, 4), (1, 2)).Equals(Map((1, 2), (3, 4)))); Assert.True(Map((3, 4), (1, 2)).Equals(Map((3, 4), (1, 2)))); } [Fact] public void SliceTest() { var m = Map((1, 1), (2, 2), (3, 3), (4, 4), (5, 5)); var x = m.Slice(1, 2); Assert.True(x.Count == 2); Assert.True(x.ContainsKey(1)); Assert.True(x.ContainsKey(2)); var y = m.Slice(2, 4); Assert.True(y.Count == 3); Assert.True(y.ContainsKey(2)); Assert.True(y.ContainsKey(3)); Assert.True(y.ContainsKey(4)); var z = m.Slice(4, 5); Assert.True(z.Count == 2); Assert.True(z.ContainsKey(4)); Assert.True(z.ContainsKey(5)); } [Fact] public void MinMaxTest() { var m = Map((1, 1), (2, 2), (3, 3), (4, 4), (5, 5)); Assert.True(m.Min == (1, 1)); Assert.True(m.Max == (5, 5)); var me = Map(); Assert.True(me.Min == None); Assert.True(me.Max == None); } [Fact] public void FindPredecessorWhenKeyExistsTest() { var m = Map((1, 1), (2, 2), (3, 3), (4, 4), (5, 5), (6, 6), (7, 7), (8, 8), (9, 9), (10, 10), (11, 11), (12, 12), (13, 13), (14, 14), (15, 15)); Assert.True(m.FindPredecessor(1) == None); Assert.True(m.FindPredecessor(2) == (1, 1)); Assert.True(m.FindPredecessor(3) == (2, 2)); Assert.True(m.FindPredecessor(4) == (3, 3)); Assert.True(m.FindPredecessor(5) == (4, 4)); Assert.True(m.FindPredecessor(6) == (5, 5)); Assert.True(m.FindPredecessor(7) == (6, 6)); Assert.True(m.FindPredecessor(8) == (7, 7)); Assert.True(m.FindPredecessor(9) == (8, 8)); Assert.True(m.FindPredecessor(10) == (9, 9)); Assert.True(m.FindPredecessor(11) == (10, 10)); Assert.True(m.FindPredecessor(12) == (11, 11)); Assert.True(m.FindPredecessor(13) == (12, 12)); Assert.True(m.FindPredecessor(14) == (13, 13)); Assert.True(m.FindPredecessor(15) == (14, 14)); } [Fact] public void FindPredecessorWhenKeyNotExistsTest() { var m = Map((1, 1), (3, 3), (5, 5), (7, 7), (9, 9), (11, 11), (13, 13), (15, 15)); Assert.True(m.FindPredecessor(1) == None); Assert.True(m.FindPredecessor(2) == (1, 1)); Assert.True(m.FindPredecessor(3) == (1, 1)); Assert.True(m.FindPredecessor(4) == (3, 3)); Assert.True(m.FindPredecessor(5) == (3, 3)); Assert.True(m.FindPredecessor(6) == (5, 5)); Assert.True(m.FindPredecessor(7) == (5, 5)); Assert.True(m.FindPredecessor(8) == (7, 7)); Assert.True(m.FindPredecessor(9) == (7, 7)); Assert.True(m.FindPredecessor(10) == (9, 9)); Assert.True(m.FindPredecessor(11) == (9, 9)); Assert.True(m.FindPredecessor(12) == (11, 11)); Assert.True(m.FindPredecessor(13) == (11, 11)); Assert.True(m.FindPredecessor(14) == (13, 13)); Assert.True(m.FindPredecessor(15) == (13, 13)); } [Fact] public void FindExactOrPredecessorWhenKeyExistsTest() { var m = Map((1, 1), (2, 2), (3, 3), (4, 4), (5, 5), (6, 6), (7, 7), (8, 8), (9, 9), (10, 10), (11, 11), (12, 12), (13, 13), (14, 14), (15, 15)); Assert.True(m.FindExactOrPredecessor(1) == (1, 1)); Assert.True(m.FindExactOrPredecessor(2) == (2, 2)); Assert.True(m.FindExactOrPredecessor(3) == (3, 3)); Assert.True(m.FindExactOrPredecessor(4) == (4, 4)); Assert.True(m.FindExactOrPredecessor(5) == (5, 5)); Assert.True(m.FindExactOrPredecessor(6) == (6, 6)); Assert.True(m.FindExactOrPredecessor(7) == (7, 7)); Assert.True(m.FindExactOrPredecessor(8) == (8, 8)); Assert.True(m.FindExactOrPredecessor(9) == (9, 9)); Assert.True(m.FindExactOrPredecessor(10) == (10, 10)); Assert.True(m.FindExactOrPredecessor(11) == (11, 11)); Assert.True(m.FindExactOrPredecessor(12) == (12, 12)); Assert.True(m.FindExactOrPredecessor(13) == (13, 13)); Assert.True(m.FindExactOrPredecessor(14) == (14, 14)); Assert.True(m.FindExactOrPredecessor(15) == (15, 15)); } [Fact] public void FindExactOrPredecessorWhenKeySometimesExistsTest() { var m = Map((1, 1), (3, 3), (5, 5), (7, 7), (9, 9), (11, 11), (13, 13), (15, 15)); Assert.True(m.FindExactOrPredecessor(1) == (1, 1)); Assert.True(m.FindExactOrPredecessor(2) == (1, 1)); Assert.True(m.FindExactOrPredecessor(3) == (3, 3)); Assert.True(m.FindExactOrPredecessor(4) == (3, 3)); Assert.True(m.FindExactOrPredecessor(5) == (5, 5)); Assert.True(m.FindExactOrPredecessor(6) == (5, 5)); Assert.True(m.FindExactOrPredecessor(7) == (7, 7)); Assert.True(m.FindExactOrPredecessor(8) == (7, 7)); Assert.True(m.FindExactOrPredecessor(9) == (9, 9)); Assert.True(m.FindExactOrPredecessor(10) == (9, 9)); Assert.True(m.FindExactOrPredecessor(11) == (11, 11)); Assert.True(m.FindExactOrPredecessor(12) == (11, 11)); Assert.True(m.FindExactOrPredecessor(13) == (13, 13)); Assert.True(m.FindExactOrPredecessor(14) == (13, 13)); Assert.True(m.FindExactOrPredecessor(15) == (15, 15)); } [Fact] public void FindSuccessorWhenKeyExistsTest() { var m = Map((1, 1), (2, 2), (3, 3), (4, 4), (5, 5), (6, 6), (7, 7), (8, 8), (9, 9), (10, 10), (11, 11), (12, 12), (13, 13), (14, 14), (15, 15)); Assert.True(m.FindSuccessor(1) == (2, 2)); Assert.True(m.FindSuccessor(2) == (3, 3)); Assert.True(m.FindSuccessor(3) == (4, 4)); Assert.True(m.FindSuccessor(4) == (5, 5)); Assert.True(m.FindSuccessor(5) == (6, 6)); Assert.True(m.FindSuccessor(6) == (7, 7)); Assert.True(m.FindSuccessor(7) == (8, 8)); Assert.True(m.FindSuccessor(8) == (9, 9)); Assert.True(m.FindSuccessor(9) == (10, 10)); Assert.True(m.FindSuccessor(10) == (11, 11)); Assert.True(m.FindSuccessor(11) == (12, 12)); Assert.True(m.FindSuccessor(12) == (13, 13)); Assert.True(m.FindSuccessor(13) == (14, 14)); Assert.True(m.FindSuccessor(14) == (15, 15)); Assert.True(m.FindSuccessor(15) == None); } [Fact] public void FindSuccessorWhenKeyNotExistsTest() { var m = Map((1, 1), (3, 3), (5, 5), (7, 7), (9, 9), (11, 11), (13, 13), (15, 15)); Assert.True(m.FindSuccessor(1) == (3, 3)); Assert.True(m.FindSuccessor(2) == (3, 3)); Assert.True(m.FindSuccessor(3) == (5, 5)); Assert.True(m.FindSuccessor(4) == (5, 5)); Assert.True(m.FindSuccessor(5) == (7, 7)); Assert.True(m.FindSuccessor(6) == (7, 7)); Assert.True(m.FindSuccessor(7) == (9, 9)); Assert.True(m.FindSuccessor(8) == (9, 9)); Assert.True(m.FindSuccessor(9) == (11, 11)); Assert.True(m.FindSuccessor(10) == (11, 11)); Assert.True(m.FindSuccessor(11) == (13, 13)); Assert.True(m.FindSuccessor(12) == (13, 13)); Assert.True(m.FindSuccessor(13) == (15, 15)); Assert.True(m.FindSuccessor(14) == (15, 15)); Assert.True(m.FindSuccessor(15) == None); } [Fact] public void FindExactOrSuccessorWhenKeyExistsTest() { var m = Map((1, 1), (2, 2), (3, 3), (4, 4), (5, 5), (6, 6), (7, 7), (8, 8), (9, 9), (10, 10), (11, 11), (12, 12), (13, 13), (14, 14), (15, 15)); Assert.True(m.FindExactOrSuccessor(1) == (1, 1)); Assert.True(m.FindExactOrSuccessor(2) == (2, 2)); Assert.True(m.FindExactOrSuccessor(3) == (3, 3)); Assert.True(m.FindExactOrSuccessor(4) == (4, 4)); Assert.True(m.FindExactOrSuccessor(5) == (5, 5)); Assert.True(m.FindExactOrSuccessor(6) == (6, 6)); Assert.True(m.FindExactOrSuccessor(7) == (7, 7)); Assert.True(m.FindExactOrSuccessor(8) == (8, 8)); Assert.True(m.FindExactOrSuccessor(9) == (9, 9)); Assert.True(m.FindExactOrSuccessor(10) == (10, 10)); Assert.True(m.FindExactOrSuccessor(11) == (11, 11)); Assert.True(m.FindExactOrSuccessor(12) == (12, 12)); Assert.True(m.FindExactOrSuccessor(13) == (13, 13)); Assert.True(m.FindExactOrSuccessor(14) == (14, 14)); Assert.True(m.FindExactOrSuccessor(15) == (15, 15)); } [Fact] public void FindExactOrSuccessorWhenKeySometimesExistsTest() { var m = Map((1, 1), (3, 3), (5, 5), (7, 7), (9, 9), (11, 11), (13, 13), (15, 15)); Assert.True(m.FindExactOrSuccessor(1) == (1, 1)); Assert.True(m.FindExactOrSuccessor(2) == (3, 3)); Assert.True(m.FindExactOrSuccessor(3) == (3, 3)); Assert.True(m.FindExactOrSuccessor(4) == (5, 5)); Assert.True(m.FindExactOrSuccessor(5) == (5, 5)); Assert.True(m.FindExactOrSuccessor(6) == (7, 7)); Assert.True(m.FindExactOrSuccessor(7) == (7, 7)); Assert.True(m.FindExactOrSuccessor(8) == (9, 9)); Assert.True(m.FindExactOrSuccessor(9) == (9, 9)); Assert.True(m.FindExactOrSuccessor(10) == (11, 11)); Assert.True(m.FindExactOrSuccessor(11) == (11, 11)); Assert.True(m.FindExactOrSuccessor(12) == (13, 13)); Assert.True(m.FindExactOrSuccessor(13) == (13, 13)); Assert.True(m.FindExactOrSuccessor(14) == (15, 15)); Assert.True(m.FindExactOrSuccessor(15) == (15, 15)); } // Exponential test - takes too long to run //[Fact] //public void Issue_454() //{ // var tmp = "".PadLeft(30000, 'x'); // something big enough (one Referral object = 20-40kb) // var map = Map(); // for (int i = 0; i < 30000; i++) // for our real system it is only 3000 items, but with string it needs more // { // map = map.AddOrUpdate(i, tmp); // map = map.Filter(_ => true); // } // map.Filter(_ => false); //} [Fact] public void itemLensGetShouldGetExistingValue() { var expected = "3"; var map = Map((1, "1"), (2, "2"), (3, "3"), (4, "4"), (5, "5")); var actual = Map.item(3).Get(map); Assert.Equal(expected, actual); } [Fact] public void itemLensGetShouldThrowExceptionForNonExistingValue() { Assert.Throws(() => { var map = Map((1, "1"), (2, "2"), (3, "3"), (4, "4"), (5, "5")); var actual = Map.item(10).Get(map); }); } [Fact] public void itemOrNoneLensGetShouldGetExistingValue() { var expected = "3"; var map = Map((1, "1"), (2, "2"), (3, "3"), (4, "4"), (5, "5")); var actual = Map.itemOrNone(3).Get(map); Assert.Equal(expected, actual); } [Fact] public void itemOrNoneLensGetShouldReturnNoneForNonExistingValue() { var expected = Option.None; var map = Map((1, "1"), (2, "2"), (3, "3"), (4, "4"), (5, "5")); var actual = Map.itemOrNone(10).Get(map); Assert.Equal(expected, actual); } [Fact] public void foldStepValuesShouldYieldInOrder() { var expected = Seq("1", "2", "3", "4", "5", "6", "7", "8", "9", "10"); var map = Map((1, "1"), (2, "2"), (3, "3"), (4, "4"), (5, "5"), (6, "6"), (7, "7"), (8, "8"), (9, "9"), (10, "10")); var step = map.FoldStepValues(Seq()); while (step is not Fold>.Done) { if (step is Fold>.Loop loop) { step = loop.Next(loop.State.Add(loop.Value)); } } Assert.True(expected == step.State); } [Fact] public void foldStepBackValuesShouldYieldInReverseOrder() { var expected = Seq("10", "9", "8", "7", "6", "5", "4", "3", "2", "1"); var map = Map((1, "1"), (2, "2"), (3, "3"), (4, "4"), (5, "5"), (6, "6"), (7, "7"), (8, "8"), (9, "9"), (10, "10")); var step = map.FoldStepBackValues(Seq()); while (step is not Fold>.Done) { if (step is Fold>.Loop loop) { step = loop.Next(loop.State.Add(loop.Value)); } } Assert.True(expected == step.State); } } ================================================ FILE: LanguageExt.Tests/MemoImplTests.cs ================================================ using Xunit; using System; using System.Linq; using static LanguageExt.List; namespace LanguageExt.Tests; public class MemoImplTests { [Fact] public void MemoTest1() { var saved = DateTime.Now; var date = saved; var f = memo(() => date.ToString()); var res1 = ~f; date = DateTime.Now.AddDays(1); var res2 = ~f; Assert.True(res1 == res2); } [Fact] public void MemoTest2() { var fix = 0; var count = 100; Func fn = x => x + fix; var m = fn.MemoUnsafe(); var nums1 = map(Range(0, count), i => m(i)); fix = 1000; var nums2 = map(Range(0, count), i => m(i)); Assert.True( length(filter(zip(nums1, nums2, (a, b) => a == b), v => v)) == count ); } // Commenting out because this test is unreliable when all the other tests are // running. This functionality is likely never going to change, so I'm fine with // that for now. //[Fact] //public void MemoTest3() //{ // GC.Collect(); // var fix = 0; // var count = 100; // Func fn = x => x + fix; // var m = fn.Memo(); // var nums1 = freeze(map(Range(0, count), i => m(i))); // fix = 100; // var nums2 = freeze(map(Range(0, count), i => m(i))); // var matches = length(filter(zip(nums1, nums2, (a, b) => a == b), v => v)); // Assert.True(matches == count, "Numbers don't match (" + matches + " total matches, should be " + count + ")"); //} [Fact] public void ListMemoTest() { var vals = List(1,2,3,4,5).Memo(); Assert.True(vals.Sum() == 15); Assert.True(vals.Sum() == 15); } /* Uncomment this if you have time on your hands [Fact] public void MemoMemoryTest() { var mbStart = GC.GetTotalMemory(false) / 1048576L; Func fn = x => x.ToString(); var m = fn.Memo(); Range(0, Int32.MaxValue).Iter(i => m(i)); var mbFinish = GC.GetTotalMemory(false) / 1048576L; Assert.True(mbFinish - mbStart < 30); } */ } ================================================ FILE: LanguageExt.Tests/MemoryConsoleTests.cs ================================================ using System; using Xunit; using LanguageExt.Sys.Test; using Console = LanguageExt.Sys.Console; namespace LanguageExt.Tests; public class MemoryConsoleTests { [Theory] [InlineData("abc")] [InlineData("abc\ndef")] [InlineData("abc\ndef\nghi")] [InlineData("abc\n\n")] [InlineData("abc\ndef\n")] public void Write_line(string unsplitLines) { var lines = unsplitLines.Split('\n').AsIterable(); using var rt = Runtime.New(); var xs = lines.Traverse(Either.Right); var comp = lines.Traverse(Console.writeLine).As(); comp.Run(rt, EnvIO.New()).ThrowIfFail(); var clines = rt.Env.Console.AsIterable(); Assert.True(lines == clines, $"sequences don't match {lines} != {clines}"); } [Theory] [InlineData("abc")] [InlineData("abc\ndef")] [InlineData("abc\ndef\nghi")] [InlineData("abc\n\n")] [InlineData("abc\ndef\n")] public void Read_line_followed_by_write_line(string unsplitLines) { // Prep the runtime and the keyboard buffer with the typed lines using var rt = Runtime.New(); var lines = unsplitLines.Split('\n').AsIterable().ToSeq(); lines.Iter(line => rt.Env.Console.WriteKeyLine(line)); // test var comp = repeat(from l in Console.readLine from _ in Console.writeLine(l) select unit) | unitEff; // run and assert comp.Run(rt, EnvIO.New()).ThrowIfFail(); Assert.True(lines == rt.Env.Console.AsIterable().ToSeq(), "sequences don't match"); } } ================================================ FILE: LanguageExt.Tests/MemoryFSTests.cs ================================================ /* using Xunit; using System.IO; using LanguageExt.Sys.Test; using System.Threading.Tasks; using LanguageExt.Sys; using LanguageExt.UnsafeValueAccess; using File = LanguageExt.Sys.IO.File, LanguageExt.Sys.Test.Runtime>; using Dir = LanguageExt.Sys.IO.Directory, LanguageExt.Sys.Test.Runtime>; namespace LanguageExt.Tests; public class MemoryFSTests { [Fact] public void Write_and_read_file() { var root = MemoryFS.IsUnix ? "/" : "C:\\"; var rt = Runtime.New(); var comp = from _ in File.writeAllText($"{root}test.txt", "Hello, World") from t in File.readAllText($"{root}test.txt") select t; var r = comp.As().Run(rt, EnvIO.New()); Assert.True(r == "Hello, World", FailMsg(r)); } [Fact] public void Write_to_non_existent_folder() { var rt = Runtime.New(); var comp = from _ in File.writeAllText("C:\\non-exist\\test.txt", "Hello, World") from t in File.readAllText("C:\\non-exist\\test.txt") select t; var r = comp.As().Run(rt, EnvIO.New()); Assert.True(r.IsFail); } [Fact] public void Create_folder_write_and_read_file() { var root = MemoryFS.IsUnix ? "/non-exist/" : "C:\\non-exist\\"; var rt = Runtime.New(); var comp = from _1 in Dir.create(root) from _2 in File.writeAllText($"{root}test.txt", "Hello, World") from tx in File.readAllText($"{root}test.txt") select tx; var r = comp.As().Run(rt, EnvIO.New()); Assert.True(r == "Hello, World", FailMsg(r)); } [Fact] public void Create_nested_folder_write_and_read_file() { var root = MemoryFS.IsUnix ? "/non-exist/also-non-exist" : "C:\\non-exist\\also-non-exist"; var rt = Runtime.New(); var comp = from _1 in Dir.create(root) from _2 in File.writeAllText(Path.Combine(root, "test.txt"), "Hello, World") from tx in File.readAllText(Path.Combine(root, "test.txt")) select tx; var r = comp.As().Run(rt, EnvIO.New()); var eq = r == "Hello, World"; Assert.True(eq, FailMsg(r)); } [Fact] public void Create_nested_folder_write_two_files_enumerate_one_of_them_using_star_pattern() { var fldr = MemoryFS.IsUnix ? "/non-exist/also-non-exist" : "C:\\non-exist\\also-non-exist"; var root = MemoryFS.IsUnix ? "/" : "C:\\"; var rt = Runtime.New(); var comp = from _1 in Dir.create(fldr) from _2 in File.writeAllText(Path.Combine(fldr, "test.txt"), "Hello, World") from _3 in File.writeAllText(Path.Combine(fldr, "test.bat"), "Goodbye, World") from en in Dir.enumerateFiles(root, "*.txt", SearchOption.AllDirectories) from tx in File.readAllText(en.Head.ValueUnsafe()!) select (tx, en.Count); var r = comp.As().Run(rt, EnvIO.New()); Assert.True(r == ("Hello, World", 1), FailMsg(r)); } [Fact] public void Create_nested_folder_write_two_files_enumerate_one_of_them_using_qm_pattern() { var fldr = MemoryFS.IsUnix ? "/non-exist/also-non-exist" : "C:\\non-exist\\also-non-exist"; var root = MemoryFS.IsUnix ? "/" : "C:\\"; var rt = Runtime.New(); var comp = from _1 in Dir.create(fldr) from _2 in File.writeAllText(Path.Combine(fldr, "test.txt"), "Hello, World") from _3 in File.writeAllText(Path.Combine(fldr, "test.bat"), "Goodbye, World") from en in Dir.enumerateFiles(root, "????.txt", SearchOption.AllDirectories) from tx in File.readAllText(en.Head.ValueUnsafe()!) select (tx, en.Count); var r = comp.As().Run(rt, EnvIO.New()); Assert.True(r == ("Hello, World", 1), FailMsg(r)); } [Fact] public void Create_nested_folder_write_two_files_enumerate_fail_using_singular_qm_pattern() { var rt = Runtime.New(); var comp = from _1 in Dir.create("C:\\non-exist\\also-non-exist") from _2 in File.writeAllText("C:\\non-exist\\also-non-exist\\test.txt", "Hello, World") from _3 in File.writeAllText("C:\\non-exist\\also-non-exist\\test.bat", "Goodbye, World") from en in Dir.enumerateFiles("C:\\", "?.txt", SearchOption.AllDirectories) from tx in File.readAllText(en.Head.ValueUnsafe()!) select (tx, en.Count); var r = comp.As().Run(rt, EnvIO.New()); Assert.True(r.IsFail); } [Theory] [InlineData("C:\\non-exist", "/non-exist")] [InlineData("C:\\non-exist\\also-non-exist", "/non-exist/also-non-exist")] [InlineData("C:\\a\\b\\c\\d", "/a/b/c/d")] public void Create_and_delete_folder(string windowsPath, string linuxPath) { var path = MemoryFS.IsUnix ? linuxPath : windowsPath; var rt = Runtime.New(); var comp = from p in Pure(path) from _1 in Dir.create(p) from e1 in Dir.exists(p) from _2 in Dir.delete(p) from e2 in Dir.exists(p) select (e1, e2); var r = comp.As().Run(rt, EnvIO.New()); Assert.True(r == (true, false), FailMsg(r)); } [Theory] [InlineData("C:\\non-exist")] [InlineData("C:\\non-exist\\also-non-exist")] [InlineData("D:\\a\\b\\c\\d")] public void Delete_non_existent_folder(string path) { var rt = Runtime.New(); var comp = from p in SuccessEff(path) from _ in Dir.delete(p) select unit; var r = comp.As().Run(rt, EnvIO.New()); Assert.True(r.IsFail); } [Theory] [InlineData(false, "C:\\non-exist", "C:\\non-exist\\test.txt")] [InlineData(false, "C:\\non-exist\\also-non-exist", "C:\\non-exist\\also-non-exist\\test.txt")] [InlineData(true, "/non-exist", "/non-exist/test.txt")] [InlineData(true, "/non-exist/also-non-exist", "/non-exist/also-non-exist/test.txt")] public void File_exists(bool isUnix, string folder, string file) { if (MemoryFS.IsUnix != isUnix) return; var rt = Runtime.New(); var comp = from _1 in Dir.create(folder) from _2 in File.writeAllText(file, "Hello, World") from ex in File.exists(file) select ex; var r = comp.As().Run(rt, EnvIO.New()); Assert.True(r == true, FailMsg(r)); } [Theory] [InlineData("C:\\non-exist\\test.txt")] [InlineData("C:\\non-exist\\also-non-exist\\test.txt")] [InlineData("X:\\non-exist\\also-non-exist\\test.txt")] [InlineData("X:\\test.txt")] public void File_doesnt_exist(string file) { var rt = Runtime.New(); var comp = from _2 in File.writeAllText(file, "Hello, World") from ex in File.exists(file) select ex; var r = comp.As().Run(rt, EnvIO.New()); Assert.True(r.IsFail); } [Theory] [InlineData(false, "C:\\", "C:\\a\\b\\c\\d", new[] {"C:\\a", "C:\\a\\b", "C:\\a\\b\\c", "C:\\a\\b\\c\\d" }, "*")] [InlineData(false, "C:\\", "C:\\a\\b\\c", new[] {"C:\\a", "C:\\a\\b", "C:\\a\\b\\c"}, "*")] [InlineData(false, "C:\\", "C:\\a\\b", new[] {"C:\\a", "C:\\a\\b"}, "*")] [InlineData(false, "C:\\", "C:\\a", new[] {"C:\\a"}, "*")] [InlineData(false, "C:\\", "C:\\and\\all\\the\\arrows", new[] {"C:\\and", "C:\\and\\all", "C:\\and\\all\\the\\arrows" }, "*a*")] [InlineData(true, "/", "/a/b/c/d", new[] {"/a", "/a/b", "/a/b/c", "/a/b/c/d" }, "*")] [InlineData(true, "/", "/a/b/c", new[] {"/a", "/a/b", "/a/b/c"}, "*")] [InlineData(true, "/", "/a/b", new[] {"/a", "/a/b"}, "*")] [InlineData(true, "/", "/a", new[] {"/a"}, "*")] [InlineData(true, "/", "/and/all/the/arrows", new[] {"/and", "/and/all", "/and/all/the/arrows" }, "*a*")] public void Enumerate_folders(bool isUnix, string root, string path, string[] folders, string pattern) { if (MemoryFS.IsUnix != isUnix) return; var rt = Runtime.New(); var comp = from _1 in Dir.create(path) from ds in Dir.enumerateDirectories(root, pattern, SearchOption.AllDirectories) select ds.Strict(); var r = comp.As().Run(rt, EnvIO.New()); Assert.True(r == toSeq(folders)); } [Theory] [InlineData(false, "C:\\a\\b", "C:\\c\\d", "test.txt")] [InlineData(false, "C:\\", "C:\\c\\d", "test.txt")] [InlineData(true, "/a/b", "/c/d", "test.txt")] [InlineData(true, "/", "/c/d", "test.txt")] public void Move_file_from_one_folder_to_another(bool isUnix, string srcdir, string destdir, string file) { if (MemoryFS.IsUnix != isUnix) return; var rt = Runtime.New(); var comp = from _1 in Dir.create(srcdir) from _2 in Dir.create(destdir) from sf in Pure(Path.Combine(srcdir, file)) from df in Pure(Path.Combine(destdir, file)) from _3 in File.writeAllText(sf, "Hello, World") from _4 in Dir.move(sf, df) from tx in File.readAllText(df) from ex in File.exists(sf) select (ex, tx); var r = comp.As().Run(rt, EnvIO.New()); Assert.True(r == (false, "Hello, World"), FailMsg(r)); } static string FailMsg(Fin ma) => ma.Match(Succ: _ => "", Fail: e => e.Message); } */ ================================================ FILE: LanguageExt.Tests/MonadTests.cs ================================================ using Xunit; using System; using static LanguageExt.List; namespace LanguageExt.Tests; public class MonadTests { [Fact] public void WriterTest() { var tell1 = fun((string x) => tell(List(x))); var value = fun((int x) => from _ in tell1($"Got number: {x}") select x); var multWithLog = from a in value(3) from b in value(5) from _ in tell1("Gonna multiply these two") select a * b; var res = multWithLog.Run(); Assert.True(length(res.Output) == 3); Assert.True(res.Value == 15); Assert.True(head(res.Output) == "Got number: 3"); Assert.True(head(tail(res.Output)) == "Got number: 5"); Assert.True(head(tail(tail(res.Output))) == "Gonna multiply these two"); } static Writer, int> writer(int value, Seq output) => Writer.write(value, output); static Writer, Seq> multWithLog(Seq input) => from _ in writer(0, Seq("Start")) from r in input.Traverse(i => writer(i * 10, Seq($"Number: {i}"))) select r; [Fact] public void WriterSequenceTest() { var res = multWithLog(Seq(1, 2, 3)).Run(); Assert.True(res.Value == Seq(10, 20, 30)); Assert.True(res.Output == Seq("Start", "Number: 1", "Number: 2", "Number: 3")); } private class Bindings { public readonly Map Map; public Bindings(params (string, int)[] items) => Map = toMap(items); public static Bindings New(params (string, int)[] items) => new (items); } [Fact] public void ReaderAskTest() { var lookupVar = fun((string name, Bindings bindings) => Map.find(bindings.Map, name).IfNone(0)); var calcIsCountCorrect = from count in Reader.asks((Bindings env) => lookupVar("count", env)) from bindings in ask() select count == Map.length(bindings.Map); var sampleBindings = Bindings.New(("count", 3), ("1", 1), ("b", 2)); bool res = calcIsCountCorrect.Run(sampleBindings); Assert.True(res); } [Fact] public void ReaderLocalTest() { var calculateContentLen = from content in Reader.ask() select content.Length; var calculateModifiedContentLen = Reader.local(content => "Prefix " + content, calculateContentLen); var s = "12345"; var modifiedLen = calculateModifiedContentLen.Run(s); var len = calculateContentLen.Run(s); Assert.True(modifiedLen == 12); Assert.True(len == 5); } [Fact] public void StateTest() { var comp = from inp in State.get() let hw = inp + ", world" from _ in State.put(hw) select hw.Length; var r = comp.Run("hello"); Assert.True(r.Value == 12); Assert.True(r.State == "hello, world"); } // TODO: Restore when traits are complete /* [Fact] public void RWSTest() { var previous = RWS, bool, Lst, Map, int>(1); var comp = from val in previous from state in get, bool, Lst, Map, int>() from env in ask, bool, Lst, Map>() from _ in put, bool, Lst, Map, int>(state.Add(2, "B")) from __ in tell, bool, Lst, Map, int>(List($"{val}", $"{env}")) select val + 2; var (value, output, finalState, faulted) = comp(true, Map((1, "a"))); Assert.True(value == 3); Assert.True(output == List("1", "True")); Assert.True(finalState == Map((1, "a"), (2, "B"))); } [Fact] public void RWSFailTest() { var previous = RWS, bool, Lst, Map, int>(1); var comp = from val in previous from _ in modify, bool, Lst, Map, int>(_ => failwith>("")) select val + 2; Assert.ThrowsAny(act(comp.Run(false, Map((1, "a"))).IfFailThrow)); } [Fact] public void RWSBottomTest() { var previous = RWS, bool, Lst, Map, int>(1); var comp = from val in previous where false select val + 3; Assert.ThrowsAny(act(comp.Run(false, Map((1, "a"))).IfFailThrow)); } */ //[Fact] //public void ReaderWriterBindTest() //{ // var x = from a in ask() // from b in tell("Hello " + a) // select b; // Assert.True(x("everyone").Value().Output.Head() == "Hello everyone"); //} //[Fact] //public void TryReaderBindTest() //{ // var tryadd = from a in Try(() => 123) // from b in ask() // select a + b; // var res = tryadd.Map(r => r.Lift(123)) // .Lift(); // Assert.True(res == 246); //} //[Fact] //public void SomeLiftTest() //{ // var z = Some(Some(10)); // var r = z.LiftT(); // Assert.True(r == 10); //} //[Fact] //public void FilterTTest() //{ // var o = Some(List(1, 2, 3, 4, 5)); // var o2 = o.FilterT(n => n > 2); // Assert.True(o2.Count() == 1); // Assert.True(o2.CountT() == 3); //} //[Fact] //public void ReaderListSumTest() //{ // var r2 = from v in ask() // from l in List(1, 2, 3, 4, 5) // select l * v; // var r3 = r2.SumT()(10); // Assert.True(r3 == 150); //} //private static string Error() //{ // throw new Exception("Nooooo"); //} } ================================================ FILE: LanguageExt.Tests/Multiplicable.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Xunit; using static LanguageExt.Prelude; using LanguageExt.ClassInstances; namespace LanguageExt.Tests { public class Multiplicable { [Fact] public void OptionalNumericMultiply() { var x = Some(10); var y = Some(20); var z = product(x, y); Assert.True(z == 200); } } } ================================================ FILE: LanguageExt.Tests/NullChecks/AbstractNullCheckTests.cs ================================================ using Xunit; namespace LanguageExt.Tests.NullChecks { public abstract class AbstractNullCheckTests { protected abstract bool ExpectedWhenNull { get; } protected abstract bool NullCheck(T value); private bool ExpectedWhenNotNull => !ExpectedWhenNull; [Fact] public void NullCheck_NullObject_AsExpectedWhenNull() { object value = null; var actual = NullCheck(value); Assert.Equal(ExpectedWhenNull, actual); } [Fact] public void NullCheck_NonNullObject_AsExpectedWhenNotNull() { object value = new object(); var actual = NullCheck(value); Assert.Equal(ExpectedWhenNotNull, actual); } [Fact] public void NullCheck_NullString_AsExpectedWhenNull() { string value = null; var actual = NullCheck(value); Assert.Equal(ExpectedWhenNull, actual); } [Fact] public void NullCheck_HelloString_AsExpectedWhenNotNull() { string value = "hello"; var actual = NullCheck(value); Assert.Equal(ExpectedWhenNotNull, actual); } [Fact] public void NullCheck_NullCustomClass_AsExpectedWhenNull() { FooClass value = null; var actual = NullCheck(value); Assert.Equal(ExpectedWhenNull, actual); } [Fact] public void NullCheck_DefaultConstructorCustomClass_AsExpectedWhenNotNull() { FooClass value = new FooClass(); var actual = NullCheck(value); Assert.Equal(ExpectedWhenNotNull, actual); } [Fact] public void NullCheck_NullNullableByte_AsExpectedWhenNull() { byte? value = null; var actual = NullCheck(value); Assert.Equal(ExpectedWhenNull, actual); } [Fact] public void NullCheck_ZeroNullableByte_AsExpectedWhenNotNull() { byte? value = 0; var actual = NullCheck(value); Assert.Equal(ExpectedWhenNotNull, actual); } [Fact] public void NullCheck_ZeroInt_AsExpectedWhenNotNull() { int value = 0; var actual = NullCheck(value); Assert.Equal(ExpectedWhenNotNull, actual); } [Fact] public void NullCheck_DefaultCustomEnum_AsExpectedWhenNull() { FooEnum value = default(FooEnum); var actual = NullCheck(value); Assert.Equal(ExpectedWhenNotNull, actual); } [Fact] public void NullCheck_DefaultConstructorCustomStruct_AsExpectedWhenNull() { FooStruct value = new FooStruct(); var actual = NullCheck(value); Assert.Equal(ExpectedWhenNotNull, actual); } private class FooClass { } private enum FooEnum { } private struct FooStruct { } } } ================================================ FILE: LanguageExt.Tests/NullChecks/IsNullExtensionTests.cs ================================================ namespace LanguageExt.Tests.NullChecks { public class IsNullExtensionTests : AbstractNullCheckTests { protected override bool ExpectedWhenNull => true; protected override bool NullCheck(T value) => value.IsNull(); } } ================================================ FILE: LanguageExt.Tests/NullChecks/isnullPreludeTests.cs ================================================ namespace LanguageExt.Tests.NullChecks { public class isnullPreludeTests : AbstractNullCheckTests { protected override bool ExpectedWhenNull => true; protected override bool NullCheck(T value) => Prelude.isnull(value); } } ================================================ FILE: LanguageExt.Tests/NullChecks/notnullPreludeTests.cs ================================================ namespace LanguageExt.Tests.NullChecks { public class notnullPreludeTests : AbstractNullCheckTests { protected override bool ExpectedWhenNull => false; protected override bool NullCheck(T value) => Prelude.notnull(value); } } ================================================ FILE: LanguageExt.Tests/OptionApply.cs ================================================ using System; using Xunit; using LanguageExt.ClassInstances; using static LanguageExt.Prelude; namespace LanguageExt.Tests; public class OptionApply { Func add = (a, b) => a + b; [Fact] public void ApplySomeArgs() { var opt = Some(add).Apply(Some(3)).Apply(Some(4)); Assert.Equal(Some(7), opt); } [Fact] public void ApplySomeArgsF() { var opt = map(add, Some(3)).Apply(Some(4)); Assert.Equal(Some(7), opt); } [Fact] public void ApplySomeArgsF2() { var opt = Some(add).Apply(Some(3)).Apply(Some(4)); Assert.True(equals(Some(7), opt)); } [Fact] public void ApplyNoneArgs() { var opt = add.Map(Option.None).Apply(Some(4)); Assert.Equal(None, opt); } [Fact] public void ApplyNoneArgsF() { var opt = apply(apply(Some(add), Option.None), Some(4)); Assert.Equal(None, opt); } [Fact] public void ApplyNoneArgsF2() { var opt = Some(add).Apply(Option.None).Apply(Some(4)); Assert.True(equals(Option.None, opt)); } [Fact] public void ApplicativeLawHolds() { var first = Some(add) .Apply(Some(3)) .Apply(Some(4)); var second = Some(3) .ParMap(add) .Apply(Some(4)); Assert.Equal(first, second); } } ================================================ FILE: LanguageExt.Tests/OptionCoalesceTests.cs ================================================ using Xunit; namespace LanguageExt.Tests; public class OptionCoalesceTests { [Fact] public void OptionCoalesceTest1() { var optional = Some(123); var value = optional || 456; Assert.True(value == 123); } [Fact] public void OptionCoalesceTest2() { Option optional = None; var value = optional || 456; Assert.True(value == 456); } [Fact] public void OptionCoalesceTest3() { Option optional1 = None; Option optional2 = None; var value = optional1 || optional2 || 456; Assert.True(value == 456); } [Fact] public void OptionUnsafeCoalesceTest1() { var optional = Some(123); var value = optional || 456; Assert.True(value == 123); } } ================================================ FILE: LanguageExt.Tests/OptionLazy.cs ================================================ //using Xunit; //using System; //using System.Collections.Generic; //using LanguageExt; //using static LanguageExt.Prelude; //namespace LanguageExt.Tests //{ // public class OptionLazyTests // { // [Fact] // public void OptionLazyTest1() // { // var items = List(); // var option = from w in Some(_ => { items = items.Add(5); return 5; }) // from x in Some(_ => { items = items.Add(10); return 10; }) // from y in Some(_ => { items = items.Add(15); return 15; }) // from z in Some(_ => { items = items.Add(20); return 20; }) // select w + x + y + z; // Assert.True(items.Count == 0); // var value = option.IfNone(0); // Assert.True(items.Count == 4); // Assert.True(value == 50); // var value2 = option.IfNone(0); // var value3 = option.IfNone(0); // var value4 = option.IfNone(0); // var value5 = option.IfNone(0); // var value6 = option.IfNone(0); // Assert.True(items.Count == 4); // Assert.True(value6 == 50); // } // [Fact] // public void OptionLazyStrictCombinedTest1() // { // var items = List(); // var option = from w in Some(_ => { items = items.Add(5); return 5; }) // from x in Some(_ => { items = items.Add(10); return 10; }) // from y in Some(_ => { items = items.Add(15); return 15; }) // from z in Some(_ => { items = items.Add(20); return 20; }) // from i in Some(30) // select w + x + y + z + i; // Assert.True(items.Count == 0); // var value = option.IfNone(0); // Assert.True(items.Count == 4); // Assert.True(value == 80); // var value2 = option.IfNone(0); // var value3 = option.IfNone(0); // var value4 = option.IfNone(0); // var value5 = option.IfNone(0); // var value6 = option.IfNone(0); // Assert.True(items.Count == 4); // Assert.True(value6 == 80); // } // [Fact] // public void OptionLazyStrictCombinedTest2() // { // var items = List(); // var option = from i in Some(30) // from w in Some(_ => { items = items.Add(5); return 5; }) // from x in Some(_ => { items = items.Add(10); return 10; }) // from y in Some(_ => { items = items.Add(15); return 15; }) // from z in Some(_ => { items = items.Add(20); return 20; }) // select w + x + y + z + i; // Assert.True(items.Count == 0); // var value = option.IfNone(0); // Assert.True(items.Count == 4); // Assert.True(value == 80); // var value2 = option.IfNone(0); // var value3 = option.IfNone(0); // var value4 = option.IfNone(0); // var value5 = option.IfNone(0); // var value6 = option.IfNone(0); // Assert.True(items.Count == 4); // Assert.True(value6 == 80); // } // [Fact] // public void OptionStrictOnlyTest() // { // var items = List(); // var option = from t in Some(30) // let _a = items = items.Add(5) // from u in Some(30) // let _b = items = items.Add(10) // from v in Some(30) // let _c = items = items.Add(15) // select t * u * v; // Assert.True(!option.IsLazy); // Assert.True(items.Count == 3); // var res = option.IfNone(0); // Assert.True(res == 27000); // } // [Fact] // public void OptionLazyMapTest() // { // var n = 0; // var x = Some(_ => ++n); // Assert.True(x.IsLazy); // Assert.True(n == 0); // var y = x.Map(v => v * 10); // Assert.True(x.IsLazy); // Assert.True(n == 0); // Assert.True(y == 10); // Assert.True(n == 1); // } // [Fact] // public void OptionLazyFilter() // { // var items = List(); // var option = from i in Some(30) // where i > 10 // from w in Some(_ => { items = items.Add(5); return 5; }) // where w > 4 // from x in Some(_ => { items = items.Add(10); return 10; }) // where x > 9 // from y in Some(_ => { items = items.Add(15); return 15; }) // where y > 14 // from z in Some(_ => { items = items.Add(20); return 20; }) // where z > 19 // select w + x + y + z + i; // Assert.True(items.Count == 0); // var value = option.IfNone(0); // Assert.True(items.Count == 4); // Assert.True(value == 80); // var value2 = option.IfNone(0); // var value3 = option.IfNone(0); // var value4 = option.IfNone(0); // var value5 = option.IfNone(0); // var value6 = option.IfNone(0); // Assert.True(items.Count == 4); // Assert.True(value6 == 80); // } // } //} ================================================ FILE: LanguageExt.Tests/OptionTTests.cs ================================================ using Xunit; using System.Linq; using LanguageExt.ClassInstances; namespace LanguageExt.Tests; public class OptionTTests { [Fact] public void TestOptionT2() { var list = List(Some(1), Some(2), Some(3), Some(4), Some(5)); var newlist = list.KindT, int>() .BindT(x => x % 2 == 0 ? Some(x) : None) .AsT, int>().As(); Assert.True(newlist == List(None, Some(2), None, Some(4), None)); } [Fact] public void TestOptionT3() { var list = List(Some(1), Some(2), Some(3), Some(4), Some(5)); var result = list.KindT, int>() .SumT(); Assert.True(result == 15); } [Fact] public void TestOptionT4() { var list = List(Some(1), Some(2), Some(3), Some(4), Some(5)); var result = list.KindT, int>() .CountT(); Assert.True(result == 5); } [Fact] public void WrappedListTest() { var opt = Some(List(1, 2, 3, 4, 5)); var res = opt.KindT, int>().FoldT(0, (s, v) => s + v); var mopt = opt.KindT, int>().MapT(x => x * 2); var mres = mopt.FoldT(0, (s, v) => s + v); Assert.True(res == 15, "Expected 15, but got " + res); Assert.True(mres == 30, "Expected 30, but got " + mres); Assert.True(opt.KindT, int>().CountT() == 5, "opt != 5, (" + opt.KindT, int>().CountT() + ")"); Assert.True(mopt.CountT() == 5, "mopt != 5, (" + mopt.CountT() + ")"); opt = None; res = opt.KindT, int>().FoldT(0, (s, v) => s + v); Assert.True(res == 0, "res != 0, got " + res); Assert.True(opt.KindT, int>().CountT() == 0, "opt.Count() != 0, got " + opt.KindT, int>().CountT()); } //[Fact] //public void WrappedMapTest() //{ // var opt = Some(Map(Tuple(1, "A"), Tuple(2, "B"), Tuple(3, "C"), Tuple(4, "D"), Tuple(5, "E"))); // var res = opt.FoldT("", (s, v) => s + v); // var mopt = opt.MapT(x => x.ToLower()); // var mres = mopt.FoldT("", (s, v) => s + v); // Assert.True(res == "ABCDE"); // Assert.True(opt.CountT() == 5); // Assert.True(mopt.CountT() == 5); // match(mopt, // Some: x => // { // Assert.True(x[1] == "a"); // Assert.True(x[2] == "b"); // Assert.True(x[3] == "c"); // Assert.True(x[4] == "d"); // Assert.True(x[5] == "e"); // }, // None: () => Assert.False(true) // ); //} [Fact] public void WrappedListLinqTest() { var opt = Some(List(1, 2, 3, 4, 5)); var res = from x in opt.ToIterable() from y in x select y * 2; var total = res.Sum(); Assert.True(total == 30); } [Fact] public void WrappedMapLinqTest() { var opt = Some(Map((1, "A"), (2, "B"), (3, "C"), (4, "D"), (5, "E"))); var res = from x in opt.ToIterable() from y in x.AsIterable() select y.Value.ToLower(); var fd = res.Fold("", (s, x) => s + x); Assert.True(fd == "abcde"); } [Fact] public void WrappedOptionOptionLinqTest() { var opt = Some(Some(Some(100))); var res = from x in opt from y in x from z in y select z * 2; Assert.True(equals(res, Some(200))); opt = Some(Some>(None)); var res2 = from x in opt from y in x from z in y select z * 2; Assert.False(equals(res, Some(0))); } [Fact] public void WrappedOptionLinqTest() { var opt = Some(Some(100)); var res = from x in opt from y in x select y * 2; Assert.True(res.Map(x => x == 200).IfNone(false)); opt = Some>(None); res = from x in opt from y in x select y * 2; var bopt = res.Map(x => x == 1); Assert.True(bopt.IfNone(true)); } //[Fact] //public void WrappedEitherLinqTest() //{ // var opt = Some(Right(100)); // var res = from x in opt // from y in x // select y * 2; // Assert.True(res.LiftT() == 200); // opt = Some(Left("left")); // res = from x in opt // from y in x // select y * 2; // Assert.True(res.LiftT() == 0); //} [Fact] public void WrappedListOfOptionsTest2() { var opt = List(Some(1), Some(2), Some(3), Some(4), Some(5)); opt = opt.KindT, int>() .FoldT(Lst>.Empty, (xs, x) => x > 2 ? xs.Add(x) : xs); // .FilterT(x => x > 2); <-- TODO: Above was this -- should we restore a filter operation? Can we even? Assert.True(opt.Count() == 3, "Count should be 3, is: " + opt.Count()); Assert.True(equals(opt[0], Some(3)), "opt[0] != Some(3), Is: " + opt[0]); Assert.True(equals(opt[1], Some(4)), "opt[1] != Some(4), Is: " + opt[1]); Assert.True(equals(opt[2], Some(5)), "opt[2] != Some(5), Is: " + opt[2]); } } ================================================ FILE: LanguageExt.Tests/OptionTests.cs ================================================ using Xunit; using System; using System.Collections.Generic; using static LanguageExt.Prelude; namespace LanguageExt.Tests { public class OptionTests { [Fact] public void SomeGeneratorTestsObject() { var optional = Some(123); optional.Match(Some: i => Assert.True(i == 123), None: () => Assert.Fail("Shouldn't get here")); int c = optional.Match(Some: i => i + 1, None: () => 0); Assert.True(c == 124); } [Fact] public void SomeGeneratorTestsFunction() { var optional = Some(123); match(optional, Some: i => Assert.True(i == 123), None: () => Assert.Fail("Shouldn't get here")); int c = match(optional, Some: i => i + 1, None: () => 0); Assert.True(c == 124); } [Fact] public void NoneGeneratorTestsObject() { Option optional = None; optional.Match(Some: i => Assert.Fail("Shouldn't get here"), None: () => Assert.True(true)); int c = optional.Match(Some: i => i + 1, None: () => 0); Assert.True(c == 0); } [Fact] public void NoneGeneratorTestsFunction() { Option optional = None; match(optional, Some: i => Assert.Fail("Shouldn't get here"), None: () => Assert.True(true)); int c = match(optional, Some: i => i + 1, None: () => 0); Assert.True(c == 0); } [Fact] public void SomeLinqTest() { var two = Some(2); var four = Some(4); var six = Some(6); var expr = from x in two from y in four from z in six select x + y + z; match(expr, Some: v => Assert.True(v == 12), None: failwith("Shouldn't get here")); } [Fact] public void NoneLinqTest() { var two = Some(2); var four = Some(4); var six = Some(6); Option none = None; match(from x in two from y in four from _ in none from z in six select x + y + z, Some: v => failwith("Shouldn't get here"), None: () => Assert.True(true)); } [Fact] public void OptionFluentSomeNoneTest() { int res1 = GetValue(true) .Some(x => x + 10) .None(0); int res2 = GetValue(false) .Some(x => x + 10) .None(() => 0); Assert.True(res1 == 1010); Assert.True(res2 == 0); } [Fact] public void NullableTest() { var res = GetNullable(true) .Some(v => v) .None(() => 0); Assert.True(res == 1000); } [Fact] public void NullableDenySomeNullTest() { Assert.Throws( () => { var res = GetNullable(false) .Some(v => v) .None(() => 0); } ); } [Fact] public void BiIterSomeTest() { var x = Some(3); int way = 0; var dummy = x.BiIter(_ => way = 1, () => way = 2); Assert.Equal(1, way); } [Fact] public void BiIterNoneTest() { var x = Option.None; int way = 0; var dummy = x.BiIter(_ => way = 1, () => way = 2); Assert.Equal(2, way); } [Fact] public void IfNoneSideEffect() { int sideEffectResult = 0; Action sideEffectNone = () => sideEffectResult += 1; Assert.Equal(0, Option.Some("test").IfNone(sideEffectNone).Return(sideEffectResult)); Assert.Equal(1, Option.None.IfNone(sideEffectNone).Return(sideEffectResult)); } [Fact] public void ISomeSideEffect() { int sideEffectResult = 0; Action sideEffectSome = _ => sideEffectResult += 2; Assert.Equal(0, Option.None.IfSome(sideEffectSome).Return(sideEffectResult)); Assert.Equal(2, Option.Some("test").IfSome(sideEffectSome).Return(sideEffectResult)); } [Fact] public void OptionToFin() { var e = LanguageExt.Common.Error.New("Example error"); var some = Some(123); var none = Option.None; var mx = Fin.Succ(123); var my = some.ToFin(); var me = none.ToFin(e); var e2 = mx.Equals(my); var e1 = mx == my; var e3 = mx.Equals((object)my); Assert.True(e1); Assert.True(e2); Assert.True(e3); Assert.True(me.IsFail); } private Option GetStringNone() { string? nullStr = null; return Some(nullStr); } private Option GetNullable(bool select) => select ? Some((int?)1000) : Some((int?)null); private Option GetValue(bool select) => select ? Some(1000) : None; private Option> GetSomeOptionValue(bool select) => select ? Some(Some(1000)) : Some(Option.None); private Option ImplicitConversion() => 1000; private Option ImplicitNoneConversion() => None; private void InferenceTest1() { Action actionint = v => v = v * 2; Option optional1 = Some(123); optional1.Some(actionint) //Compiler tries to call: public static Option Some(T value) .None(() => { }); } } } ================================================ FILE: LanguageExt.Tests/OptionUnsafeApply.cs ================================================ using System; using Xunit; using static LanguageExt.Prelude; namespace LanguageExt.Tests { public class OptionUnsafeApply { Func add = (a, b) => a + b; // TODO: Restore //[Fact] //public void ApplySomeArgs() //{ // var opt = SomeUnsafe(add) // .Apply(SomeUnsafe(3)) // .Apply(SomeUnsafe(4)); // Assert.Equal(SomeUnsafe(7), opt); //} //[Fact] //public void ApplySomeArgsF() //{ // var opt = apply(apply(SomeUnsafe(add), SomeUnsafe(3)), SomeUnsafe(4)); // Assert.Equal(SomeUnsafe(7), opt); //} //[Fact] //public void ApplyNoneArgs() //{ // var opt = SomeUnsafe(add) // .Apply(None) // .Apply(SomeUnsafe(4)); // Assert.Equal(None, opt); //} //[Fact] //public void ApplyNoneArgsF() //{ // var opt = apply(apply(SomeUnsafe(add), None), SomeUnsafe(4)); // Assert.Equal(None, opt); //} //[Fact] //public void ApplicativeLawHolds() //{ // var first = SomeUnsafe(add) // .Apply(SomeUnsafe(3)) // .Apply(SomeUnsafe(4)); // var second = SomeUnsafe(3) // .ParMap(add) // .Apply(SomeUnsafe(4)); // Assert.Equal(first, second); //} //[Fact] //public void ApplicativeLawHoldsF() //{ // var first = apply(apply(SomeUnsafe(add), SomeUnsafe(3)), SomeUnsafe(4)); // var second = apply(parmap(SomeUnsafe(3), add), SomeUnsafe(4)); // Assert.Equal(first, second); //} } } ================================================ FILE: LanguageExt.Tests/ParsecIOTests.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Xunit; using LanguageExt.Common; using LanguageExt.Parsec; using static LanguageExt.Prelude; using static LanguageExt.Parsec.PrimIO; using static LanguageExt.Parsec.ItemIO; using static LanguageExt.Parsec.IndentIO; using Char = System.Char; namespace LanguageExt.Tests { public class ParsecIOTests { class Tok { public Tok(int col, int ln, char val) { Col = col; Ln = ln; Val = val; } public readonly int Col; public readonly int Ln; public readonly char Val; } [Fact] public void Indent1SuccessTest() { var toks = Seq(new Tok(1, 1, 'a'), new Tok(2, 1, 'b'), new Tok(2, 2, 'c'), new Tok(1, 3, 'd'), new Tok(1, 4, '1')); var letter = satisfy(t => Char.IsLetter(t.Val)); var digit = satisfy(t => Char.IsDigit(t.Val)); var letters = many1(attempt(letter)); var digits = many1(attempt(digit)); var block = from ls1 in indented1>(letters) from ls2 in indented1>(letters) from dg1 in indented1>(digits) from ___ in eof() select (ls1, ls2, dg1); var res = block.Parse(toks, t => new Pos(t.Ln - 1, t.Col - 1)).ToEither().IfLeft(() => default); Assert.True(res.Item1.Count == 3); Assert.True(res.Item2.Count == 1); Assert.True(res.Item3.Count == 1); } } } ================================================ FILE: LanguageExt.Tests/ParsecTests.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Xunit; using M = LanguageExt.Map; using LanguageExt.Parsec; using static LanguageExt.Prelude; using static LanguageExt.Parsec.Prim; using static LanguageExt.Parsec.Char; using static LanguageExt.Parsec.Expr; using static LanguageExt.Parsec.Token; using static LanguageExt.UnitsOfMeasure; using LanguageExt.ClassInstances; using Char = System.Char; namespace LanguageExt.Tests { public class ParsecTests { [Fact] public void MultiLineNestedComments() { var jstp = makeTokenParser(Language.JavaStyle.With(NestedComments: true)); var ws = jstp.WhiteSpace; var test3 = parse(ws, @" /* */ "); } [Fact] public void MultiLineComments() { var jstp = makeTokenParser(Language.JavaStyle.With(NestedComments: false)); var ws = jstp.WhiteSpace; var test3 = parse(ws, @" /* */ "); } [Fact] public void ResultComb() { var p = result(1234); var r = parse(p, "Hello"); Assert.False(r.IsFaulted); Assert.True(r.Reply.Result == 1234); } [Fact] public void ZeroComb() { var p = zero(); var r = parse(p, "Hello"); Assert.True(r.IsFaulted); } [Fact] public void ItemComb() { var p = anyChar; var r = parse(p, "Hello"); Assert.False(r.IsFaulted); Assert.True(r.Reply.Result == 'H'); Assert.True(r.Reply.State.ToString() == "ello"); } [Fact] public void ItemFailComb() { var p = anyChar; var r = parse(p, ""); Assert.True(r.IsFaulted); } [Fact] public void Item2Comb() { var p = anyChar; var r1 = parse(p, "Hello"); Assert.False(r1.IsFaulted); Assert.True(r1.Reply.Result == 'H'); Assert.True(r1.Reply.State.ToString() == "ello"); var r2 = parse(p, r1.Reply.State); Assert.False(r2.IsFaulted); Assert.True(r2.Reply.Result == 'e'); Assert.True(r2.Reply.State.ToString() == "llo"); } [Fact] public void Item1LinqComb() { var p = from x in anyChar select x; var r = parse(p, "Hello"); Assert.False(r.IsFaulted); Assert.True(r.Reply.Result == 'H'); Assert.True(r.Reply.State.ToString() == "ello"); } [Fact] public void Item2LinqComb() { var p = from x in anyChar from y in anyChar select (x, y); var r = parse(p, "Hello"); Assert.False(r.IsFaulted); Assert.True(r.Reply.Result.Item1 == 'H'); Assert.True(r.Reply.Result.Item2 == 'e'); Assert.True(r.Reply.State.ToString() == "llo"); } [Fact] public void EitherFirstComb() { var p = either(ch('a'), ch('1')); var r = parse(p, "a"); Assert.False(r.IsFaulted); Assert.True(r.Reply.Result == 'a'); Assert.True(r.Reply.State.ToString() == ""); } [Fact] public void EitherSecondComb() { var p = either(ch('a'), ch('1')); var r = parse(p, "1"); Assert.False(r.IsFaulted); Assert.True(r.Reply.Result == '1'); Assert.True(r.Reply.State.ToString() == ""); } [Fact] public void EitherLINQComb() { var p = from x in either(ch('a'), ch('1')) from y in either(ch('a'), ch('1')) select (x, y); var r = parse(p, "a1"); Assert.False(r.IsFaulted); Assert.True(r.Reply.Result.Item1 == 'a'); Assert.True(r.Reply.Result.Item2 == '1'); Assert.True(r.Reply.State.ToString() == ""); } [Fact] public void UpperComb() { var p = upper; var r = parse(p, "Hello"); Assert.False(r.IsFaulted); Assert.True(r.Reply.Result == 'H'); Assert.True(r.Reply.State.ToString() == "ello"); } [Fact] public void UpperFailComb() { var p = upper; var r = parse(p, "hello"); Assert.True(r.IsFaulted); } [Fact] public void LowerComb() { var p = Parsec.Char.lower; var r = parse(p, "hello"); Assert.False(r.IsFaulted); Assert.True(r.Reply.Result == 'h'); Assert.True(r.Reply.State.ToString() == "ello"); } [Fact] public void LowerFailComb() { var p = Parsec.Char.lower; var r = parse(p, "Hello"); Assert.True(r.IsFaulted); } [Fact] public void DigitComb() { var p = digit; var r = parse(p, "1234"); Assert.False(r.IsFaulted); Assert.True(r.Reply.Result == '1'); Assert.True(r.Reply.State.ToString() == "234"); } [Fact] public void DigitFailComb() { var p = digit; var r = parse(p, "Hello"); Assert.True(r.IsFaulted); } [Fact] public void LetterComb() { var p = letter; var r = parse(p, "hello"); Assert.False(r.IsFaulted); Assert.True(r.Reply.Result == 'h'); Assert.True(r.Reply.State.ToString() == "ello"); } [Fact] public void LetterFailComb() { var p = letter; var r = parse(p, "1ello"); Assert.True(r.IsFaulted); } [Fact] public void WordComb() { var p = asString(many1(letter)); var r = parse(p, "hello "); Assert.False(r.IsFaulted); Assert.True(r.Reply.Result == "hello"); Assert.True(r.Reply.State.ToString() == " "); } [Fact] public void WordFailComb() { var p = asString(many1(letter)); var r = parse(p, "1ello "); Assert.True(r.IsFaulted); } [Fact] public void StringMatchComb() { var p = str("hello"); var r = parse(p, "hello world"); Assert.False(r.IsFaulted); Assert.True(r.Reply.Result == "hello"); Assert.True(r.Reply.State.ToString() == " world"); } [Fact] public void StringMatchFailComb() { var p = str("hello"); var r = parse(p, "no match"); Assert.True(r.IsFaulted); } [Fact] public void StringOrdinalIgnoreCase() { var p = str("Hello"); Assert.Equal("hello", parse(p, "hello").ToOption()); Assert.Equal("Hello", parse(p, "Hello").ToOption()); Assert.Equal("HELLO" , parse(p, "HELLO").ToOption()); Assert.Equal("hELLO", parse(p, "hELLO").ToOption()); Assert.True(parse(p, "olleH").IsFaulted); Assert.True(parse(p, "Héllo").IsFaulted); } [Fact] public void NaturalNumberComb() { var tok = makeTokenParser(Language.HaskellStyle); var p = tok.Natural; var r = parse(p, "1234 "); Assert.False(r.IsFaulted); Assert.True(r.Reply.Result == 1234); Assert.True(r.Reply.State.ToString() == ""); } [Fact] public void NaturalNumberFailComb() { var tok = makeTokenParser(Language.HaskellStyle); var p = tok.Natural; var r = parse(p, "no match"); Assert.True(r.IsFaulted); } [Fact] public void IntegerNumberComb() { var tok = makeTokenParser(Language.HaskellStyle); var p = tok.Integer; var r = parse(p, "1234 "); Assert.False(r.IsFaulted); Assert.True(r.Reply.Result == 1234); Assert.True(r.Reply.State.ToString() == ""); } [Fact] public void IntegerNegativeNumberComb() { var tok = makeTokenParser(Language.HaskellStyle); var p = tok.Integer; var r = parse(p, "-1234 "); Assert.False(r.IsFaulted); Assert.True(r.Reply.Result == -1234); Assert.True(r.Reply.State.ToString() == ""); } [Fact] public void IntegerNumberFailComb() { var tok = makeTokenParser(Language.HaskellStyle); var p = tok.Integer; var r = parse(p, "no match"); Assert.True(r.IsFaulted); } [Fact] public void BracketAndIntegerComb() { var tok = makeTokenParser(Language.HaskellStyle); var p = from x in tok.Brackets(tok.Integer) from _ in tok.WhiteSpace select x; var r = parse(p, "[1] "); Assert.False(r.IsFaulted); Assert.True(r.Reply.Result == 1); Assert.True(r.Reply.State.ToString() == ""); } [Fact] public void BracketAndIntegerFailComb() { var tok = makeTokenParser(Language.HaskellStyle); var p = tok.Brackets(tok.Integer); var r = parse(p, "[x] "); Assert.True(r.IsFaulted); } [Fact] public void BracketAndIntegerListComb() { var tok = makeTokenParser(Language.HaskellStyle); var p = from x in tok.BracketsCommaSep(tok.Integer) from _ in tok.WhiteSpace select x; var r = parse(p, "[1,2,3,4] "); Assert.False(r.IsFaulted); var arr = r.Reply.Result.ToArray(); Assert.True(arr[0] == 1); Assert.True(arr[1] == 2); Assert.True(arr[2] == 3); Assert.True(arr[3] == 4); Assert.True(r.Reply.State.ToString() == ""); } [Fact] public void BracketAndSpacedIntegerListComb() { var tok = makeTokenParser(Language.HaskellStyle); var p = from x in tok.BracketsCommaSep(tok.Integer) from _ in tok.WhiteSpace select x; var r = parse(p, "[ 1, 2 ,3, 4] "); Assert.False(r.IsFaulted); var arr = r.Reply.Result.ToArray(); Assert.True(arr[0] == 1); Assert.True(arr[1] == 2); Assert.True(arr[2] == 3); Assert.True(arr[3] == 4); Assert.True(r.Reply.State.ToString() == ""); } [Fact] public void BracketAndIntegerListFailComb() { var tok = makeTokenParser(Language.HaskellStyle); var p = tok.BracketsCommaSep(tok.Integer); var r = parse(p, "[1,x,3,4] "); Assert.True(r.IsFaulted); } [Fact] public void JunkEmptyComb() { var tok = makeTokenParser(Language.HaskellStyle); var p = tok.WhiteSpace; var r = parse(p, ""); Assert.False(r.IsFaulted); Assert.True(r.Reply.Result == unit); } [Fact] public void JunkNoMatchComb() { var tok = makeTokenParser(Language.HaskellStyle); var p = tok.WhiteSpace; var r = parse(p, ","); Assert.False(r.IsFaulted); Assert.True(r.Reply.Result == unit); } [Fact] public void JunkFourSpacesComb() { var tok = makeTokenParser(Language.HaskellStyle); var p = tok.WhiteSpace; var r = parse(p, " ,"); Assert.False(r.IsFaulted); Assert.True(r.Reply.Result == unit); } [Fact] public void JunkFourSpacesThenCommentComb() { var tok = makeTokenParser(Language.JavaStyle); var p = tok.WhiteSpace; var r = parse(p, " // A comment\nabc"); Assert.False(r.IsFaulted); Assert.True(r.Reply.Result == unit); Assert.True(r.Reply.State.ToString() == "abc"); } [Fact] public void StringLiteralComb() { var tok = makeTokenParser(Language.HaskellStyle); var p = tok.StringLiteral; var r = parse(p, "\"/abc\""); Assert.False(r.IsFaulted); Assert.True(r.Reply.Result == "/abc"); } [Theory] [InlineData("1234")] [InlineData("12345")] [InlineData("123456")] [InlineData("1234567")] [InlineData("12345678")] public void ParseNTimes(string input) { var p = asString(manyn(digit, 4)); var r = parse(p, input).ToEither(); Assert.True(r.IfLeft("") == "1234"); } [Theory] [InlineData("")] [InlineData("1")] [InlineData("12")] [InlineData("123")] public void ParseNTimesFail(string input) { var p = asString(manyn(digit, 4)); var r = parse(p, input).ToEither(); Assert.True(r.IsLeft); } [Theory] [InlineData("1", "1")] [InlineData("12", "12")] [InlineData("123", "123")] [InlineData("1234", "1234")] [InlineData("12345", "1234")] public void ParseN1Times(string input, string expected) { var p = asString(manyn1(digit, 4)); var r = parse(p, input).ToEither(); Assert.True(r.IfLeft("") == expected); } [Fact] public void ParseN1TimesFail() { var p = asString(manyn1(digit, 4)); var r = parse(p, "").ToEither(); Assert.True(r.IsLeft); } [Theory] [InlineData("", "")] [InlineData("1", "1")] [InlineData("12", "12")] [InlineData("123", "123")] [InlineData("1234", "1234")] [InlineData("12345", "1234")] public void ParseN0Times(string input, string expected) { var p = asString(manyn0(digit, 4)); var r = parse(p, input).ToEither(); Assert.True(r.IfLeft("") == expected); } [Fact] public void ParseN0TimesZeroNegative() { Assert.True(parse(asString(manyn0(digit, 0)), "123").ToEither().IfLeft("x") == ""); Assert.True(parse(asString(manyn0(digit, -1)), "123").ToEither().IfLeft("x") == ""); } [Fact] public void SepByTest() { // greedy, but works because of nice input Assert.Equal(Seq("this", "is", "a", "path"), parse(sepBy1(asString(many1(alphaNum)), ch('/')), "this/is/a/path").ToEither()); Assert.Equal(Seq("this", "is", "a", "path"), parse(sepBy1(asString(many1(alphaNum)), ch('/')), "this/is/a/path.").ToEither()); // greedy + runs into dead end Assert.True(parse(sepBy1(asString(many1(alphaNum)), ch('/')), "this/is/a/path/").IsFaulted); // consume as many items as possible without failing Assert.Equal(Seq("this", "is", "a", "path"), parse(from x in asString(many1(alphaNum)) from xs in many(attempt(from sep in ch('/') from word in asString(many1(alphaNum)) select word)) select x.Cons(xs), "this/is/a/path/").ToEither()); } [Fact] public void EndByTest() { // greedy, but works because of nice input Assert.Equal(Seq("this", "is", "a", "folder"), parse(endBy1(asString(many1(alphaNum)), ch('/')), "this/is/a/folder//").ToEither()); Assert.Equal(Seq("this", "is", "a", "folder"), parse(endBy1(asString(many1(alphaNum)), ch('/')), "this/is/a/folder/").ToEither()); // greedy + runs into dead end Assert.True(parse(endBy1(attempt(asString(many1(alphaNum))), attempt(ch('/'))), "this/is/a/folder/filename").IsFaulted); // consume as many items as possible without failing Assert.Equal(Seq("this", "is", "a", "folder"), parse(many1(attempt(from word in asString(many1(alphaNum)) from sep in ch('/') select word)), "this/is/a/folder/filename").ToEither()); } [Fact] public void SepEndByTest() { Assert.Equal(Seq("this", "is", "a", "path"), parse(sepEndBy1(asString(many1(alphaNum)), ch('/')), "this/is/a/path//").ToEither()); Assert.Equal(Seq("this", "is", "a", "path"), parse(sepEndBy1(asString(many1(alphaNum)), ch('/')), "this/is/a/path/").ToEither()); Assert.Equal(Seq("this", "is", "a", "path"), parse(sepEndBy1(asString(many1(alphaNum)), ch('/')), "this/is/a/path").ToEither()); Assert.True(parse(sepEndBy1(asString(many1(alphaNum)), ch('/')), ".").IsFaulted); Assert.Equal(Seq("this", "is", "a", "path"), parse(sepEndBy(asString(many1(alphaNum)), ch('/')), "this/is/a/path//").ToEither()); Assert.Equal(Seq("this", "is", "a", "path"), parse(sepEndBy(asString(many1(alphaNum)), ch('/')), "this/is/a/path/").ToEither()); Assert.Equal(Seq("this", "is", "a", "path"), parse(sepEndBy(asString(many1(alphaNum)), ch('/')), "this/is/a/path").ToEither()); Assert.Equal(Seq(), parse(sepEndBy(asString(many1(alphaNum)), ch('/')), ".").ToEither()); } [Fact] public void ParallelCheck() { // works Parallel.ForEach(Enumerable.Repeat("", 4), str => parse(from _ in notFollowedBy(anyChar).label("end of input") select unit, str)); // sometimes crashes (net461) Parallel.ForEach(Enumerable.Repeat("", 4), str => parse(from _ in eof select unit, str)); } [Fact] public void FlattenCheck() { Parser HexDigitParser() => from hexDigitString in asString(flatten(sepBy1(many1(hexDigit), ch('-')))) from end in eof select hexDigitString; Assert.Equal("17CBA779DF1E4794A4992AAB59802C19", parse(HexDigitParser(), "17CBA779-DF1E-4794-A499-2AAB59802C19").ToOption()); Assert.Equal("17CBA779DF1E4794A4992AAB59802C19", parse(HexDigitParser(), "17CBA779DF1E4794A4992AAB59802C19").ToOption()); Assert.Equal(Option.None, parse(HexDigitParser(), "-17CBA779-DF1E-4794-A499-2AAB59802C19").ToOption()); Assert.Equal(Option.None, parse(HexDigitParser(), "17CBA779-DF1E-4794-A499--2AAB59802C19").ToOption()); } [Fact] public void ConsCheck() { Parser HexDigitBlocksParser() => from guidString in asString(flatten(cons(many1(hexDigit), many(cons(ch('-'), many1(hexDigit)))))) from end in eof select guidString; Assert.Equal("17CBA779-DF1E-4794-A499-2AAB59802C19", parse(HexDigitBlocksParser(), "17CBA779-DF1E-4794-A499-2AAB59802C19").ToOption()); Assert.Equal("17CBA779DF1E4794A4992AAB59802C19", parse(HexDigitBlocksParser(), "17CBA779DF1E4794A4992AAB59802C19").ToOption()); Assert.Equal(Option.None, parse(HexDigitBlocksParser(), "-17CBA779-DF1E-4794-A499-2AAB59802C19").ToOption()); Assert.Equal(Option.None, parse(HexDigitBlocksParser(), "17CBA779-DF1E-4794-A499--2AAB59802C19").ToOption()); } [Fact] public void EMailParserCheck() { // Note: This e-mail parser is not correct! See https://tools.ietf.org/html/rfc5322#section-3.4.1 and related RFCs Parser QuickAndDirtyEMailParser() => from localpart in asString(flatten(cons(many1(alphaNum), many(cons(oneOf('-','.'), many1(alphaNum)))))) // simple name from at in ch('@') from domain in asString(flatten(cons(many1(alphaNum), many1(cons(ch('.'), many1(alphaNum)))))) // domain with at least one dot select $"{localpart}{at}{domain.ToLower()}"; Assert.Equal("john@example.org", parse(QuickAndDirtyEMailParser(), "john@EXAMPLE.org").ToOption()); Assert.EndsWith("expecting letter or digit or '@'", parse(QuickAndDirtyEMailParser(), "john @EXAMPLE.org").ToEither().IfRight("")); Assert.EndsWith("expecting letter or digit", parse(QuickAndDirtyEMailParser(), ".john @EXAMPLE.org").ToEither().IfRight("")); Assert.EndsWith("expecting letter or digit or '.'", parse(QuickAndDirtyEMailParser(), "john.doe@EXAMPLE").ToEither().IfRight("")); Assert.Equal("john-doe@example.org", parse(QuickAndDirtyEMailParser(), "john-doe@EXAMPLE.org").ToOption()); Assert.Equal("john.doe@example.org", parse(QuickAndDirtyEMailParser(), "john.doe@EXAMPLE.org").ToOption()); } [Fact] public void Issue889() { // Code adapted from the AccountingDSL example, keeping only the basic arithmetic operators var opChars = "+-*/"; var definition = GenLanguageDef.Empty.With( CommentStart: "/*", CommentEnd: "*/", CommentLine: "//", NestedComments: true, OpStart: oneOf(opChars), OpLetter: oneOf(opChars), IdentStart: letter, IdentLetter: either(alphaNum, ch('_')), ReservedNames: Empty, ReservedOpNames: List("+", "-", "*", "/") ); var lexer = makeTokenParser(definition); var input = "3.14159"; var result = lexer.NaturalOrFloat.Parse(input).ToEither().Map(x => x.IfLeft(Double.MaxValue)); // > Success(Right(1418.9)) var expected = lexer.Float.Parse(input).ToEither().IfLeft(Double.MinValue); // > Success(3.14159) Assert.True(result == expected); } [Fact] public void Nat_or_float_must_be_int() { var opChars = "+-*/"; var definition = GenLanguageDef.Empty.With( CommentStart: "/*", CommentEnd: "*/", CommentLine: "//", NestedComments: true, OpStart: oneOf(opChars), OpLetter: oneOf(opChars), IdentStart: letter, IdentLetter: either(alphaNum, ch('_')), ReservedNames: Empty, ReservedOpNames: List("+", "-", "*", "/") ); var lexer = makeTokenParser(definition); var input = "300"; var result = lexer.NaturalOrFloat.Parse(input).ToEither().Map(x => x.IfRight(0)); Assert.True(result == 300); } [Fact] public void ExpressionResultShouldBeSixDueToMultiplicationOperationPriority() { // Arrange var tokenParser = makeTokenParser(Language.JavaStyle); var reservedOp = tokenParser.ReservedOp; // Natural parser var natural = from n in tokenParser.Natural select Expr.Natural(n); // Binary operator expression factory Func binaryOp(string op) => (Expr lhs, Expr rhs) => op == "+" ? Expr.Add(lhs, rhs) : op == "-" ? Expr.Sub(lhs, rhs) : op == "/" ? Expr.Div(lhs, rhs) : op == "*" ? Expr.Mul(lhs, rhs) : throw new NotSupportedException(); // Binary operator parser builder Operator binary(string op, Assoc assoc) => Operator.Infix(assoc, from x in reservedOp(op) select binaryOp(op)); // Operator table Operator[][] table = { new[] { binary("+", Assoc.Left), binary("-", Assoc.Left) }, new[] { binary("*", Assoc.Left), binary("/", Assoc.Left) } }; // Null because it will be not null later and can be used by the lazyp parser Parser expr = null; // Build up the expression term var term = either( attempt(natural), tokenParser.Parens(lazyp(() => expr))); // Build the expression parser expr = buildExpressionParser(table, term).label("expression"); var expression = "2 + 2 * 2"; var exprectedResult = 6; // Act var actualResult = parse(expr, expression) .ToOption() .Map(ex => ex.Eval()) .IfNone(0); // Assert Assert.Equal(exprectedResult, actualResult); } public abstract class Expr { public abstract int Eval(); public static Expr Natural(int x) => new NaturalExpr(x); public static Expr Add(Expr left, Expr right) => new AddExpr(left, right); public static Expr Sub(Expr left, Expr right) => new SubExpr(left, right); public static Expr Mul(Expr left, Expr right) => new MulExpr(left, right); public static Expr Div(Expr left, Expr right) => new DivExpr(left, right); public class NaturalExpr : Expr { public int Value; public NaturalExpr(int value) => Value = value; public override int Eval() => Value; } public class AddExpr : Expr { public readonly Expr Left; public readonly Expr Right; public AddExpr(Expr left, Expr right) { Left = left; Right = right; } public override int Eval() => Left.Eval() + Right.Eval(); } public class SubExpr : Expr { public readonly Expr Left; public readonly Expr Right; public SubExpr(Expr left, Expr right) { Left = left; Right = right; } public override int Eval() => Left.Eval() - Right.Eval(); } public class MulExpr : Expr { public readonly Expr Left; public readonly Expr Right; public MulExpr(Expr left, Expr right) { Left = left; Right = right; } public override int Eval() => Left.Eval() * Right.Eval(); } public class DivExpr : Expr { public readonly Expr Left; public readonly Expr Right; public DivExpr(Expr left, Expr right) { Left = left; Right = right; } public override int Eval() => Left.Eval() / Right.Eval(); } } } } ================================================ FILE: LanguageExt.Tests/Parsing/AbstractParseTPrecisionIntervalTests.cs ================================================ using Xunit; namespace LanguageExt.Tests.Parsing { public abstract class AbstractParseTPrecisionIntervalTests : AbstractParseTTests where T : struct { protected abstract T MinValue { get; } protected abstract T MaxValue { get; } [Fact] public void ParseT_ValidStringFromMinValue_SomeMinValue() => ParseT_ValidStringFromGiven_SomeAsGiven(MinValue); [Fact] public void ParseT_ValidStringFromMaxValue_SomeMaxValue() => ParseT_ValidStringFromGiven_SomeAsGiven(MaxValue); } } ================================================ FILE: LanguageExt.Tests/Parsing/AbstractParseTSignedPrecisionIntervalTests.cs ================================================ using Xunit; namespace LanguageExt.Tests.Parsing { public abstract class AbstractParseTSignedPrecisionIntervalTests : AbstractParseTPrecisionIntervalTests where T : struct { protected abstract T NegativeOne { get; } protected abstract T PositiveOne { get; } [Fact] public void ParseT_ValidStringFromNegativeOne_SomeNegativeOne() => ParseT_ValidStringFromGiven_SomeAsGiven(NegativeOne); [Fact] public void ParseT_ValidStringFromPositiveOne_SomePositiveOne() => ParseT_ValidStringFromGiven_SomeAsGiven(PositiveOne); } } ================================================ FILE: LanguageExt.Tests/Parsing/AbstractParseTTests.cs ================================================ using Xunit; using static LanguageExt.Prelude; namespace LanguageExt.Tests.Parsing { public abstract class AbstractParseTTests where T : struct { protected abstract Option ParseT(string value); [Fact] public void ParseT_NullString_None() { Option expected = None; var actual = ParseT(null); Assert.Equal(expected, actual); } [Fact] public void ParseT_EmptyString_None() { Option expected = None; var actual = ParseT(""); Assert.Equal(expected, actual); } [Fact] public void ParseT_ValidStringFromDefaultValue_SomeDefaultValue() => ParseT_ValidStringFromGiven_SomeAsGiven(default(T)); protected void ParseT_ValidStringFromGiven_SomeAsGiven(T expected) => ParseT_ValidStringFromGiven_SomeAsGiven(expected, expected.ToString()); protected void ParseT_ValidStringFromGivenToLower_SomeAsGiven(T expected) => ParseT_ValidStringFromGiven_SomeAsGiven(expected, expected.ToString().ToLower()); private void ParseT_ValidStringFromGiven_SomeAsGiven(T expected, string value) { var actual = ParseT(value); Assert.Equal(expected, actual); } } } ================================================ FILE: LanguageExt.Tests/Parsing/AbstractParseTUnsignedPrecisionIntervalTests.cs ================================================ using Xunit; namespace LanguageExt.Tests.Parsing { public abstract class AbstractParseTUnsignedPrecisionIntervalTests : AbstractParseTPrecisionIntervalTests where T : struct { protected abstract T PositiveOne { get; } protected abstract T PositiveTwo { get; } [Fact] public void ParseT_ValidStringFromPositiveOne_SomePositiveOne() => ParseT_ValidStringFromGiven_SomeAsGiven(PositiveOne); [Fact] public void ParseT_ValidStringFromPositiveTwo_SomePositiveTwo() => ParseT_ValidStringFromGiven_SomeAsGiven(PositiveTwo); } } ================================================ FILE: LanguageExt.Tests/Parsing/parseBoolTests.cs ================================================ using Xunit; namespace LanguageExt.Tests.Parsing { public class parseBoolTests : AbstractParseTTests { protected override Option ParseT(string value) => Prelude.parseBool(value); [Fact] public void parseBool_ValidStringFromTrue_SomeTrue() => ParseT_ValidStringFromGiven_SomeAsGiven(true); } } ================================================ FILE: LanguageExt.Tests/Parsing/parseByteTests.cs ================================================ namespace LanguageExt.Tests.Parsing { public class parseByteTests : AbstractParseTUnsignedPrecisionIntervalTests { protected override Option ParseT(string value) => Prelude.parseByte(value); protected override byte MinValue => byte.MinValue; protected override byte MaxValue => byte.MaxValue; protected override byte PositiveOne => 1; protected override byte PositiveTwo => 2; } } ================================================ FILE: LanguageExt.Tests/Parsing/parseCharTests.cs ================================================ using Xunit; namespace LanguageExt.Tests.Parsing { public class parseCharTests : AbstractParseTPrecisionIntervalTests { protected override Option ParseT(string value) => Prelude.parseChar(value); protected override char MinValue => char.MinValue; protected override char MaxValue => char.MaxValue; [Theory] [InlineData('a')] [InlineData('1')] [InlineData(' ')] public void parseChar_ValidStringFromGiven_SomeAsGiven(char value) => ParseT_ValidStringFromGiven_SomeAsGiven(value); } } ================================================ FILE: LanguageExt.Tests/Parsing/parseDateTimeOffsetTests.cs ================================================ using System; using Xunit; using static LanguageExt.UnitsOfMeasure; namespace LanguageExt.Tests.Parsing; public class parseDateTimeOffsetTests : AbstractParseTTests { protected override Option ParseT(string value) => Prelude.parseDateTimeOffset(value); [Fact] public void parseDateTimeOffset_ValidStringFromNewMillennium_SomeUtc() => ParseT_ValidStringFromGiven_SomeAsGiven(new DateTimeOffset(2001, 1, 1, 12, 0, 0, 0 * hours)); [Fact] public void parseDateTimeOffset_ValidStringFromNewMillennium_SomeBerlin() => ParseT_ValidStringFromGiven_SomeAsGiven(new DateTimeOffset(2001, 1, 1, 12, 0, 0, 1 * hours)); [Fact] public void parseDateTimeOffset_ISO8601_SomeBerlin() => Assert.Equal(Some(new DateTimeOffset(2018, 1, 25, 15, 32, 5, 1 * hours)), parseDateTimeOffset("2018-01-25T15:32:05+01:00")); [Fact] public void parseDateTimeOffset_ISO8601_SomeUtc() => Assert.Equal(Some(new DateTimeOffset(2018, 1, 25, 15, 32, 5, 0 * hours)), parseDateTimeOffset("2018-01-25T15:32:05Z")); [Fact] public void parseDateTimeOffset_Universal_SomeBerlin() => Assert.Equal(Some(new DateTimeOffset(2018, 1, 25, 15, 32, 5, 1 * hours)), parseDateTimeOffset("2018-01-25 15:32:05+01:00")); [Fact] public void parseDateTimeOffset_Universal_SomeUtc() => Assert.Equal(Some(new DateTimeOffset(2018, 1, 25, 15, 32, 5, 0 * hours)), parseDateTimeOffset("2018-01-25 15:32:05Z")); } ================================================ FILE: LanguageExt.Tests/Parsing/parseDateTimeTests.cs ================================================ using System; using Xunit; namespace LanguageExt.Tests.Parsing { public class parseDateTimeTests : AbstractParseTTests { protected override Option ParseT(string value) => Prelude.parseDateTime(value); [Fact] public void parseDateTime_ValidStringFromNewMillennium_SomeNewMillennium() => ParseT_ValidStringFromGiven_SomeAsGiven(new DateTime(2001, 1, 1)); } } ================================================ FILE: LanguageExt.Tests/Parsing/parseDecimalTests.cs ================================================ namespace LanguageExt.Tests.Parsing { public class parseDecimalTests : AbstractParseTPrecisionIntervalTests { protected override Option ParseT(string value) => Prelude.parseDecimal(value); protected override decimal MinValue => decimal.MinValue; protected override decimal MaxValue => decimal.MaxValue; } } ================================================ FILE: LanguageExt.Tests/Parsing/parseDoubleTests.cs ================================================ using Xunit; namespace LanguageExt.Tests.Parsing { public class parseDoubleTests : AbstractParseTTests { protected override Option ParseT(string value) => Prelude.parseDouble(value); [Theory] [InlineData(0.5)] [InlineData(-0.5)] [InlineData(double.Epsilon)] [InlineData(double.NegativeInfinity)] [InlineData(double.PositiveInfinity)] public void parseDouble_ValidStringFromGiven_SomeAsGiven(double value) => ParseT_ValidStringFromGiven_SomeAsGiven(value); } } ================================================ FILE: LanguageExt.Tests/Parsing/parseEnumTests.cs ================================================ using Xunit; namespace LanguageExt.Tests.Parsing { public class parseEnumTests : AbstractParseTTests { protected override Option ParseT(string value) => Prelude.parseEnum(value); [Fact] public void parseEnum_ValidStringFromFoo_SomeFoo() => ParseT_ValidStringFromGiven_SomeAsGiven(FooBarEnum.Foo); [Fact] public void parseEnum_ValidStringFromBar_SomeBar() => ParseT_ValidStringFromGiven_SomeAsGiven(FooBarEnum.Bar); } public class parseEnumIgnoreCaseTests : AbstractParseTTests { protected override Option ParseT(string value) => Prelude.parseEnumIgnoreCase(value); [Fact] public void parseEnum_ValidStringFromFoo_SomeFoo() => ParseT_ValidStringFromGivenToLower_SomeAsGiven(FooBarEnum.Foo); [Fact] public void parseEnum_ValidStringFromBar_SomeBar() => ParseT_ValidStringFromGivenToLower_SomeAsGiven(FooBarEnum.Bar); } public enum FooBarEnum { Foo, Bar } } ================================================ FILE: LanguageExt.Tests/Parsing/parseFloatTests.cs ================================================ using Xunit; namespace LanguageExt.Tests.Parsing { public class parseFloatTests : AbstractParseTTests { protected override Option ParseT(string value) => Prelude.parseFloat(value); [Theory] [InlineData(0.5)] [InlineData(-0.5)] [InlineData(float.Epsilon)] [InlineData(float.NegativeInfinity)] [InlineData(float.PositiveInfinity)] //[InlineData(float.NaN)] TODO -- Why is this here? public void parseFloat_ValidStringFromGiven_SomeAsGiven(float value) => ParseT_ValidStringFromGiven_SomeAsGiven(value); } } ================================================ FILE: LanguageExt.Tests/Parsing/parseGuidTests.cs ================================================ using System; using Xunit; namespace LanguageExt.Tests.Parsing { public class parseGuidTests : AbstractParseTTests { protected override Option ParseT(string value) => Prelude.parseGuid(value); [Fact] public void ParseGuid_ValidStringFixedGuid_SomeOfSameFixedGuid() { var guid = Guid.Parse("F9168C5E-CEB2-4faa-B6BF-329BF39FA1E4"); ParseT_ValidStringFromGiven_SomeAsGiven(guid); } } } ================================================ FILE: LanguageExt.Tests/Parsing/parseIntTests.cs ================================================ namespace LanguageExt.Tests.Parsing { public class parseIntTests : AbstractParseTSignedPrecisionIntervalTests { protected override Option ParseT(string value) => Prelude.parseInt(value); protected override int MinValue => int.MinValue; protected override int MaxValue => int.MaxValue; protected override int NegativeOne => -1; protected override int PositiveOne => 1; } } ================================================ FILE: LanguageExt.Tests/Parsing/parseLongTests.cs ================================================ namespace LanguageExt.Tests.Parsing { public class parseLongTests : AbstractParseTSignedPrecisionIntervalTests { protected override Option ParseT(string value) => Prelude.parseLong(value); protected override long MinValue => long.MinValue; protected override long MaxValue => long.MaxValue; protected override long NegativeOne => -1; protected override long PositiveOne => 1; } } ================================================ FILE: LanguageExt.Tests/Parsing/parseSByteTests.cs ================================================ namespace LanguageExt.Tests.Parsing { public class parseSByteTests : AbstractParseTSignedPrecisionIntervalTests { protected override Option ParseT(string value) => Prelude.parseSByte(value); protected override sbyte MinValue => sbyte.MinValue; protected override sbyte MaxValue => sbyte.MaxValue; protected override sbyte NegativeOne => -1; protected override sbyte PositiveOne => 1; } } ================================================ FILE: LanguageExt.Tests/Parsing/parseShortTests.cs ================================================ namespace LanguageExt.Tests.Parsing { public class parseShortTests : AbstractParseTSignedPrecisionIntervalTests { protected override Option ParseT(string value) => Prelude.parseShort(value); protected override short MinValue => short.MinValue; protected override short MaxValue => short.MaxValue; protected override short NegativeOne => -1; protected override short PositiveOne => 1; } } ================================================ FILE: LanguageExt.Tests/Parsing/parseTimeSpanTests.cs ================================================ using System; using Xunit; namespace LanguageExt.Tests.Parsing { public class parseTimeSpanTests : AbstractParseTTests { protected override Option ParseT(string value) => Prelude.parseTimeSpan(value); [Fact] public void parseTimeSpan_valid() => Assert.Equal(Prelude.Some(new TimeSpan(0, 0, 0, 19, 12)), Prelude.parseTimeSpan("00:00:19.0120000")); [Theory] [InlineData("00:00:19.1200000")] [InlineData("00:00:19")] [InlineData("00:00")] public void parseTimeSpan_multipleValid(string input) => ParseT_ValidStringFromGiven_SomeAsGiven(TimeSpan.Parse(input)); [Theory] [InlineData("")] [InlineData("petter")] [InlineData("0:60:0")] [InlineData("0:0:60")] [InlineData(".123")] [InlineData("10.12")] public void parseTimeSpan_multipleInvalid(string input) => Assert.Equal(Prelude.None, Prelude.parseTimeSpan(input)); } } ================================================ FILE: LanguageExt.Tests/Parsing/parseUIntTests.cs ================================================ namespace LanguageExt.Tests.Parsing { public class parseUIntTests : AbstractParseTUnsignedPrecisionIntervalTests { protected override Option ParseT(string value) => Prelude.parseUInt(value); protected override uint MinValue => uint.MinValue; protected override uint MaxValue => uint.MaxValue; protected override uint PositiveOne => 1; protected override uint PositiveTwo => 2; } } ================================================ FILE: LanguageExt.Tests/Parsing/parseULongTests.cs ================================================ namespace LanguageExt.Tests.Parsing { public class parseULongTests : AbstractParseTUnsignedPrecisionIntervalTests { protected override Option ParseT(string value) => Prelude.parseULong(value); protected override ulong MinValue => ulong.MinValue; protected override ulong MaxValue => ulong.MaxValue; protected override ulong PositiveOne => 1; protected override ulong PositiveTwo => 2; } } ================================================ FILE: LanguageExt.Tests/Parsing/parseUShortTests.cs ================================================ namespace LanguageExt.Tests.Parsing { public class parseUShortTests : AbstractParseTUnsignedPrecisionIntervalTests { protected override Option ParseT(string value) => Prelude.parseUShort(value); protected override ushort MinValue => ushort.MinValue; protected override ushort MaxValue => ushort.MaxValue; protected override ushort PositiveOne => 1; protected override ushort PositiveTwo => 2; } } ================================================ FILE: LanguageExt.Tests/PartialAndCurryingTests.cs ================================================ using Xunit; using static LanguageExt.Prelude; namespace LanguageExt.Tests { public class PartialAndCurryingTests { [Fact] public void CurryTest() { var add = curry((int x, int y) => x + y); Assert.True(add(10)(5) == 15); } [Fact] public void PartialTest1() { var partial = curry((int x, int y) => x + y)(10); Assert.True(partial(5) == 15); } [Fact] public void PartialTest2() { var partial = par((int x, int y) => x + y, 10); Assert.True(partial(5) == 15); } [Fact] public void PartialTest3() { var partial = par((int x, int y, int c, int d) => x + y + c + d, 10, 10); Assert.True(partial(5, 5) == 30); } [Fact] public void CurryPartialTest() { var partial = curry(par((int x, int y, int c, int d) => x + y + c + d, 10, 10)); Assert.True(partial(5)(5) == 30); } } } ================================================ FILE: LanguageExt.Tests/PatchTests.cs ================================================ using static LanguageExt.Prelude; using static LanguageExt.Patch; using LanguageExt.ClassInstances; using Xunit; namespace LanguageExt.Tests { public class PatchTests { [Fact] public void PatchAppendEmptyIsPatch() { var docA = List("Hello", "World"); var docB = List("Hello", "World", "Again"); var patchA = diff(docA, docB); var patchB = append(patchA, Patch.Empty); Assert.True(patchA == patchB); } [Fact] public void EmptyPatchAppendPatchIsPatch() { var docA = List("Hello", "World"); var docB = List("Hello", "World", "Again"); var patchA = diff(docA, docB); var patchB = append(Patch.Empty, patchA); Assert.True(patchA == patchB); } [Fact] public void PatchCommutes() { var docA = List("Hello", "World"); var docB = List("Hello", "World", "Again"); var docC = List("World"); var docD = List("World", "War"); var patchAB = diff(docA, docB); var patchBC = diff(docB, docC); var patchCD = diff(docC, docD); var patchA_BC = append(patchAB, append(patchBC, patchCD)); var patchAB_C = append(append(patchAB, patchBC), patchCD); Assert.True(patchA_BC == patchAB_C); } [Fact] public void InsertAtEndDiff() { var docA = List("Hello", "World"); var docB = List("Hello", "World", "Again"); var patch = diff(docA, docB); Assert.True(patch.Edits.Count == 1); Assert.True(patch.Edits.Head == Edit.Insert.New(2, "Again")); } [Fact] public void InsertAtBeginningDiff() { var docA = List("Hello", "World"); var docB = List("Again", "Hello", "World"); var patch = diff(docA, docB); Assert.True(patch.Edits.Count == 1); Assert.True(patch.Edits.Head == Edit.Insert.New(0, "Again")); } [Fact] public void InsertAtMidDiff() { var docA = List("Hello", "World"); var docB = List("Hello", "Again", "World"); var patch = diff(docA, docB); Assert.True(patch.Edits.Count == 1); Assert.True(patch.Edits.Head == Edit.Insert.New(1, "Again")); } [Fact] public void InsertMultiDiff() { var docA = List("Hello", "World"); var docB = List("It's", "Hello", "Again", "World", "Cheers"); var patch = diff(docA, docB); Assert.True(patch.Edits.Count == 3); Assert.True(patch.Edits.Head == Edit.Insert.New(0, "It's")); Assert.True(patch.Edits.Tail.Head == Edit.Insert.New(1, "Again")); Assert.True(patch.Edits.Tail.Tail.Head == Edit.Insert.New(2, "Cheers")); } [Fact] public void DeleteAtEndDiff() { var docA = List("Hello", "World", "Again"); var docB = List("Hello", "World"); var patch = diff(docA, docB); Assert.True(patch.Edits.Count == 1); Assert.True(patch.Edits.Head == Edit.Delete.New(2, "Again")); } [Fact] public void DeleteAtBeginningDiff() { var docA = List("Again", "Hello", "World"); var docB = List("Hello", "World"); var patch = diff(docA, docB); Assert.True(patch.Edits.Count == 1); Assert.True(patch.Edits.Head == Edit.Delete.New(0, "Again")); } [Fact] public void DeleteAtMidDiff() { var docA = List("Hello", "Again", "World"); var docB = List("Hello", "World"); var patch = diff(docA, docB); Assert.True(patch.Edits.Count == 1); Assert.True(patch.Edits.Head == Edit.Delete.New(1, "Again")); } [Fact] public void DeleteMultiDiff() { var docA = List("It's", "Hello", "Again", "World", "Cheers"); var docB = List("Hello", "World"); var patch = diff(docA, docB); Assert.True(patch.Edits.Count == 3); Assert.True(patch.Edits.Head == Edit.Delete.New(0, "It's")); Assert.True(patch.Edits.Tail.Head == Edit.Delete.New(2, "Again")); Assert.True(patch.Edits.Tail.Tail.Head == Edit.Delete.New(4, "Cheers")); } [Fact] public void ReplaceAtEndDiff() { var docA = List("Hello", "World", "Again"); var docB = List("Hello", "World", "Once More"); var patch = diff(docA, docB); Assert.True(patch.Edits.Count == 1); Assert.True(patch.Edits.Head == Edit.Replace.New(2, "Again", "Once More")); } [Fact] public void ReplaceAtBeginningDiff() { var docA = List("Again", "Hello", "World"); var docB = List("Once More", "Hello", "World"); var patch = diff(docA, docB); Assert.True(patch.Edits.Count == 1); Assert.True(patch.Edits.Head == Edit.Replace.New(0, "Again", "Once More")); } [Fact] public void ReplaceAtMidDiff() { var docA = List("Hello", "Again", "World"); var docB = List("Hello", "Once More", "World"); var patch = diff(docA, docB); Assert.True(patch.Edits.Count == 1); Assert.True(patch.Edits.Head == Edit.Replace.New(1, "Again", "Once More")); } [Fact] public void ReplaceMultiDiff() { var docA = List("It's", "Hello", "Again", "World", "Cheers"); var docB = List("Yes", "Hello", "My", "World", "Of Joy"); var patch = diff(docA, docB); Assert.True(patch.Edits.Count == 3); Assert.True(patch.Edits.Head == Edit.Replace.New(0, "It's", "Yes")); Assert.True(patch.Edits.Tail.Head == Edit.Replace.New(2, "Again", "My")); Assert.True(patch.Edits.Tail.Tail.Head == Edit.Replace.New(4, "Cheers", "Of Joy")); } [Fact] public void ApplyInsertAtEndDiff() { var docA = List("Hello", "World"); var docB = List("Hello", "World", "Again"); var patch = diff(docA, docB); var docC = apply(patch, docA); Assert.True(docB == docC); } [Fact] public void ApplyInsertAtBeginningDiff() { var docA = List("Hello", "World"); var docB = List("Again", "Hello", "World"); var patch = diff(docA, docB); var docC = apply(patch, docA); Assert.True(docB == docC); } [Fact] public void ApplyInsertAtMidDiff() { var docA = List("Hello", "World"); var docB = List("Hello", "Again", "World"); var patch = diff(docA, docB); var docC = apply(patch, docA); Assert.True(docB == docC); } [Fact] public void ApplyInsertMultiDiff() { var docA = List("Hello", "World"); var docB = List("It's", "Hello", "Again", "World", "Cheers"); var patch = diff(docA, docB); var docC = apply(patch, docA); Assert.True(docB == docC); } [Fact] public void ApplyDeleteAtEndDiff() { var docA = List("Hello", "World", "Again"); var docB = List("Hello", "World"); var patch = diff(docA, docB); var docC = apply(patch, docA); Assert.True(docB == docC); } [Fact] public void ApplyDeleteAtBeginningDiff() { var docA = List("Again", "Hello", "World"); var docB = List("Hello", "World"); var patch = diff(docA, docB); var docC = apply(patch, docA); Assert.True(docB == docC); } [Fact] public void ApplyDeleteAtMidDiff() { var docA = List("Hello", "Again", "World"); var docB = List("Hello", "World"); var patch = diff(docA, docB); var docC = apply(patch, docA); Assert.True(docB == docC); } [Fact] public void ApplyDeleteMultiDiff() { var docA = List("It's", "Hello", "Again", "World", "Cheers"); var docB = List("Hello", "World"); var patch = diff(docA, docB); var docC = apply(patch, docA); Assert.True(docB == docC); } [Fact] public void ApplyReplaceAtEndDiff() { var docA = List("Hello", "World", "Again"); var docB = List("Hello", "World", "Once More"); var patch = diff(docA, docB); var docC = apply(patch, docA); Assert.True(docB == docC); } [Fact] public void ApplyReplaceAtBeginningDiff() { var docA = List("Again", "Hello", "World"); var docB = List("Once More", "Hello", "World"); var patch = diff(docA, docB); var docC = apply(patch, docA); Assert.True(docB == docC); } [Fact] public void ApplyReplaceAtMidDiff() { var docA = List("Hello", "Again", "World"); var docB = List("Hello", "Once More", "World"); var patch = diff(docA, docB); var docC = apply(patch, docA); Assert.True(docB == docC); } [Fact] public void ApplyReplaceMultiDiff() { var docA = List("It's", "Hello", "Again", "World", "Cheers"); var docB = List("Yes", "Hello", "My", "World", "Of Joy"); var patch = diff(docA, docB); var docC = apply(patch, docA); Assert.True(docB == docC); } [Fact] public void PatchAppendInversePatchIsEmpty() { var docA = List("It's", "Hello", "Again", "World", "Cheers"); var docB = List("Yes", "Hello", "My", "World", "Of Joy"); var patch = diff(docA, docB); var inverseP = inverse(patch); var patch1 = patch.Combine(inverseP); var empty = Patch.Empty; Assert.True(patch1 == empty); } [Fact] public void InversePatchAppendPatchIsEmpty() { var docA = List("It's", "Hello", "Again", "World", "Cheers"); var docB = List("Yes", "Hello", "My", "World", "Of Joy"); var patch = diff(docA, docB); var inverseP = inverse(patch); var patch1 = inverseP.Combine(patch); var empty = Patch.Empty; Assert.True(patch1 == empty); } [Fact] public void InverseInversePatchIsPatch() { var docA = List("It's", "Hello", "Again", "World", "Cheers"); var docB = List("Yes", "Hello", "My", "World", "Of Joy"); var patch = diff(docA, docB); var inverseP = inverse(patch); var inverseInverse = inverse(inverseP); Assert.True(patch == inverseInverse); } [Fact] public void PatchAppend() { var docA = List("It's", "Hello", "Again", "World", "Cheers"); var docB = List("Yes", "Hello", "My", "World", "Of Joy"); var docC = List("Yes", "My", "Of Joy"); var patchP = diff(docA, docB); var patchQ = diff(docB, docC); var patchPQ = patchP.Combine(patchQ); var docD = apply(patchPQ, docA); //var docD1 = apply(patchP, docA); //var docD2 = apply(patchQ, docD1); Assert.True(docC == docD); var edits = patchPQ.Edits.ToArr(); Assert.True(edits[0] == Edit.Replace.New(0, "It's", "Yes")); Assert.True(edits[1] == Edit.Delete.New(1, "Hello")); Assert.True(edits[2] == Edit.Replace.New(2, "Again", "My")); Assert.True(edits[3] == Edit.Delete.New(3, "World")); Assert.True(edits[4] == Edit.Replace.New(4, "Cheers", "Of Joy")); } [Fact] public void InverseOfPatchPAppendPatchQIsInversePatchQAppendInversePatchP() { var docA = List("It's", "Hello", "Again", "World", "Cheers"); var docB = List("Yes", "Hello", "My", "World", "Of Joy"); var docC = List("Yes", "Hello", "My", "World", "Of Joy", "And More"); var patchP = diff(docA, docB); var patchQ = diff(docB, docC); var invPatchP = inverse(patchP); var invPatchQ = inverse(patchQ); Assert.True(inverse(invPatchP) == patchP); Assert.True(inverse(invPatchQ) == patchQ); var patchPQ = patchP.Combine(patchQ); var invPatchPQ = inverse(patchPQ); Assert.True(inverse(invPatchPQ) == patchPQ); var invPatchQinvPatchP = invPatchQ.Combine(invPatchP); Assert.True(inverse(invPatchPQ) == inverse(invPatchQinvPatchP)); Assert.True(invPatchPQ == invPatchQinvPatchP); } [Fact] public void InverseEmptyIsEmpty() { var empty = Patch.Empty; Assert.True(empty == inverse(empty)); } [Fact] public void ComposableWithInverse() { var docA = List("It's", "Hello", "Again", "World", "Cheers"); var docB = List("Yes", "Hello", "My", "World", "Of Joy"); var patch = diff(docA, docB); var isComposable = composable(patch, inverse(patch)); Assert.True(isComposable); } [Fact] public void InverseComposableWith() { var docA = List("It's", "Hello", "Again", "World", "Cheers"); var docB = List("Yes", "Hello", "My", "World", "Of Joy"); var patch = diff(docA, docB); var isComposable = composable(inverse(patch), patch); Assert.True(isComposable); } [Fact] public void InverseApplicableToAnyAppliedPatchOfP() { var docA = List("It's", "Hello", "Again", "World", "Cheers"); var docB = List("Yes", "Hello", "My", "World", "Of Joy"); var docC = List("Yes", "Hello", "My", "World", "Of Joy", "And More"); var patch = diff(docA, docB); var invPatch = inverse(patch); var isApplicable = applicable(invPatch, apply(patch, docC)); Assert.True(isApplicable); } [Fact] public void ApplyDiffResultsInTargetDocument() { var d = List("It's", "Hello", "Again", "World", "Cheers"); var e = List("Yes", "Hello", "My", "World", "Of Joy"); Assert.True(apply(diff(d, e), d) == e); } [Fact] public void DiffTheSameDocumentProducesEmptyPatch() { var d = List("It's", "Hello", "Again", "World", "Cheers"); Assert.True(diff(d, d) == empty()); } [Fact] public void DiffsBetweenThreePatchesAreEqualToDiffsBetweenFirstAndLastPatch() { var a = List("It's", "Hello", "Again", "World", "Cheers"); var b = List("Yes", "Hello", "My", "World", "Of Joy"); var c = List("Yes", "Hello", "My", "World", "Of Joy", "And More"); Assert.True( apply(append(diff(a, b), diff(b, c)), a) == apply(diff(a, c), a) ); } [Fact] public void DiffWithConflictTakeOurs() { var a = List("Hello", "World"); var b = List("World", "Hello"); var c = List("Worldy", "Hello"); var ab = diff(a, b); var ac = diff(a, c); var (pa, pb) = transformWith(ours, ab, ac); var newa = apply(pa, apply(ab, a)); var newb = apply(pb, apply(ac, a)); Assert.True(newa == newb); Assert.True(newa == b); Assert.True(newb == b); } [Fact] public void DiffWithConflictTakeTheirs() { var a = List("Hello", "World"); var b = List("World", "Hello"); var c = List("Worldy", "Hello"); var ab = diff(a, b); var ac = diff(a, c); var (pa, pb) = transformWith(theirs, ab, ac); var newa = apply(pa, apply(ab, a)); var newb = apply(pb, apply(ac, a)); Assert.True(newa == newb); Assert.True(newa == c); Assert.True(newb == c); } } } ================================================ FILE: LanguageExt.Tests/PipesTests.cs ================================================ using LanguageExt.Sys.Test; using Xunit; using static LanguageExt.Pipes.Producer; using static LanguageExt.Pipes.Consumer; namespace LanguageExt.Tests; public class PipesTests { [Fact] public void MergeSynchronousProducersSucceeds() { using var rt = Runtime.New(); (merge(yield(1), yield(1)) | awaiting().Map(ignore)) .Run().As() .Run(rt, EnvIO.New()) .Ignore(); } } ================================================ FILE: LanguageExt.Tests/PrismTests.cs ================================================ /* TODO: Restore when SourceGen available using Xunit; namespace LanguageExt.Tests; public class PrismTests { private static readonly Prism jobWorkerCarMileage = prism(Job.worker.ToPrism(), Worker.car.ToPrism(), Car.mileage.ToPrism()); [Fact] public void PrismShouldGetSome() { var expected = Some(20000); var car = new Car("Maserati", "Ghibli", 20000); var worker = new Worker("Joe Bloggs", 50000, car); var job = new Job("Programmer", "Write code and tests.", worker); var actual = jobWorkerCarMileage.Get(job); Assert.Equal(expected, actual); } [Fact] public void PrismShouldGetNone() { var expected = Option.None; var worker = new Worker("Joe Bloggs", 50000, Option.None); var job = new Job("Programmer", "Write code and tests.", worker); var actual = jobWorkerCarMileage.Get(job); Assert.Equal(expected, actual); } [Fact] public void PrismShouldSetWhenOptionalPartOfChainIsSome() { var expectedCar = new Car("Maserati", "Ghibli", 25000); var expectedWorker = new Worker("Joe Bloggs", 50000, expectedCar); var expected = new Job("Programmer", "Write code and tests.", expectedWorker); var car = new Car("Maserati", "Ghibli", 20000); var worker = new Worker("Joe Bloggs", 50000, car); var job = new Job("Programmer", "Write code and tests.", worker); var actual = jobWorkerCarMileage.Set(25000, job); Assert.Equal(expected, actual); } [Fact] public void PrismShouldNotSetWhenOptionalPartOfChainIsNone() { var expectedWorker = new Worker("Joe Bloggs", 50000, Option.None); var expected = new Job("Programmer", "Write code and tests.", expectedWorker); var worker = new Worker("Joe Bloggs", 50000, Option.None); var job = new Job("Programmer", "Write code and tests.", worker); var actual = jobWorkerCarMileage.Set(25000, job); Assert.Equal(expected, actual); } } [Record] public partial class Worker { public readonly string Name; public readonly int Salary; public readonly Option Car; } [Record] public partial class Job { public readonly string Name; public readonly string Description; public readonly Worker Worker; } */ ================================================ FILE: LanguageExt.Tests/QueryTests.cs ================================================ using Xunit; using static LanguageExt.Prelude; using static LanguageExt.Query; namespace LanguageExt.Tests { public class QueryTests { [Fact] public void MapTest() { // Generates 10,20,30,40,50 var input = toQuery(new[] { 1, 2, 3, 4, 5 }); var output1 = map(input, x => x * 10); // Generates 30,40,50 var output2 = filter(output1, x => x > 20); // Generates 120 var output3 = fold(output2, 0, (x, s) => s + x); Assert.True(output3 == 120); } [Fact] public void ReduceTest1() { // Generates 10,20,30,40,50 var input = toQuery(new[] { 1, 2, 3, 4, 5 }); var output1 = map(input, (i,x) => x * 10); // Generates 30,40,50 var output2 = filter(output1, x => x > 20); // Generates 120 var output3 = reduce(output2, (x, s) => s + x); Assert.True(output3 == 120); } [Fact] public void ReduceTest2() { // Generates 10,20,30,40,50 var input = toQuery(new[] { 1, 2, 3, 4, 5 }); var output1 = map(input, (i, x) => (i + 1) * 10); // Generates 30,40,50 var output2 = filter(output1, x => x > 20); // Generates 120 var output3 = reduce(output2, (x, s) => s + x); Assert.True(output3 == 120); } [Fact] public void MapTestFluent() { var res = toQuery(new[] { 1, 2, 3, 4, 5 }) .Map(x => x * 10) .Filter(x => x > 20) .Fold(0, (x, s) => s + x); Assert.True(res == 120); } [Fact] public void ReduceTestFluent() { var res = toQuery(new[] { 1, 2, 3, 4, 5 }) .Map(x => x * 10) .Filter(x => x > 20) .Reduce((x, s) => s + x); Assert.True(res == 120); } } } ================================================ FILE: LanguageExt.Tests/QueueTests.cs ================================================ using static LanguageExt.Prelude; using static LanguageExt.List; using static LanguageExt.Queue; using Xunit; namespace LanguageExt.Tests { public class QueueTests { [Fact] public void EmptyQueuePeek() { var test = Queue(); var res = peek(test); Assert.True(res.IsNone); } [Fact] public void EmptyQueueDeq() { var test = Queue(); var res = map(deq(test), (stack, value) => value); Assert.True(res.IsNone); } [Fact] public void Dequeuing() { var test = Queue(1, 2, 3, 4, 5); Deq5(test); } [Fact] public void EnqDeq5() { var test = Queue(); test = enq(test, 1); test = enq(test, 2); test = enq(test, 3); test = enq(test, 4); test = enq(test, 5); Deq5(test); } void Deq5(Que test) { test = map(deq(test), (queue, value) => { Assert.True(value.IsSome); return queue; }); test = map(deq(test), (queue, value) => { Assert.True(value.IsSome); return queue; }); test = map(deq(test), (queue, value) => { Assert.True(value.IsSome); return queue; }); test = map(deq(test), (queue, value) => { Assert.True(value.IsSome); return queue; }); match(peek(test), Some: v => Assert.True(v == 5, "Actually equals "+v), None: () => Assert.False(true) ); } [Fact] public void CollectionFunctions() { var queue = toQueue(Range(0,100)); Assert.True(exists(queue, v => v == 50)); Assert.True(length(queue) == 100); Assert.True(length(takeWhile(queue, v => v != 90)) == 90); Assert.True(length(take(queue, 10)) == 10); Assert.True(head(take(queue, 1)) == 0); } [Fact] public void RecursiveSumTest() { var values = toQueue(Range(1, 10)); var res = Sum(values); Assert.True(res == sum(values)); } public int Sum(Que queue) => map( deq(queue), (newqueue, option) => match(option, Some: value => value + Sum(newqueue), None: () => 0 ) ); } } ================================================ FILE: LanguageExt.Tests/RangeTests.cs ================================================ using Xunit; namespace LanguageExt.Tests; public class RangeTests { [Fact] public void IntRangeAsc() { var x = Range.fromMinMax(2, 5).ToLst(); Assert.True(x == List(2, 3, 4, 5)); } [Fact] public void IntRangeDesc() { var x = Range.fromMinMax(5, 2).ToLst(); Assert.True(x == List(5, 4, 3, 2)); } [Fact] public void IntCountAsc() { var x = Range.fromCount(2, 5, 2).ToLst(); Assert.True(x == List(2, 4, 6, 8, 10)); } [Fact] public void IntCountDesc() { var x = Range.fromCount(2, 5, -2).ToLst(); Assert.True(x == List(2, 0, -2, -4, -6)); } [Fact] public void CharCountAsc() { var x = Range.fromCount('a', (char)5).ToLst(); Assert.True(x == List('a', 'b', 'c', 'd', 'e')); } [Fact] public void CharCountDesc() { var x = Range('e', 'a').ToLst(); Assert.True(x == List('e', 'd', 'c', 'b', 'a')); } [Fact] public void CharRangeAsc() { var x = Range.fromMinMax('a', 'e').ToLst(); Assert.True(x == List('a', 'b', 'c', 'd', 'e')); } [Fact] public void CharRangeDesc() { var x = Range.fromMinMax('e', 'a').ToLst(); Assert.True(x == List('e', 'd', 'c', 'b', 'a')); } } ================================================ FILE: LanguageExt.Tests/Read-Me.cs ================================================ using LanguageExt.ClassInstances; namespace LanguageExt.Tests; public class ReadMe { public void ReadMeCode() { var abc = ('a', 'b').Add('c'); // ('a', 'b', 'c') var abcd = ('a', 'b').Add('c').Add('d'); // ('a', 'b', 'c', 'd') var abcd5 = ('a', 'b').Add('c').Add('d').Add(5); // ('a', 'b', 'c', 'd', 5) var sumA = (1, 2, 3).Sum(); // 6 var sumB = (2, 4, 8).Product(); // 64 var flag = ("one", "two", "three").Contains("one"); // true } } ================================================ FILE: LanguageExt.Tests/RecordIgnoreBaseTests.cs ================================================ using System; using Xunit; namespace LanguageExt.Tests { public class RecordIgnoreBaseTests { public class BaseClass { public readonly int X; public BaseClass(int x) => X = x; } public class SubClass1 : BaseClass, IEquatable, IComparable { public readonly int Y; public SubClass1(int x, int y) : base(x) => Y = y; public int CompareTo(SubClass1? other) => RecordTypeIgnoreBase.Compare(this, other); public override bool Equals(object? obj) => RecordTypeIgnoreBase.Equality(this, obj); public bool Equals(SubClass1? other) => RecordTypeIgnoreBase.EqualityTyped(this, other); public override int GetHashCode() => RecordTypeIgnoreBase.Hash(this); } [IgnoreBase] public class SubClass2 : BaseClass, IEquatable, IComparable { public readonly int Y; public SubClass2(int x, int y) : base(x) => Y = y; public int CompareTo(SubClass2 other) => RecordType.Compare(this, other); public override bool Equals(object obj) => RecordType.Equality(this, obj); public bool Equals(SubClass2 other) => RecordType.EqualityTyped(this, other); public override int GetHashCode() => RecordType.Hash(this); } public class SubClass3 : BaseClass, IEquatable, IComparable { public readonly int Y; public SubClass3(int x, int y) : base(x) => Y = y; public int CompareTo(SubClass3 other) => RecordType.Compare(this, other); public override bool Equals(object obj) => RecordType.Equality(this, obj); public bool Equals(SubClass3 other) => RecordType.EqualityTyped(this, other); public override int GetHashCode() => RecordType.Hash(this); } public class FirstAttributeAttribute : Attribute { } [FirstAttribute] [IgnoreBase] public class SubClass4 : BaseClass, IEquatable, IComparable { public readonly int Y; public SubClass4(int x, int y) : base(x) => Y = y; public int CompareTo(SubClass4 other) => RecordType.Compare(this, other); public override bool Equals(object obj) => RecordType.Equality(this, obj); public bool Equals(SubClass4 other) => RecordType.EqualityTyped(this, other); public override int GetHashCode() => RecordType.Hash(this); } [Fact] public void TestSubClass1Eq() { var a = new SubClass1(0, 1); var b = new SubClass1(1, 1); Assert.True(a.Equals(b)); Assert.True(a.GetHashCode() == b.GetHashCode()); Assert.True(a.CompareTo(b) == 0); } [Fact] public void TestSubClass2Eq() { var a = new SubClass2(0, 1); var b = new SubClass2(1, 1); Assert.True(a.Equals(b)); Assert.True(a.GetHashCode() == b.GetHashCode()); Assert.True(a.CompareTo(b) == 0); } [Fact] public void TestSubClass3NotEq() { var a = new SubClass3(0, 1); var b = new SubClass3(1, 1); Assert.False(a.Equals(b)); Assert.False(a.GetHashCode() == b.GetHashCode()); Assert.False(a.CompareTo(b) == 0); } [Fact] public void TestSubClass4Eq() { var a = new SubClass4(0, 1); var b = new SubClass4(1, 1); Assert.True(a.Equals(b)); Assert.True(a.GetHashCode() == b.GetHashCode()); Assert.True(a.CompareTo(b) == 0); } } } ================================================ FILE: LanguageExt.Tests/RecordTests.cs ================================================ using System; using System.Collections.Generic; using System.Text; using Xunit; namespace LanguageExt.Tests { public class RecordTests { /// /// Cons type - singly linked list /// public class Cons : Record> { public readonly A Head; public readonly Cons Tail; public Cons(A head, Cons tail) { Head = head; Tail = tail; } } [Fact] public void ConsTests() { var listA = new Cons(1, new Cons(2, new Cons(3, new Cons(4, null)))); var listB = new Cons(1, new Cons(2, new Cons(3, new Cons(4, null)))); var listC = new Cons(1, new Cons(2, new Cons(3, null))); Assert.True(listA == listB); Assert.True(listB != listC); Assert.True(listA != listC); } /// /// Binary tree /// public class Tree : Record> { public readonly A Value; public readonly Tree Left; public readonly Tree Right; public Tree(A value, Tree left, Tree right) { Value = value; Left = left; Right = right; } } [Fact] public void TreeTests() { var treeA = new Tree(5, new Tree(3, null, null), new Tree(7, null, new Tree(9, null, null))); var treeB = new Tree(5, new Tree(3, null, null), new Tree(7, null, new Tree(9, null, null))); var treeC = new Tree(5, new Tree(3, null, null), new Tree(7, null, null)); Assert.True(treeA == treeB); Assert.True(treeB != treeC); Assert.True(treeA != treeC); var str = treeA.ToString(); } class Disorder : Record { public string Present = "Here"; public string Absent = null; } [Fact] public void NullMemberEqualityTest() { var a = new Disorder(); var b = new Disorder(); Assert.True(a == b); Assert.True(a.GetHashCode() == b.GetHashCode()); Assert.False(a != b); Assert.False(a.GetHashCode() != b.GetHashCode()); } [Fact] public void NullMemberOrderingTest() { var a = new Disorder(); var b = new Disorder(); Assert.True(a.CompareTo(b) == 0); Assert.True(b.CompareTo(a) == 0); } } } ================================================ FILE: LanguageExt.Tests/RecordTypesTest.cs ================================================ using LanguageExt.ClassInstances; using LanguageExt.Traits; using Newtonsoft.Json; using System; using System.Collections.Generic; using System.IO; using System.Runtime.Serialization; using Xunit; namespace LanguageExt.Tests { public class TestClass : Record { public readonly int X; public readonly string Y; public readonly Guid Z; public TestClass(int x, string y, Guid z) { X = x; Y = y; Z = z; } TestClass(SerializationInfo info, StreamingContext context) : base(info, context) { } } public class TestRecord : Record { public readonly Option Opt1; public readonly Option Opt2; public TestRecord(Option opt1, Option opt2) { Opt1 = opt1; Opt2 = opt2; } } public class DerivedTestClass : TestClass { public readonly int Extra; public DerivedTestClass(int x, string y, Guid z, int extra) : base(x, y, z) { Extra = extra; } } public class TestClass2 : Record { [NonEq] public readonly int X; [NonHash] public readonly string Y; [NonShow] public readonly Guid Z; public TestClass2(int x, string y, Guid z) { X = x; Y = y; Z = z; } } public class TestClass3 : Record { public readonly int X; public readonly string Y; public readonly Guid Z; public TestClass W { get; } public TestClass3(int x, string y, Guid z, TestClass w) { X = x; Y = y; Z = z; W = w; } TestClass3(SerializationInfo info, StreamingContext context) : base(info, context) { } } public class RecordTypeTests { static readonly Guid guid = Guid.Parse("{2ba1ec03-8309-46f6-a93e-5d6aada3a43c}"); [Fact] public void EqualityOfOriginTest() { var x = new TestClass(1, "Hello", Guid.Empty); var y = new DerivedTestClass(1, "Hello", Guid.Empty, 1000); Assert.False(x.Equals(y)); // Different types must not be equal Assert.False(y.Equals(x)); // Different types must not be equal } [Fact] public void DeepEqualityTestFieldsAndProperties() { var x1 = new TestClass(1, "Hello", Guid.Empty); var x2 = new TestClass(1, "Hello", Guid.Empty); var y1 = new TestClass3(1, "Hello", Guid.Empty, x1); var y2 = new TestClass3(1, "Hello", Guid.Empty, x2); Assert.True(y1 == y2); } [Fact] public void DeepInEqualityTestFieldsAndProperties() { var x1 = new TestClass(1, "Hello", Guid.Empty); var x2 = new TestClass(1, "Hello", guid); var y1 = new TestClass3(1, "Hello", Guid.Empty, x1); var y2 = new TestClass3(1, "Hello", Guid.Empty, x2); Assert.True(y1 != y2); } [Fact] public void SerialisationTest() { var x = new TestClass(1, "Hello", Guid.Empty); var y = new TestClass(1, "Hello", guid); var x1 = JsonConvert.SerializeObject(x); var y1 = JsonConvert.SerializeObject(y); var x2 = JsonConvert.DeserializeObject(x1); var y2 = JsonConvert.DeserializeObject(y1); Assert.True(x == x2); Assert.True(y == y2); } [Fact] public void ToStringTest() { var x = new TestClass(1, "Hello", Guid.Empty); var y = new TestClass(1, "Hello", guid); Assert.True(x.ToString() == "TestClass(1, Hello, 00000000-0000-0000-0000-000000000000)"); Assert.True(y.ToString() == "TestClass(1, Hello, 2ba1ec03-8309-46f6-a93e-5d6aada3a43c)"); } [Fact] public void EqualityOperatorTest() { var x = new TestClass(1, "Hello", Guid.Empty); var y = new TestClass(1, "Hello", Guid.Empty); Assert.True(x == y); } [Fact] public void EqualityMethodTest() { var x = new TestClass(1, "Hello", Guid.Empty); var y = new TestClass(1, "Hello", Guid.Empty); Assert.True(x.Equals(y)); } [Fact] public void NullEqualityOperatorTest() { TestClass x = new TestClass(1, "Hello", Guid.Empty); TestClass y = null; TestClass z = null; Assert.True(x != y); Assert.True(y != x); Assert.True(y == z); Assert.True(z == y); } [Fact] public void NullEqualityMethodTest() { TestClass x = new TestClass(1, "Hello", Guid.Empty); TestClass y = null; Assert.False(x.Equals(y)); } [Fact] public void InEqualityOperatorTest() { var x = new TestClass(1, "Hello", Guid.Empty); var y = new TestClass(2, "Hello", Guid.Empty); var z = new TestClass(1, "Hello!", Guid.Empty); var w = new TestClass(1, "Hello", guid); Assert.True(x != y); Assert.True(x != z); Assert.True(x != w); } [Fact] public void InEqualityMethodTest() { var x = new TestClass(1, "Hello", Guid.Empty); var y = new TestClass(2, "Hello", Guid.Empty); var z = new TestClass(1, "Hello!", Guid.Empty); var w = new TestClass(1, "Hello", guid); Assert.False(x.Equals(y)); Assert.False(x.Equals(z)); Assert.False(x.Equals(w)); } [Fact] public void HashingTest() { var a = new TestClass(1, "Hello", Guid.Empty); var b = new TestClass(1, "Hello", Guid.Empty); var c = new TestClass(2, "Hello", Guid.Empty); var d = new TestClass(1, "Hello!", Guid.Empty); var e = new TestClass(1, "Hello", guid); Assert.True(a.GetHashCode() == b.GetHashCode()); Assert.True(a.GetHashCode() != c.GetHashCode()); Assert.True(a.GetHashCode() != d.GetHashCode()); Assert.True(a.GetHashCode() != e.GetHashCode()); } [Fact] public void OrderingTest() { var x = new TestClass(1, "Hello", Guid.Empty); var y = new TestClass(1, "Hello", Guid.Empty); Assert.True(x.CompareTo(y) == 0); } [Fact] public void NullOrderingOperatorTest() { TestClass x = new TestClass(1, "Hello", Guid.Empty); TestClass y = null; TestClass z = null; Assert.True(x > y); Assert.True(x >= y); Assert.True(y < x); Assert.True(y <= x); Assert.True(y == z); Assert.True(z == y); } [Fact] public void NullOrderingMethodTest() { TestClass x = new TestClass(1, "Hello", Guid.Empty); TestClass y = null; Assert.True(x.CompareTo(y) > 0); } [Fact] public void OrderingOperatorTest() { var x = new TestClass(1, "Hello", Guid.Empty); var y = new TestClass(2, "Hello", Guid.Empty); var z = new TestClass(1, "Jello", Guid.Empty); Assert.True(x < y); Assert.True(x <= y); Assert.True(y > x); Assert.True(y >= x); Assert.True(x < z); Assert.True(x <= z); Assert.True(z > x); Assert.True(z >= x); } [Fact] public void OrderingMethodTest() { var x = new TestClass(1, "Hello", Guid.Empty); var y = new TestClass(2, "Hello", Guid.Empty); var z = new TestClass(1, "Jello", Guid.Empty); Assert.True(GenericCompare, TestClass>(x, y) < 0); Assert.True(GenericCompare, TestClass>(x, y) <= 0); Assert.True(GenericCompare, TestClass>(y, x) > 0); Assert.True(GenericCompare, TestClass>(y, x) >= 0); Assert.True(GenericCompare, TestClass>(x, z) < 0); Assert.True(GenericCompare, TestClass>(x, z) <= 0); Assert.True(GenericCompare, TestClass>(z, x) > 0); Assert.True(GenericCompare, TestClass>(z, x) >= 0); } [Fact] public void EqClassInstanceTest() { var x = new TestClass(1, "Hello", Guid.Empty); var y = new TestClass(1, "Hello", Guid.Empty); var z = new TestClass(2, "Hello", Guid.Empty); var resA = GenericEquals,TestClass>(x, y); var resB = GenericEquals,TestClass>(x, z); Assert.True(resA); Assert.False(resB); } [Fact] public void OrdClassInstanceTest() { var x = new TestClass(1, "Hello", Guid.Empty); var y = new TestClass(2, "Hello", Guid.Empty); var z = new TestClass(1, "Jello", Guid.Empty); Assert.True(x.CompareTo(y) < 0); Assert.True(x.CompareTo(y) <= 0); Assert.True(y.CompareTo(x) > 0); Assert.True(y.CompareTo(x) >= 0); Assert.True(x.CompareTo(z) < 0); Assert.True(x.CompareTo(z) <= 0); Assert.True(z.CompareTo(x) > 0); Assert.True(z.CompareTo(x) >= 0); } public bool GenericEquals(A x, A y) where EqA : Eq => EqA.Equals(x, y); public int GenericCompare(A x, A y) where OrdA : Ord => OrdA.Compare(x, y); [Fact] public void OptOutOfEqTest() { var x = new TestClass2(1, "Hello", Guid.Empty); var y = new TestClass2(1, "Hello", Guid.Empty); var z = new TestClass2(2, "Hello", Guid.Empty); Assert.True(x == y); Assert.True(x == z); } [Fact] public void OptOutOfHashCodeTest() { var x = new TestClass2(1, "Hello", Guid.Empty); var y = new TestClass2(1, "Hello32543534", Guid.Empty); var z = new TestClass2(1, "Hello", Guid.Empty); Assert.True(x.GetHashCode() == y.GetHashCode()); Assert.True(x.GetHashCode() == z.GetHashCode()); } [Fact] public void OptOutOfToString() { var x = new TestClass2(1, "Hello", Guid.Empty); var y = new TestClass2(1, "Hello", Guid.NewGuid()); Assert.True(x.ToString() == y.ToString()); } [Fact] // https://github.com/louthy/language-ext/issues/560 public void EnsureHashingIsNotOnlyXoring() { var testRecord1 = new TestRecord(Option.None, 2); var testRecord2 = new TestRecord(2, Option.None); Assert.False(testRecord1.GetHashCode() == testRecord2.GetHashCode()); testRecord1 = new TestRecord(Option.None, Option.None); testRecord2 = new TestRecord(1, 1); Assert.False(testRecord1.GetHashCode() == testRecord2.GetHashCode()); } } } ================================================ FILE: LanguageExt.Tests/RefTest.cs ================================================ using System.Linq; using Xunit; namespace LanguageExt.Tests; public class Account { public readonly int Balance; Account(int balance) => Balance = balance; public Account SetBalance(int value) => new Account(value); public Account AddBalance(int value) => new Account(Balance + value); public Account Deposit(int value) => new Account(Balance + value); public static Ref New(int balance) => Ref(new Account(balance), Account.Validate); public static bool Validate(Account a) => a.Balance >= 0; public override string ToString() => Balance.ToString(); } public static class Transfer { public static Unit Do(Ref from, Ref to, int amount) => atomic(() => { from.Value = from.Value.AddBalance(-amount); to.Value = to.Value.AddBalance(amount); }); } public class RefTest { [Fact] public void BankBalanceChangeTest() { var accountA = Account.New(200); var accountB = Account.New(0); var stateA = Option.None; var stateB = Option.None; var changedA = 0; var changedB = 0; accountA.Change += v => { stateA = v; changedA++; }; accountB.Change += v => { stateB = v; changedB++; }; atomic(() => { accountA.Value = accountA.Value.AddBalance(-50); accountB.Value = accountB.Value.AddBalance(50); accountA.Value = accountA.Value.AddBalance(-5); accountB.Value = accountB.Value.AddBalance(5); Assert.Equal(None, stateA); Assert.Equal(None, stateB); }); Assert.Equal(Some(accountA.Value), stateA); Assert.Equal(Some(accountB.Value), stateB); Assert.Equal(1, changedA); Assert.Equal(1, changedB); } [Fact] public void SimpleBankBalanceTest() { var accountA = Account.New(200); var accountB = Account.New(0); Transfer.Do(accountA, accountB, 100); var (balanceA, balanceB) = atomic(() => (accountA.Value.Balance, accountB.Value.Balance)); Assert.True(balanceA == balanceB); } [Fact] public void CommuteTest() { const int count = 1000; static int inc(Ref counter) => atomic(() => commute(counter, static x => x + 1)); var num = Ref(0); var res = Range(0, count).AsParallel() .Select(_ => inc(num)) .AsIterable() .ToSeq() .Strict(); Assert.True(num == count); } [Fact] static void DepositCommuteTest() { var bank = Account.New(0); LogDeposit(bank, 100); LogDeposit(bank, 50); Assert.True(bank.Value.Balance == 150); var bank2 = Account.New(0); LogDeposit(bank2, 50); LogDeposit(bank2, 100); Assert.True(bank2.Value.Balance == 150); } static void LogDeposit(Ref account, int amount) => atomic(() => commute(account, a => a.Deposit(amount))); } ================================================ FILE: LanguageExt.Tests/ReflectTests.cs ================================================ using System; using System.Collections.Generic; using System.Text; using Xunit; namespace LanguageExt.Tests { public class ReflectTests { public class TestClass { public readonly string W; public readonly string X; public readonly string Y; public readonly string Z; public TestClass() { } public TestClass(string w) { W = w; } public TestClass(string w, string x) { W = w; X = x; } public TestClass(string w, bool x) { W = w; X = x.ToString(); } public TestClass(string w, string x, string y) { W = w; X = x; Y = y; } public TestClass(string w, string x, string y, string z) { W = w; X = x; Y = y; Z = z; } } [Fact] public void CtorOfArity1Test() { var ctor = IL.Ctor(); var res = ctor("Hello"); Assert.True(res.W == "Hello"); } [Fact] public void CtorOfArity2Test() { var ctor = IL.Ctor(); var res = ctor("Hello", "World"); Assert.True(res.W == "Hello"); Assert.True(res.X == "World"); } [Fact] public void CtorOfArity2Test2() { var ctor = IL.Ctor(); var res = ctor("Hello", true); Assert.True(res.W == "Hello"); Assert.True(res.X == "True"); } [Fact] public void CtorOfArity3Test() { var ctor = IL.Ctor(); var res = ctor("Roland","TR", "909"); Assert.True(res.W == "Roland"); Assert.True(res.X == "TR"); Assert.True(res.Y == "909"); } [Fact] public void CtorOfArity4Test() { var ctor = IL.Ctor(); var res = ctor("Chandler", "Curve", "Bender", "EQ"); Assert.True(res.W == "Chandler"); Assert.True(res.X == "Curve"); Assert.True(res.Y == "Bender"); Assert.True(res.Z == "EQ"); } [Fact] public void DateConstructTest() { var ticks = new DateTime(2017, 1, 1).Ticks; var ctor = IL.Ctor(); DateTime res = ctor(ticks); Assert.True(res.Ticks == ticks); } } } ================================================ FILE: LanguageExt.Tests/ScheduleTest/EffTests1.cs ================================================ using Xunit; using System.IO; using System.Text; using LanguageExt.Sys; using LanguageExt.Common; using LanguageExt.Sys.Test; using LanguageExt.Sys.Traits; using System.Collections.Generic; using static LanguageExt.UnitsOfMeasure; namespace LanguageExt.Tests.ScheduleTest; public static class EffTests1 { static Schedule TestSchedule() => Schedule.fixedInterval(1 * ms) | Schedule.NoDelayOnFirst | Schedule.recurs(5); [Fact] public static void BailBeforeScheduleTest1() { const int counter = 0; var effect = FailEff((Error)"Failed"); var result = effect.RepeatIO(TestSchedule()).Run(); Assert.Equal(0, counter); result.AssertFail(Error.New("Failed")); } [Fact] public static void BailBeforeScheduleTest2() { using var rt = Runtime.New(); const int counter = 0; var effect = FailEff((Error)"Failed"); var result = effect.RepeatIO(TestSchedule()).Run(rt, EnvIO.New()); Assert.Equal(0, counter); result.AssertFail(Error.New("Failed")); } [Fact] public static void RepeatTest1() { var counter = 0; var effect = liftEff(async () => await (++counter).AsValueTask()); var result = effect.RepeatIO(TestSchedule()).Run(); Assert.Equal(6, counter); result.AssertSucc(6); } [Fact] public static void RepeatTest2() { using var rt = Runtime.New(); var counter = 0; var effect = liftEff(async _ => await (++counter).AsValueTask()); var result = effect.RepeatIO(TestSchedule()).Run(rt, EnvIO.New()); Assert.Equal(6, counter); result.AssertSucc(6); } [Fact] public static void RetryTest1() { var counter = 0; var effect = liftEff( async () => { await (++counter).AsValueTask(); return Error.New("Failed"); }); var result = effect.RetryIO(TestSchedule()).Run(); Assert.Equal(6, counter); result.AssertFail(Error.New("Failed")); } [Fact] public static void RetryTest2() { using var rt = Runtime.New(); var counter = 0; var effect = liftEff( async _ => { await (++counter).AsValueTask(); return Error.New("Failed"); }); var result = effect.RetryIO(TestSchedule()).Run(rt, EnvIO.New()); Assert.Equal(6, counter); result.AssertFail(Error.New("Failed")); } [Fact] public static void RepeatWhileTest1() { var counter = 0; var effect = liftEff(async () => await (++counter).AsValueTask()); var result = effect.RepeatWhileIO(TestSchedule(), static i => i < 3).Run(); Assert.Equal(3, counter); result.AssertSucc(3); } [Fact] public static void RepeatWhileTest2() { using var rt = Runtime.New(); var counter = 0; var effect = liftEff(async _ => await (++counter).AsValueTask()); var result = effect.RepeatWhileIO(TestSchedule(), static i => i < 3).Run(rt, EnvIO.New()); Assert.Equal(3, counter); result.AssertSucc(3); } [Fact] public static void RetryWhileTest1() { var counter = 0; var effect = liftEff( async () => { await (++counter).AsValueTask(); return Error.New(counter.ToString()); }); var result = effect.RetryWhileIO(TestSchedule(), static e => (int)parseInt(e.Message) < 3).Run(); Assert.Equal(3, counter); result.AssertFail(Error.New("3")); } [Fact] public static void RetryWhileTest2() { using var rt = Runtime.New(); var counter = 0; var effect = liftEff( async _ => { await (++counter).AsValueTask(); return Error.New(counter.ToString()); }); var result = effect.RetryWhileIO(TestSchedule(), static e => (int)parseInt(e.Message) < 3).Run(rt, EnvIO.New()); Assert.Equal(3, counter); result.AssertFail(Error.New("3")); } [Fact] public static void RepeatUntilTest1() { var counter = 0; var effect = liftEff(async () => await (++counter).AsValueTask()); var result = effect.RepeatUntilIO(static i => i == 10).Run(); Assert.Equal(10, counter); result.AssertSucc(10); } [Fact] public static void RepeatUntilTest2() { using var rt = Runtime.New(); var counter = 0; var effect = liftEff(async _ => await (++counter).AsValueTask()); var result = effect.RepeatUntilIO(static i => i == 10).Run(rt, EnvIO.New()); Assert.Equal(10, counter); result.AssertSucc(10); } [Fact] public static void RetryUntilTest1() { var counter = 0; var effect = liftEff( async () => { await (++counter).AsValueTask(); return Error.New(counter.ToString()); }); var result = effect.RetryUntilIO(static e => (int)parseInt(e.Message) == 10).Run(); Assert.Equal(10, counter); result.AssertFail(Error.New("10")); } [Fact] public static void RetryUntilTest2() { using var rt = Runtime.New(); var counter = 0; var effect = liftEff( async _ => { await (++counter).AsValueTask(); return Error.New(counter.ToString()); }); var result = effect.RetryUntilIO(static e => (int)parseInt(e.Message) == 10).Run(rt, EnvIO.New()); Assert.Equal(10, counter); result.AssertFail(Error.New("10")); } [Fact] public static void FoldTest1() { var counter = 0; var effect = liftEff(async () => await (++counter).AsValueTask()); var result = effect.FoldIO(TestSchedule(), 1, (i, j) => i + j).Run(); Assert.Equal(6, counter); result.AssertSucc(22); } [Fact] public static void FoldTest2() { using var rt = Runtime.New(); var counter = 0; var effect = liftEff(async _ => await (++counter).AsValueTask()); var result = effect.FoldIO(TestSchedule(), 1, (i, j) => i + j).Run(rt, EnvIO.New()); Assert.Equal(6, counter); result.AssertSucc(22); } [Fact] public static void FoldWhileTest1() { var counter = 0; var effect = liftEff(async () => await (++counter).AsValueTask()); var result = effect.FoldWhileIO(TestSchedule(), 1, (i, j) => i + j, valueIs: i => i < 3).Run(); Assert.Equal(3, counter); result.AssertSucc(4); } [Fact] public static void FoldWhileTest2() { using var rt = Runtime.New(); var counter = 0; var effect = liftEff(async _ => await (++counter).AsValueTask()); var result = effect.FoldWhileIO(TestSchedule(), 1, (i, j) => i + j, valueIs: i => i < 3).Run(rt, EnvIO.New()); Assert.Equal(3, counter); result.AssertSucc(4); } [Fact] public static void FoldUntilTest1() { var counter = 0; var effect = liftEff(async () => await (++counter).AsValueTask()); var result = effect.FoldUntilIO(TestSchedule(), 1, (i, j) => i + j, valueIs: i => i > 4).Run(); Assert.Equal(5, counter); result.AssertSucc(16); } [Fact] public static void FoldUntilTest2() { using var rt = Runtime.New(); var counter = 0; var effect = liftEff(async _ => await (++counter).AsValueTask()); var result = effect.FoldUntilIO(TestSchedule(), 1, (i, j) => i + j, valueIs: i => i > 4) .Run(rt, EnvIO.New()); Assert.Equal(5, counter); result.AssertSucc(16); } [Fact] public static void CancelTest() { using var rt = Runtime.New(); var counter = 0; var envIO = EnvIO.New(); var effect = liftEff(async _ => await (++counter).AsValueTask()).RepeatIO(Schedule.Forever); envIO.Source.Cancel(); var result = effect.Run(rt, envIO); Assert.Equal(0, counter); result.AssertFail(Errors.Cancelled); } [Fact(DisplayName = "Schedule Run against Aff should not capture state")] public static void ShouldNotCaptureState1Test() { var content = Encoding.ASCII.GetBytes("test\0test\0test\0"); var memStream = new MemoryStream(100); memStream.Write(content, 0, content.Length); memStream.Seek(0, SeekOrigin.Begin); Eff AddToBuffer(ICollection buffer, string value) => lift(() => { buffer.Add(value); return unit; }); Eff CreateEffect(ICollection buffer) => repeat(from ln in (from data in liftEff(() => memStream.ReadByte().AsTask()) from _ in guard(data != -1, Errors.Cancelled) select data) .FoldUntilIO(string.Empty, (s, ch) => s + (char)ch, ch => ch == '\0') from _0 in AddToBuffer(buffer,ln) select unit).As() | @catch(error => AddToBuffer(buffer,error.Message)); var buffer = new List(); var effect = CreateEffect(buffer); effect.Run(EnvIO.New()).Ignore(); Assert.True(toSeq(buffer) == Seq("test\0", "test\0", "test\0", "cancelled")); } [Fact(DisplayName = "Schedule Run against Aff should not capture state")] public static void ShouldNotCaptureState2Test() { var content = Encoding.ASCII.GetBytes("test\0test\0test\0"); var memStream = new MemoryStream(100); memStream.Write(content, 0, content.Length); memStream.Seek(0, SeekOrigin.Begin); Eff CreateEffect() where RT : Has, ConsoleIO> => repeat(from ln in (from data in liftEff(() => memStream.ReadByte().AsTask()) from _ in guard(data != -1, Errors.Cancelled) select data) .FoldUntilIO(string.Empty, (s, ch) => s + (char)ch, ch => ch == '\0') .As() from _0 in Console.writeLine(ln) select unit).As() | @catch(error => Console.writeLine(error.Message)); using var runtime = Runtime.New(); var effect = CreateEffect(); effect.Run(runtime, EnvIO.New()).Ignore(); Assert.True(toSeq(runtime.Env.Console) == Seq("test\0", "test\0", "test\0", "cancelled")); } } ================================================ FILE: LanguageExt.Tests/ScheduleTest/PositiveDurationTests.cs ================================================ using Xunit; namespace LanguageExt.Tests.ScheduleTest; public static class PositiveDurationTests { [Fact] public static void EqualsTest() => Assert.True(new Duration(2) == new Duration(2)); [Fact] public static void NotEqualsTest() => Assert.True(new Duration(2) != new Duration(4)); [Fact] public static void GreaterThanTest() => Assert.True(new Duration(4) > new Duration(2)); [Fact] public static void GreaterThanEqualToTest() => Assert.True(new Duration(4) >= new Duration(4)); [Fact] public static void GreaterThanEqualToTest2() => Assert.False(new Duration(2) >= new Duration(4)); [Fact] public static void LessThanTest() => Assert.True(new Duration(2) < new Duration(4)); [Fact] public static void GreaterLessThanOrEqualToTest() => Assert.True(new Duration(2) <= new Duration(2)); [Fact] public static void GreaterLessThanOrEqualToTest2() => Assert.False(new Duration(5) <= new Duration(2)); } ================================================ FILE: LanguageExt.Tests/ScheduleTest/ScheduleTests.cs ================================================ using Xunit; using System; using System.Linq; using FluentAssertions; using static LanguageExt.Prelude; using System.Diagnostics.Contracts; using LanguageExt.UnsafeValueAccess; using static LanguageExt.UnitsOfMeasure; namespace LanguageExt.Tests.ScheduleTest; public sealed class ScheduleTests { [Fact] public static void ForeverTest() { var result = Schedule.Forever; result .Run() .Take(10) .AsEnumerable() .Should() .HaveCount(10) .And .OnlyContain(x => x == Duration.Zero); } [Fact] public static void NeverTest() { var result = Schedule.Never; result .Run() .AsEnumerable() .Should() .BeEmpty(); } [Fact] public static void OnceTest() { var result = Schedule.Once; result .Run() .AsEnumerable() .Should() .ContainSingle(x => x == Duration.Zero); } [Fact] public static void FromDurationsTest() { var result = Schedule.TimeSeries(1 * sec, 2 * sec, 3 * sec); result .Run() .AsEnumerable() .Should() .Equal(1 * sec, 2 * sec, 3 * sec); } [Fact] public static void FromDurationsTest2() { var result = Schedule.TimeSeries( Seq(1, 2, 3, 4, 5) .Filter(x => x % 2 == 0) .Map(x => x * seconds)); result .Run() .AsEnumerable() .Should() .Equal(2 * sec, 4 * sec); } [Fact] public static void RecursTest() { var results = Schedule.recurs(5) | Schedule.Forever; results .Run() .AsEnumerable() .Should() .HaveCount(5) .And .Contain(x => x == Duration.Zero); } [Fact] public static void SpacedTest() { var results = Schedule.spaced(5 * sec); results .Run() .Take(5) .AsEnumerable() .Should() .HaveCount(5) .And .OnlyContain(x => x == 5 * sec); } [Fact] public static void LinearTest() { var results = Schedule.linear(1 * sec); results .Run() .Take(5) .AsEnumerable() .Should() .Equal(1 * sec, 2 * sec, 3 * sec, 4 * sec, 5 * sec); } [Fact] public static void LinearTest2() { var results = Schedule.linear(100 * ms, 2); results .Run() .Take(5) .AsEnumerable() .Should() .Equal(100, 300, 500, 700, 900); } [Fact] public static void ExponentialTest() { var results = Schedule.exponential(1 * sec); results .Run() .Take(5) .AsEnumerable() .Should() .Equal(1 * sec, 2 * sec, 4 * sec, 8 * sec, 16 * sec); } [Fact] public static void ExponentialTest2() { var results = Schedule.exponential(1 * sec, 3); results .Run() .Take(5) .AsEnumerable() .Should() .Equal(1 * sec, 3 * sec, 9 * sec, 27 * sec, 81 * sec); } [Fact] public static void FibonacciTest() { var results = Schedule.fibonacci(1 * sec); results .Run() .Take(6) .AsEnumerable() .Should() .Equal(1 * sec, 1 * sec, 2 * sec, 3 * sec, 5 * sec, 8 * sec); } [Fact] public static void NoDelayOnFirstTest() { var transformer = Schedule.NoDelayOnFirst; var results = transformer.Apply(Schedule.spaced(10 * sec)); results .Run() .Take(5) .AsEnumerable() .Should() .Equal(0 * sec, 10 * sec, 10 * sec, 10 * sec, 10 * sec); } [Fact] public static void MaxDelayTest() { var transformer = Schedule.maxDelay(25 * sec); var results = transformer.Apply(Schedule.linear(10 * sec)); results .Run() .Take(5) .Max().ValueUnsafe() .Should().Be(25 * sec); } [Fact] public static void MaxCumulativeDelayTest() { var transformer = Schedule.maxCumulativeDelay(40 * sec); var results = transformer.Apply(Schedule.linear(10 * sec)).Run().ToSeq(); Assert.True(results.Count == 3); Assert.True(results.Max().ValueUnsafe() == 30 * sec); } [Fact] public static void UnionTest() { var results = Schedule.spaced(5 * sec).Union(Schedule.exponential(1 * sec)); results .Run() .Take(5) .AsEnumerable() .Should() .Equal(1 * sec, 2 * sec, 4 * sec, 5 * sec, 5 * sec); } [Fact] public static void IntersectTest() { var results = Schedule.spaced(5 * sec).Intersect(Schedule.exponential(1 * sec)); results .Run() .Take(5) .AsEnumerable() .Should() .Equal(5 * sec, 5 * sec, 5 * sec, 8 * sec, 16 * sec); } [Fact] public static void AppendTest() { var results = Schedule.TimeSeries(1 * sec, 2 * sec, 3 * sec) + Schedule.TimeSeries(4 * sec, 5 * sec, 6 * sec); results .Run() .AsEnumerable() .Should() .Equal(1 * sec, 2 * sec, 3 * sec, 4 * sec, 5 * sec, 6 * sec); } [Pure] static Seq FromDuration(Duration duration) { var now = DateTime.Now; return IterableExtensions.AsIterable(Range(0, (int)((TimeSpan)duration).TotalSeconds)) .Map(i => now + TimeSpan.FromSeconds(i)) .ToSeq(); } [Pure] static Seq FromDurations(Seq durations) => durations.Fold(Seq(DateTime.Now), (times, duration) => { var last = times.Head; return times.Add(last.ValueUnsafe() + (TimeSpan)duration); }); [Pure] static Func FromDates(Seq dates) => () => { var date = dates.Head.IfNone(() => DateTime.Now); dates = dates.Tail; return date; }; [Fact] public static void UpToTest() { var results = Schedule.upto(5 * sec, FromDates(FromDuration(2 * min))); results .Run() .AsEnumerable() .Should() .Equal(0, 0, 0, 0); } [Fact] public static void FixedTest() { var results = Schedule.fixedInterval(5 * sec, FromDates(FromDurations(Seq( 6 * sec, 1 * sec, 4 * sec )))); results .Run() .Take(3) .AsEnumerable() .Should() .Equal(0, 4 * sec, 1 * sec); } [Fact] public static void WindowedTest() { var results = Schedule.windowed(5 * sec, FromDates(FromDurations(Seq( 6 * sec, 1 * sec, 7 * sec )))); results .Run() .Take(3) .AsEnumerable() .Should() .Equal(4 * sec, 4 * sec, 3 * sec); } [Fact] public static void SecondOfMinuteTest() { var results = Schedule.secondOfMinute(3, FromDates(Seq( new DateTime(2022, 1, 1, 1, 1, 26), new DateTime(2022, 1, 1, 1, 1, 1), new DateTime(2022, 1, 1, 1, 1, 47) ))); results .Run() .Take(3) .AsEnumerable() .Should() .Equal(37 * sec, 2 * sec, 16 * sec); } [Fact] public static void MinuteOfHourTest() { var results = Schedule.minuteOfHour(3, FromDates(Seq( new DateTime(2022, 1, 1, 1, 26, 0), new DateTime(2022, 1, 1, 1, 1, 0), new DateTime(2022, 1, 1, 1, 47, 0) ))); results .Run() .Take(3) .AsEnumerable() .Should() .Equal(37 * min, 2 * min, 16 * min); } [Fact] public static void HourOfDayTest() { var results = Schedule.hourOfDay(3, FromDates(Seq( new DateTime(2022, 1, 1, 1, 0, 0), new DateTime(2022, 1, 1, 4, 0, 0), new DateTime(2022, 1, 1, 6, 0, 0), new DateTime(2022, 1, 1, 3, 0, 0) ))); results .Run() .Take(4) .AsEnumerable() .Should() .Equal(2 * hours, 23 * hours, 21 * hour, 24 * hours); } [Fact] public static void DayOfWeekTest() { var results = Schedule.dayOfWeek(DayOfWeek.Wednesday, FromDates(Seq( new DateTime(2022, 1, 1, 0, 0, 0), // Saturday new DateTime(2022, 1, 3, 0, 0, 0), // Monday new DateTime(2022, 1, 7, 0, 0, 0), // Friday new DateTime(2022, 1, 5, 0, 0, 0) // Wednesday ))); results .Run() .Take(4) .AsEnumerable() .Should() .Equal(4 * days, 2 * days, 5 * days, 7 * days); } const int Seed = 98192732; [Fact] public static void JitterTest1() { var noJitter = ( Schedule.linear(10 * seconds) & Schedule.recurs(5)).Run().ToSeq(); var withJitter = ( Schedule.linear(10 * seconds) & Schedule.recurs(5) & Schedule.jitter(1 * ms, 10 * ms)).Run().ToSeq(); var res = withJitter.ToArray(); Assert.True(res.Length == 5); Assert.True(res.Zip(noJitter) .AsIterable() .Filter(p => p.Item1 > p.Item2 && p.Item1 - p.Item2 <= 100) .Any()); } [Fact] public static void JitterTest2() { var noJitter = ( Schedule.linear(10 * seconds) & Schedule.recurs(5)).Run().ToSeq(); var withJitter = ( Schedule.linear(10 * seconds) & Schedule.recurs(5) & Schedule.jitter(1.5)).Run().ToSeq(); var res = withJitter.ToArray(); Assert.True(res.Length == 5); Assert.True(res.Zip(noJitter) .AsIterable() .Filter(p => p.Item1 > p.Item2 && p.Item1 - p.Item2 <= p.Item2 * 1.5) .Any()); } [Fact] public static void DecorrelatedTest() { var schedule = Schedule.linear(10 * sec) | Schedule.decorrelate(seed: Seed); var result = schedule.Take(5).Run().ToSeq(); Assert.True(result.Zip(result.Skip(1)).Filter(x => x.First > x.Second).Any()); } [Fact] public static void ResetAfterTest() { var results = Schedule.linear(10 * sec) | Schedule.resetAfter(25 * sec); results .Run() .Take(4) .AsEnumerable() .Should() .Equal(10 * sec, 20 * sec, 10 * sec, 20 * sec); } [Fact] public static void RepeatForeverTest() { var results = Schedule.TimeSeries(1 * sec, 5 * sec, 20 * sec) | Schedule.RepeatForever; results .Run() .Take(12) .AsEnumerable() .Should() .Equal(1 * sec, 5 * sec, 20 * sec, 1 * sec, 5 * sec, 20 * sec, 1 * sec, 5 * sec, 20 * sec, 1 * sec, 5 * sec, 20 * sec); } [Fact] public static void RepeatTest() { var results = Schedule.TimeSeries(1 * sec, 5 * sec, 20 * sec) | Schedule.repeat(3); results .Run() .AsEnumerable() .Should() .HaveCount(9) .And .Equal(1 * sec, 5 * sec, 20 * sec, 1 * sec, 5 * sec, 20 * sec, 1 * sec, 5 * sec, 20 * sec); } [Fact] public static void IntersperseTest() { var results = Schedule.TimeSeries(1 * sec, 5 * sec, 20 * sec) | Schedule.intersperse(2 * sec); results .Run() .AsEnumerable() .Should() .HaveCount(6) .And .Equal(1 * sec, 2 * sec, 5 * sec, 2 * sec, 20 * sec, 2 * sec); } [Fact] public static void InterleaveTest() { var schedule1 = Schedule.TimeSeries(1 * sec, 5 * sec, 20 * sec); var schedule2 = Schedule.TimeSeries(2 * sec, 7 * sec, 25 * sec); var results = schedule1.Interleave(schedule2); results .Run() .AsEnumerable() .Should() .HaveCount(6) .And .Equal(1 * sec, 2 * sec, 5 * sec, 7 * sec, 20 * sec, 25 * sec); } [Fact] public static void RandomDurationTest() { var schedule1 = Schedule.linear(Duration.random(10 * ms, 50 * ms)) | Schedule.decorrelate() | Schedule.recurs(5); var schedule2 = Schedule.linear(Duration.random(10 * ms, 50 * ms)) | Schedule.decorrelate() | Schedule.recurs(5); var schedule3 = Schedule.linear(Duration.random(10 * ms, 50 * ms)) | Schedule.decorrelate() | Schedule.recurs(5); schedule1.Run().AsEnumerable() .Should().HaveCount(5); schedule2.Run().AsEnumerable() .Should().HaveCount(5); schedule3.Run().AsEnumerable() .Should().HaveCount(5); } [Fact] public static void MapTest() { var schedule = Schedule.linear(1 * ms).Map((x, i) => x % 2 == 0 ? x + i : x - i).Take(4); schedule.Run() .AsEnumerable() .Should().Equal(1 * ms, 3 * ms, 1 * ms, 7 * ms); } [Fact] public static void FilterTest() { var schedule = Schedule.linear(1 * ms).Filter(x => x % 2 == 0).Take(4); schedule.Run().AsEnumerable() .Should().Equal(2 * ms, 4 * ms, 6 * ms, 8 * ms); } [Fact] public static void BindTest1() { var schedule = Schedule .linear(1 * ms) .Filter(x => x % 2 == 0) .Take(2) .Bind(even => Schedule .linear(1 * ms) .Filter(x => x % 2 != 0) .Take(2) .Bind(odd => Schedule.TimeSeries(even, odd))); schedule.Run() .AsEnumerable() .Should() .Equal(2 * ms, 1 * ms, 2 * ms, 3 * ms, 4 * ms, 1 * ms, 4 * ms, 3 * ms); } [Fact] public static void BindTest2() { var schedule = from even in Schedule .linear(1 * ms) .Filter(x => x % 2 == 0) .Take(2) from odd in Schedule .linear(1 * ms) .Filter(x => x % 2 != 0) .Take(2) select Math.Pow(even, odd); schedule.Run().AsEnumerable() .Should().Equal(2 * ms, 8 * ms, 4 * ms, 64 * ms); } } ================================================ FILE: LanguageExt.Tests/SeqTypes/Seq.Arr.Tests.cs ================================================ using System; using Xunit; namespace LanguageExt.Tests; public class SeqArrTests { [Fact] public void TestEmpty() { var arr = Arr.empty(); var seq = toSeq(arr); Assert.True(seq.IsEmpty); Assert.True(seq.Tail.IsEmpty); Assert.True(seq.Tail.Tail.IsEmpty); Assert.True(seq.Head.IsNone); Assert.True(seq.Count == 0); var res1 = seq.Match( () => true, (x, xs) => false); var res2 = seq.Match( () => true, x => false, (x, xs) => false); Assert.True(res1); Assert.True(res2); var skipped = seq.Skip(1); Assert.True(skipped.IsEmpty); Assert.True(skipped.Count == 0); Assert.True(skipped.Head.IsNone); } [Fact] public void TestGetHashCodeEmpty() { var seq = Arr.empty(); // check that GetHashCode won't throw _ = seq.GetHashCode(); } [Fact] public void TestGetHashCodeDefault() { Arr seq = default; // check that GetHashCode won't throw _ = seq.GetHashCode(); } [Fact] public void TestGetHashCodeDefaultCtor() { var seq = new Arr(); // check that GetHashCode won't throw _ = seq.GetHashCode(); } [Fact] public void TestGetHashCodePartOfContainer() { var container = new Arr[1]; // check that GetHashCode won't throw _ = container[0].GetHashCode(); } [Fact] public void TestGetHashCodeEmptyVsDefault() { var emptySeq = Arr.empty(); Arr defaultSeq = default; var emptyHash = emptySeq.GetHashCode(); var defaultHash = defaultSeq.GetHashCode(); Assert.Equal(emptyHash, defaultHash); } [Fact] public void TestOne() { var arr = Arr.create(1); var seq = toSeq(arr); Assert.True(seq.Head == 1); Assert.True(seq.Tail.IsEmpty); Assert.True(seq.Tail.Tail.IsEmpty); Assert.True(seq.Count == 1); var res1 = seq.Match( () => false, (x, xs) => x == 1 && xs.IsEmpty); var res2 = seq.Match( () => false, x => x == 1, (x, xs) => false); Assert.True(res1); Assert.True(res2); var skipped = seq.Skip(1); Assert.True(skipped.IsEmpty); Assert.True(skipped.Count == 0); Assert.True(skipped.Head.IsNone); } static int Sum(Seq seq) => seq.Match( () => 0, x => x, (x, xs) => x + Sum(xs)); [Fact] public void TestMore() { var arr = Arr.create(1, 2, 3, 4, 5); var seq = toSeq(arr); Assert.True(seq.Head == 1); Assert.True(seq.Tail.Head == 2); Assert.True(seq.Tail.Tail.Head == 3); Assert.True(seq.Tail.Tail.Tail.Head == 4); Assert.True(seq.Tail.Tail.Tail.Tail.Head == 5); Assert.True(seq.Tail.Tail.Tail.Tail.Tail.IsEmpty); Assert.True(seq.Count == 5); Assert.True(seq.Tail.Count == 4); Assert.True(seq.Tail.Tail.Count == 3); Assert.True(seq.Tail.Tail.Tail.Count == 2); Assert.True(seq.Tail.Tail.Tail.Tail.Count == 1); var res = Sum(seq); Assert.True(res == 15); var skipped1 = seq.Skip(1); Assert.True(skipped1.Head == 2); Assert.True(skipped1.Count == 4); var skipped2 = seq.Skip(2); Assert.True(skipped2.Head == 3); Assert.True(skipped2.Count == 3); var skipped3 = seq.Skip(3); Assert.True(skipped3.Head == 4); Assert.True(skipped3.Count == 2); var skipped4 = seq.Skip(4); Assert.True(skipped4.Head == 5); Assert.True(skipped4.Count == 1); var skipped5 = seq.Skip(5); Assert.True(skipped5.IsEmpty); Assert.True(skipped5.Count == 0); } [Fact] public void MapTest() { var arr = Arr.create(1, 2, 3, 4, 5); var Seq = toSeq(arr); var seq2 = Seq.Map(x => x * 2); var seq3 = Seq.Select(x => x * 2); var seq4 = from x in Seq select x * 2; var expected = toSeq(Arr.create(2, 4, 6, 8, 10)); Assert.True(expected == seq2); Assert.True(expected == seq3); Assert.True(expected == seq4); } [Fact] public void FilterTest() { var arr = Arr.create(1, 2, 3, 4, 5); var Seq = toSeq(arr); var seq2 = Seq.Filter(x => x % 2 == 0); var seq3 = Seq.Where(x => x % 2 == 0); var seq4 = from x in Seq where x % 2 == 0 select x; var expected = toSeq(Arr.create(2, 4)); Assert.True(expected == seq2); Assert.True(expected == seq3); Assert.True(expected == seq4); } [Fact] public void BindTest() { var seq1 = toSeq(Arr.create(10, 100)); var seq2 = toSeq(Arr.create(1, 2, 3, 4, 5)); var seq3 = seq1.Bind(x => seq2.Map(y => x * y)); var expected = Seq(10, 20, 30, 40, 50, 100, 200, 300, 400, 500); Assert.True(seq3 == expected); } [Fact] public void FoldTest1() { var seq = toSeq(Arr.create(1, 2, 3, 4, 5)); var res1 = seq.Fold(1, (s, x) => s * x); var res2 = seq.FoldBack(1, (s, x) => s * x); Assert.True(res1 == 120); Assert.True(res2 == 120); } [Fact] public void FoldTest2() { var seq = toSeq(Arr.create("a", "b", "c", "d", "e")); var res1 = seq.Fold("", (s, x) => s + x); var res2 = seq.FoldBack("", (s, x) => s + x); Assert.True(res1 == "abcde"); Assert.True(res2 == "edcba"); } [Fact] public void Existential() { var Seq = toSeq(Arr.create('a', 'b', 'c', 'd', 'e')); var seq2 = toSeq(Arr.create('a', 'b', 'c', '_', 'e')); var ex1 = Seq.Exists(x => x == 'd'); var ex2 = seq2.Exists(x => x == 'd'); var fa1 = Seq.ForAll(Char.IsLetter); var fa2 = seq2.ForAll(Char.IsLetter); Assert.True(ex1); Assert.False(ex2); Assert.True(fa1); Assert.False(fa2); } } ================================================ FILE: LanguageExt.Tests/SeqTypes/Seq.Cons.Tests.cs ================================================ using System; using Xunit; using static LanguageExt.Prelude; namespace LanguageExt.Tests { public class SeqConsTests { [Fact] public void TestEmpty() { var arr = Seq.Empty; var seq = toSeq(arr); Assert.True(seq.IsEmpty); Assert.True(seq.Tail.IsEmpty); Assert.True(seq.Tail.Tail.IsEmpty); Assert.True(seq.Head.IsNone); Assert.True(seq.Count == 0); var res1 = seq.Match( () => true, (x, xs) => false); var res2 = seq.Match( () => true, x => false, (x, xs) => false); Assert.True(res1); Assert.True(res2); var skipped = seq.Skip(1); Assert.True(skipped.IsEmpty); Assert.True(skipped.Count == 0); Assert.True(skipped.Head.IsNone); } [Fact] public void TestOne() { var arr = 1.Cons(); var seq = toSeq(arr); Assert.True(seq.Head == 1); Assert.True(seq.Tail.IsEmpty); Assert.True(seq.Tail.Tail.IsEmpty); Assert.True(seq.Count == 1); var res1 = seq.Match( () => false, (x, xs) => x == 1 && xs.IsEmpty); var res2 = seq.Match( () => false, x => x == 1, (x, xs) => false); Assert.True(res1); Assert.True(res2); var skipped = seq.Skip(1); Assert.True(skipped.IsEmpty); Assert.True(skipped.Count == 0); Assert.True(skipped.Head.IsNone); } static int Sum(Seq seq) => seq.Match( () => 0, x => x, (x, xs) => x + Sum(xs)); [Fact] public void TestMore() { var arr = 1.Cons(2.Cons(3.Cons(4.Cons(5.Cons())))); var seq = toSeq(arr); Assert.True(seq.Head == 1); Assert.True(seq.Tail.Head == 2); Assert.True(seq.Tail.Tail.Head == 3); Assert.True(seq.Tail.Tail.Tail.Head == 4); Assert.True(seq.Tail.Tail.Tail.Tail.Head == 5); Assert.True(seq.Tail.Tail.Tail.Tail.Tail.IsEmpty); Assert.True(seq.Count == 5); Assert.True(seq.Tail.Count == 4); Assert.True(seq.Tail.Tail.Count == 3); Assert.True(seq.Tail.Tail.Tail.Count == 2); Assert.True(seq.Tail.Tail.Tail.Tail.Count == 1); var res = Sum(seq); Assert.True(res == 15); var skipped1 = seq.Skip(1); Assert.True(skipped1.Head == 2); Assert.True(skipped1.Count == 4); var skipped2 = seq.Skip(2); Assert.True(skipped2.Head == 3); Assert.True(skipped2.Count == 3); var skipped3 = seq.Skip(3); Assert.True(skipped3.Head == 4); Assert.True(skipped3.Count == 2); var skipped4 = seq.Skip(4); Assert.True(skipped4.Head == 5); Assert.True(skipped4.Count == 1); var skipped5 = seq.Skip(5); Assert.True(skipped5.IsEmpty); Assert.True(skipped5.Count == 0); } [Fact] public void MapTest() { var arr = 1.Cons(2.Cons(3.Cons(4.Cons(5.Cons())))); var seq1 = toSeq(arr); var seq2 = seq1.Map(x => x * 2); var seq3 = seq1.Select(x => x * 2); var seq4 = from x in seq1 select x * 2; var expected = Seq(2, 4, 6, 8, 10); Assert.True(expected == seq2); Assert.True(expected == seq3); Assert.True(expected == seq4); } [Fact] public void FilterTest() { var arr = 1.Cons(2.Cons(3.Cons(4.Cons(5.Cons())))); var seq1 = toSeq(arr); var seq2 = seq1.Filter(x => x % 2 == 0); var seq3 = seq1.Where(x => x % 2 == 0); var seq4 = from x in seq1 where x % 2 == 0 select x; var expected = Seq(2, 4); Assert.True(expected == seq2); Assert.True(expected == seq3); Assert.True(expected == seq4); } [Fact] public void BindTest() { var seq1 = 10.Cons(100.Cons()); var seq2 = 1.Cons(2.Cons(3.Cons(4.Cons(5.Cons())))); var seq3 = seq1.Bind(x => seq2.Map(y => x * y)); var expected = Seq(10, 20, 30, 40, 50, 100, 200, 300, 400, 500); Assert.True(seq3 == expected); } [Fact] public void FoldTest1() { var seq = 1.Cons(2.Cons(3.Cons(4.Cons(5.Cons())))); var res1 = seq.Fold(1, (s, x) => s * x); var res2 = seq.FoldBack(1, (s, x) => s * x); Assert.True(res1 == 120); Assert.True(res2 == 120); } [Fact] public void FoldTest2() { var seq = "a".Cons("b".Cons("c".Cons("d".Cons("e".Cons())))); var res1 = seq.Fold("", (s, x) => s + x); var res2 = seq.FoldBack("", (s, x) => s + x); Assert.True(res1 == "abcde"); Assert.True(res2 == "edcba"); } [Fact] public void Existential() { var Seq = 'a'.Cons('b'.Cons('c'.Cons('d'.Cons('e'.Cons())))); var seq2 = 'a'.Cons('b'.Cons('c'.Cons('_'.Cons('e'.Cons())))); var ex1 = Seq.Exists(x => x == 'd'); var ex2 = seq2.Exists(x => x == 'd'); var fa1 = Seq.ForAll(Char.IsLetter); var fa2 = seq2.ForAll(Char.IsLetter); Assert.True(ex1); Assert.False(ex2); Assert.True(fa1); Assert.False(fa2); } } } ================================================ FILE: LanguageExt.Tests/SeqTypes/Seq.Enumerable.Tests.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using Xunit; using static LanguageExt.Prelude; namespace LanguageExt.Tests { public class SeqEnumerableTests { IEnumerable EmptyList = new int[0]; IEnumerable OneItem = new int[] { 1 }; IEnumerable FiveItems = new int[] { 1, 2, 3, 4, 5 }; IEnumerable TenHundred = new int[] { 10, 100 }; IEnumerable DoubleFiveItems = new int[] { 2, 4, 6, 8, 10 }; IEnumerable EvenItems = new int[] { 2, 4 }; IEnumerable BoundItems = new int[] { 10, 20, 30, 40, 50, 100, 200, 300, 400, 500 }; IEnumerable abcdeChars = new char[] { 'a', 'b', 'c', 'd', 'e' }; IEnumerable abc_eChars = new char[] { 'a', 'b', 'c', '_', 'e' }; IEnumerable edcbaChars = new char[] { 'e', 'd', 'c', 'b', 'a' }; IEnumerable abcdeStrs = new string[] { "a", "b", "c", "d", "e" }; IEnumerable edcbaStrs = new string[] { "e", "d", "c", "b", "a" }; [Fact] public void TestEmpty() { var arr = EmptyList; var seq = toSeq(arr); Assert.True(seq.IsEmpty); Assert.True(seq.Tail.IsEmpty); Assert.True(seq.Tail.Tail.IsEmpty); Assert.True(seq.Head.IsNone); Assert.True(seq.Count == 0); Assert.True(seq.Count() == 0); var res1 = seq.Match( () => true, (x, xs) => false); var res2 = seq.Match( () => true, x => false, (x, xs) => false); Assert.True(res1); Assert.True(res2); var skipped = seq.Skip(1); Assert.True(skipped.IsEmpty); Assert.True(skipped.Count == 0); Assert.True(skipped.Count() == 0); Assert.True(skipped.Head.IsNone); } [Fact] public void TestOne() { var arr = OneItem; var seq = toSeq(arr); Assert.True(seq.Head == 1); Assert.True(seq.Tail.IsEmpty); Assert.True(seq.Tail.Tail.IsEmpty); Assert.True(seq.Count == 1); Assert.True(seq.Count() == 1); var res1 = seq.Match( () => false, (x, xs) => x == 1 && xs.IsEmpty); var res2 = seq.Match( () => false, x => x == 1, (x, xs) => false); Assert.True(res1); Assert.True(res2); var skipped = seq.Skip(1); Assert.True(skipped.IsEmpty); Assert.True(skipped.Count == 0); Assert.True(skipped.Count() == 0); Assert.True(skipped.Head.IsNone); } static int Sum(Seq seq) => seq.Match( () => 0, x => x, (x, xs) => x + Sum(xs)); [Fact] public void TestMore() { var arr = FiveItems; var seq = toSeq(arr); Assert.True(seq.Head == 1); Assert.True(seq.Tail.Head == 2); Assert.True(seq.Tail.Tail.Head == 3); Assert.True(seq.Tail.Tail.Tail.Head == 4); Assert.True(seq.Tail.Tail.Tail.Tail.Head == 5); Assert.True(seq.Tail.Tail.Tail.Tail.Tail.IsEmpty); Assert.True(seq.Count == 5); Assert.True(seq.Count() == 5); Assert.True(seq.Tail.Count == 4); Assert.True(seq.Tail.Count() == 4); Assert.True(seq.Tail.Tail.Count == 3); Assert.True(seq.Tail.Tail.Count() == 3); Assert.True(seq.Tail.Tail.Tail.Count == 2); Assert.True(seq.Tail.Tail.Tail.Count() == 2); Assert.True(seq.Tail.Tail.Tail.Tail.Count == 1); Assert.True(seq.Tail.Tail.Tail.Tail.Count() == 1); var res = Sum(seq); Assert.True(res == 15); var skipped1 = seq.Skip(1); Assert.True(skipped1.Head == 2); Assert.True(skipped1.Count == 4); Assert.True(skipped1.Count() == 4); var skipped2 = seq.Skip(2); Assert.True(skipped2.Head == 3); Assert.True(skipped2.Count == 3); Assert.True(skipped2.Count() == 3); var skipped3 = seq.Skip(3); Assert.True(skipped3.Head == 4); Assert.True(skipped3.Count == 2); Assert.True(skipped3.Count() == 2); var skipped4 = seq.Skip(4); Assert.True(skipped4.Head == 5); Assert.True(skipped4.Count == 1); Assert.True(skipped4.Count() == 1); var skipped5 = seq.Skip(5); Assert.True(skipped5.IsEmpty); Assert.True(skipped5.Count == 0); Assert.True(skipped5.Count() == 0); } [Fact] public void MapTest() { var arr = FiveItems; var seq1 = toSeq(arr); var seq2 = seq1.Map(x => x * 2); var seq3 = seq1.Select(x => x * 2); var seq4 = from x in seq1 select x * 2; var expected = toSeq(DoubleFiveItems); Assert.True(expected == seq2); Assert.True(expected == seq3); Assert.True(expected == seq4); } [Fact] public void FilterTest() { var arr = FiveItems; var seq1 = toSeq(arr); var seq2 = seq1.Filter(x => x % 2 == 0); var seq3 = seq1.Where(x => x % 2 == 0); var seq4 = from x in seq1 where x % 2 == 0 select x; var expected = toSeq(EvenItems); Assert.True(expected == seq2); Assert.True(expected == seq3); Assert.True(expected == seq4); } [Fact] public void BindTest() { var seq1 = toSeq(TenHundred); var seq2 = toSeq(FiveItems); var seq3 = seq1.Bind(x => seq2.Map(y => x * y)); var expected = toSeq(BoundItems); Assert.True(seq3 == expected); } [Fact] public void FoldTest1() { var seq = toSeq(FiveItems); var res1 = seq.Fold(1, (s, x) => s * x); var res2 = seq.FoldBack(1, (s, x) => s * x); Assert.True(res1 == 120); Assert.True(res2 == 120); } [Fact] public void FoldTest2() { var seq = toSeq(abcdeStrs); var res1 = seq.Fold("", (s, x) => s + x); var res2 = seq.FoldBack("", (s, x) => s + x); Assert.True(res1 == "abcde"); Assert.True(res2 == "edcba"); } [Fact] public void Existential() { var Seq = toSeq(abcdeChars); var seq2 = toSeq(abc_eChars); var ex1 = Seq.Exists(x => x == 'd'); var ex2 = seq2.Exists(x => x == 'd'); var fa1 = Seq.ForAll(Char.IsLetter); var fa2 = seq2.ForAll(Char.IsLetter); Assert.True(ex1); Assert.False(ex2); Assert.True(fa1); Assert.False(fa2); } [Fact] public void TestQueryableCount() => Assert.True(Seq(1, 2, 3).AsQueryable().Count() == 3); } } ================================================ FILE: LanguageExt.Tests/SeqTypes/Seq.IList.Tests.cs ================================================ using System; using System.Collections.Generic; using Xunit; using static LanguageExt.Prelude; namespace LanguageExt.Tests { public class SeqIListTests { [Fact] public void TestEmpty() { var arr = toSeq(new List()); var seq = toSeq(arr); Assert.True(seq.IsEmpty); Assert.True(seq.Tail.IsEmpty); Assert.True(seq.Tail.Tail.IsEmpty); Assert.True(seq.Head.IsNone); Assert.True(seq.Count == 0); var res1 = seq.Match( () => true, (x, xs) => false); var res2 = seq.Match( () => true, x => false, (x, xs) => false); Assert.True(res1); Assert.True(res2); var skipped = seq.Skip(1); Assert.True(skipped.IsEmpty); Assert.True(skipped.Count == 0); Assert.True(skipped.Head.IsNone); } [Fact] public void TestOne() { var arr = toSeq(new List() { 1 }); var seq = toSeq(arr); Assert.True(seq.Head == 1); Assert.True(seq.Tail.IsEmpty); Assert.True(seq.Tail.Tail.IsEmpty); Assert.True(seq.Count == 1); var res1 = seq.Match( () => false, (x, xs) => x == 1 && xs.IsEmpty); var res2 = seq.Match( () => false, x => x == 1, (x, xs) => false); Assert.True(res1); Assert.True(res2); var skipped = seq.Skip(1); Assert.True(skipped.IsEmpty); Assert.True(skipped.Count == 0); Assert.True(skipped.Head.IsNone); } static int Sum(Seq seq) => seq.Match( () => 0, x => x, (x, xs) => x + Sum(xs)); [Fact] public void TestMore() { var arr = toSeq(new List() { 1, 2, 3, 4, 5 }); var seq = toSeq(arr); Assert.True(seq.Head == 1); Assert.True(seq.Tail.Head == 2); Assert.True(seq.Tail.Tail.Head == 3); Assert.True(seq.Tail.Tail.Tail.Head == 4); Assert.True(seq.Tail.Tail.Tail.Tail.Head == 5); Assert.True(seq.Tail.Tail.Tail.Tail.Tail.IsEmpty); Assert.True(seq.Count == 5); Assert.True(seq.Tail.Count == 4); Assert.True(seq.Tail.Tail.Count == 3); Assert.True(seq.Tail.Tail.Tail.Count == 2); Assert.True(seq.Tail.Tail.Tail.Tail.Count == 1); var res = Sum(seq); Assert.True(res == 15); var skipped1 = seq.Skip(1); Assert.True(skipped1.Head == 2); Assert.True(skipped1.Count == 4); var skipped2 = seq.Skip(2); Assert.True(skipped2.Head == 3); Assert.True(skipped2.Count == 3); var skipped3 = seq.Skip(3); Assert.True(skipped3.Head == 4); Assert.True(skipped3.Count == 2); var skipped4 = seq.Skip(4); Assert.True(skipped4.Head == 5); Assert.True(skipped4.Count == 1); var skipped5 = seq.Skip(5); Assert.True(skipped5.IsEmpty); Assert.True(skipped5.Count == 0); } [Fact] public void MapTest() { var arr = toSeq(new List() { 1, 2, 3, 4, 5 }); var seq1 = toSeq(arr); var seq2 = seq1.Map(x => x * 2); var seq3 = seq1.Select(x => x * 2); var seq4 = from x in seq1 select x * 2; var expected = toSeq(List.create(2, 4, 6, 8, 10)); Assert.True(expected == seq2); Assert.True(expected == seq3); Assert.True(expected == seq4); } [Fact] public void FilterTest() { var arr = toSeq(new List() { 1, 2, 3, 4, 5 }); var seq1 = toSeq(arr); var seq2 = seq1.Filter(x => x % 2 == 0); var seq3 = seq1.Where(x => x % 2 == 0); var seq4 = from x in seq1 where x % 2 == 0 select x; var expected = toSeq(List.create(2, 4)); Assert.True(expected == seq2); Assert.True(expected == seq3); Assert.True(expected == seq4); } [Fact] public void BindTest() { var seq1 = toSeq(new List() { 10, 100 }); var seq2 = toSeq(new List() { 1, 2, 3, 4, 5 }); var seq3 = seq1.Bind(x => seq2.Map(y => x * y)); var expected = Seq(10, 20, 30, 40, 50, 100, 200, 300, 400, 500); Assert.True(seq3 == expected); } [Fact] public void FoldTest1() { var seq = toSeq(new List() { 1, 2, 3, 4, 5 }); var res1 = seq.Fold(1, (s, x) => s * x); var res2 = seq.FoldBack(1, (s, x) => s * x); Assert.True(res1 == 120); Assert.True(res2 == 120); } [Fact] public void FoldTest2() { var seq = toSeq(new List() { "a", "b", "c", "d", "e" }); var res1 = seq.Fold("", (s, x) => s + x); var res2 = seq.FoldBack("", (s, x) => s + x); Assert.True(res1 == "abcde"); Assert.True(res2 == "edcba"); } [Fact] public void Existential() { var Seq = toSeq(new List() { 'a', 'b', 'c', 'd', 'e' }); var seq2 = toSeq(new List() { 'a', 'b', 'c', '_', 'e' }); var ex1 = Seq.Exists(x => x == 'd'); var ex2 = seq2.Exists(x => x == 'd'); var fa1 = Seq.ForAll(Char.IsLetter); var fa2 = seq2.ForAll(Char.IsLetter); Assert.True(ex1); Assert.False(ex2); Assert.True(fa1); Assert.False(fa2); } } } ================================================ FILE: LanguageExt.Tests/SeqTypes/Seq.Lazy.Tests.cs ================================================ using System; using Xunit; namespace LanguageExt.Tests.SeqTypes; public class Seq_Lazy_Tests { [Theory] [InlineData(0)] [InlineData(1)] [InlineData(2)] [InlineData(3)] [InlineData(5)] [InlineData(8)] [InlineData(13)] [InlineData(21)] [InlineData(34)] [InlineData(55)] [InlineData(89)] [InlineData(99)] public void BeginIndexTests(int index) { Index ix = index; var seq = toSeq(Range(0, 100)); var value = seq[ix]; Assert.True(value == index); } [Theory] [InlineData(0)] [InlineData(1)] [InlineData(2)] [InlineData(3)] [InlineData(5)] [InlineData(8)] [InlineData(13)] [InlineData(21)] [InlineData(34)] [InlineData(55)] [InlineData(89)] [InlineData(99)] public void EndIndexTests(int index) { index++; var ix = Index.FromEnd(index); var seq = toSeq(Range(0, 100)); var value = seq[ix]; Assert.True(value == 100 - index); } [Theory] [InlineData(0)] [InlineData(1)] [InlineData(2)] [InlineData(3)] [InlineData(5)] [InlineData(8)] [InlineData(13)] [InlineData(21)] [InlineData(34)] [InlineData(55)] [InlineData(89)] [InlineData(99)] public void PreConcatBeginIndexTests(int index) { Index ix = index; var pre = toSeq(Range(0, 50)); var seq = pre + toSeq(Range(50, 50)); var value = seq[ix]; Assert.True(value == index); } [Theory] [InlineData(0)] [InlineData(1)] [InlineData(2)] [InlineData(3)] [InlineData(5)] [InlineData(8)] [InlineData(13)] [InlineData(21)] [InlineData(34)] [InlineData(55)] [InlineData(89)] [InlineData(99)] public void PreConcatEndIndexTests(int index) { index++; var ix = Index.FromEnd(index); var pre = toSeq(Range(0, 50)); var seq = pre + toSeq(Range(50, 50)); var value = seq[ix]; Assert.True(value == 100 - index); } [Theory] [InlineData(0)] [InlineData(1)] [InlineData(2)] [InlineData(3)] [InlineData(5)] [InlineData(8)] [InlineData(13)] [InlineData(21)] [InlineData(34)] [InlineData(55)] [InlineData(89)] [InlineData(99)] public void PostConcatBeginIndexTests(int index) { Index ix = index; var post = toSeq(Range(50, 50)); var seq = toSeq(Range(0, 50)) + post; var value = seq[ix]; Assert.True(value == index); } [Theory] [InlineData(0)] [InlineData(1)] [InlineData(2)] [InlineData(3)] [InlineData(5)] [InlineData(8)] [InlineData(13)] [InlineData(21)] [InlineData(34)] [InlineData(55)] [InlineData(89)] [InlineData(99)] public void PostConcatEndIndexTests(int index) { index++; var ix = Index.FromEnd(index); var post = toSeq(Range(50, 50)); var seq = toSeq(Range(0, 50)) + post; var value = seq[ix]; Assert.True(value == 100 - index); } [Theory] [InlineData(0, 50)] [InlineData(1, 99)] [InlineData(2, 89)] [InlineData(3, 55)] [InlineData(5, 34)] [InlineData(8, 21)] [InlineData(13, 13)] [InlineData(21, 8)] [InlineData(34, 5)] [InlineData(55, 3)] [InlineData(89, 2)] [InlineData(99, 1)] public void MemoisedBeginIndexTests(int index, int load) { Index ix = index; var seq = toSeq(Range(0, 100)); Enum(seq, load); var value = seq[ix]; Assert.True(value == index); } [Theory] [InlineData(0, 50)] [InlineData(1, 99)] [InlineData(2, 89)] [InlineData(3, 55)] [InlineData(5, 34)] [InlineData(8, 21)] [InlineData(13, 13)] [InlineData(21, 8)] [InlineData(34, 5)] [InlineData(55, 3)] [InlineData(89, 2)] [InlineData(99, 1)] public void MemoisedEndIndexTests(int index, int load) { index++; var ix = Index.FromEnd(index); var seq = toSeq(Range(0, 100)); Enum(seq, load); var value = seq[ix]; Assert.True(value == 100 - index); } [Theory] [InlineData(0, 50)] [InlineData(1, 99)] [InlineData(2, 89)] [InlineData(3, 55)] [InlineData(5, 34)] [InlineData(8, 21)] [InlineData(13, 13)] [InlineData(21, 8)] [InlineData(34, 5)] [InlineData(55, 3)] [InlineData(89, 2)] [InlineData(99, 1)] public void PreConcatMemoisedBeginIndexTests(int index, int load) { Index ix = index; var pre = toSeq(Range(0, 50)); var seq = pre + toSeq(Range(50, 50)); Enum(seq, load); var value = seq[ix]; Assert.True(value == index); } [Theory] [InlineData(0, 50)] [InlineData(1, 99)] [InlineData(2, 89)] [InlineData(3, 55)] [InlineData(5, 34)] [InlineData(8, 21)] [InlineData(13, 13)] [InlineData(21, 8)] [InlineData(34, 5)] [InlineData(55, 3)] [InlineData(89, 2)] [InlineData(99, 1)] public void PreConcatMemoisedEndIndexTests(int index, int load) { index++; var ix = Index.FromEnd(index); var pre = toSeq(Range(0, 50)); var seq = pre + toSeq(Range(50, 50)); Enum(seq, load); var value = seq[ix]; Assert.True(value == 100 - index); } [Theory] [InlineData(0, 50)] [InlineData(1, 99)] [InlineData(2, 89)] [InlineData(3, 55)] [InlineData(5, 34)] [InlineData(8, 21)] [InlineData(13, 13)] [InlineData(21, 8)] [InlineData(34, 5)] [InlineData(55, 3)] [InlineData(89, 2)] [InlineData(99, 1)] public void PostConcatMemoisedBeginIndexTests(int index, int load) { Index ix = index; var post = toSeq(Range(50, 50)); var seq = toSeq(Range(0, 50)) + post; Enum(seq, load); var value = seq[ix]; Assert.True(value == index); } [Theory] [InlineData(0, 50)] [InlineData(1, 99)] [InlineData(2, 89)] [InlineData(3, 55)] [InlineData(5, 34)] [InlineData(8, 21)] [InlineData(13, 13)] [InlineData(21, 8)] [InlineData(34, 5)] [InlineData(55, 3)] [InlineData(89, 2)] [InlineData(99, 1)] public void PostConcatMemoisedEndIndexTests(int index, int load) { index++; var ix = Index.FromEnd(index); var post = toSeq(Range(50, 50)); var seq = toSeq(Range(0, 50)) + post; Enum(seq, load); var value = seq[ix]; Assert.True(value == 100 - index); } static void Enum(Seq seq, int amount) { foreach(var x in seq) { amount--; if (amount == 0) return; } } } ================================================ FILE: LanguageExt.Tests/SeqTypes/Seq.Lst.Tests.cs ================================================ using System; using Xunit; using static LanguageExt.Prelude; namespace LanguageExt.Tests { public class SeqLstTests { [Fact] public void TestEmpty() { var arr = List.empty(); var seq = toSeq(arr); Assert.True(seq.IsEmpty); Assert.True(seq.Tail.IsEmpty); Assert.True(seq.Tail.Tail.IsEmpty); Assert.True(seq.Head.IsNone); Assert.True(seq.Count == 0); var res1 = seq.Match( () => true, (x, xs) => false); var res2 = seq.Match( () => true, x => false, (x, xs) => false); Assert.True(res1); Assert.True(res2); var skipped = seq.Skip(1); Assert.True(skipped.IsEmpty); Assert.True(skipped.Count == 0); Assert.True(skipped.Head.IsNone); } [Fact] public void TestOne() { var arr = List.create(1); var seq = toSeq(arr); Assert.True(seq.Head == 1); Assert.True(seq.Tail.IsEmpty); Assert.True(seq.Tail.Tail.IsEmpty); Assert.True(seq.Count == 1); var res1 = seq.Match( () => false, (x, xs) => x == 1 && xs.IsEmpty); var res2 = seq.Match( () => false, x => x == 1, (x, xs) => false); Assert.True(res1); Assert.True(res2); var skipped = seq.Skip(1); Assert.True(skipped.IsEmpty); Assert.True(skipped.Count == 0); Assert.True(skipped.Head.IsNone); } static int Sum(Seq seq) => seq.Match( () => 0, x => x, (x, xs) => x + Sum(xs)); [Fact] public void TestMore() { var arr = List.create(1, 2, 3, 4, 5); var seq = toSeq(arr); Assert.True(seq.Head == 1); Assert.True(seq.Tail.Head == 2); Assert.True(seq.Tail.Tail.Head == 3); Assert.True(seq.Tail.Tail.Tail.Head == 4); Assert.True(seq.Tail.Tail.Tail.Tail.Head == 5); Assert.True(seq.Tail.Tail.Tail.Tail.Tail.IsEmpty); Assert.True(seq.Count == 5); Assert.True(seq.Tail.Count == 4); Assert.True(seq.Tail.Tail.Count == 3); Assert.True(seq.Tail.Tail.Tail.Count == 2); Assert.True(seq.Tail.Tail.Tail.Tail.Count == 1); var res = Sum(seq); Assert.True(res == 15); var skipped1 = seq.Skip(1); Assert.True(skipped1.Head == 2); Assert.True(skipped1.Count == 4); var skipped2 = seq.Skip(2); Assert.True(skipped2.Head == 3); Assert.True(skipped2.Count == 3); var skipped3 = seq.Skip(3); Assert.True(skipped3.Head == 4); Assert.True(skipped3.Count == 2); var skipped4 = seq.Skip(4); Assert.True(skipped4.Head == 5); Assert.True(skipped4.Count == 1); var skipped5 = seq.Skip(5); Assert.True(skipped5.IsEmpty); Assert.True(skipped5.Count == 0); } [Fact] public void MapTest() { var arr = List.create(1, 2, 3, 4, 5); var Seq = toSeq(arr); var seq2 = Seq.Map(x => x * 2); var seq3 = Seq.Select(x => x * 2); var seq4 = from x in Seq select x * 2; var expected = toSeq(List.create(2, 4, 6, 8, 10)); Assert.True(expected == seq2); Assert.True(expected == seq3); Assert.True(expected == seq4); } [Fact] public void FilterTest() { var arr = List.create(1, 2, 3, 4, 5); var Seq = toSeq(arr); var seq2 = Seq.Filter(x => x % 2 == 0); var seq3 = Seq.Where(x => x % 2 == 0); var seq4 = from x in Seq where x % 2 == 0 select x; var expected = toSeq(List.create(2, 4)); Assert.True(expected == seq2); Assert.True(expected == seq3); Assert.True(expected == seq4); } [Fact] public void BindTest() { var seq1 = toSeq(List.create(10, 100)); var seq2 = toSeq(List.create(1, 2, 3, 4, 5)); var seq3 = seq1.Bind(x => seq2.Map(y => x * y)); var expected = Seq(10, 20, 30, 40, 50, 100, 200, 300, 400, 500); Assert.True(seq3 == expected); } [Fact] public void FoldTest1() { var seq = toSeq(List.create(1, 2, 3, 4, 5)); var res1 = seq.Fold(1, (s, x) => s * x); var res2 = seq.FoldBack(1, (s, x) => s * x); Assert.True(res1 == 120); Assert.True(res2 == 120); } [Fact] public void FoldTest2() { var seq = toSeq(List.create("a", "b", "c", "d", "e")); var res1 = seq.Fold("", (s, x) => s + x); var res2 = seq.FoldBack("", (s, x) => s + x); Assert.True(res1 == "abcde"); Assert.True(res2 == "edcba"); } [Fact] public void Existential() { var Seq = toSeq(List.create('a', 'b', 'c', 'd', 'e')); var seq2 = toSeq(List.create('a', 'b', 'c', '_', 'e')); var ex1 = Seq.Exists(x => x == 'd'); var ex2 = seq2.Exists(x => x == 'd'); var fa1 = Seq.ForAll(Char.IsLetter); var fa2 = seq2.ForAll(Char.IsLetter); Assert.True(ex1); Assert.False(ex2); Assert.True(fa1); Assert.False(fa2); } } } ================================================ FILE: LanguageExt.Tests/SeqTypes/Seq.Module.Tests.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Xunit; namespace LanguageExt.Tests; public class SeqModuleTests { [Fact] public void EmptyTest() { Assert.True(Seq.empty().IsEmpty); } [Fact] public void CreateTest() { Assert.True(Seq.create().IsEmpty); var seq = Seq.create(1, 2, 3, 4, 5); Assert.True(seq.Head == 1); Assert.True(seq.Tail.Head == 2); Assert.True(seq.Tail.Tail.Head == 3); Assert.True(seq.Tail.Tail.Tail.Head == 4); Assert.True(seq.Tail.Tail.Tail.Tail.Head == 5); var seqr = Seq.createRange(new[] { 1, 2, 3, 4, 5 }); Assert.True(seqr.Head == 1); Assert.True(seqr.Tail.Head == 2); Assert.True(seqr.Tail.Tail.Head == 3); Assert.True(seqr.Tail.Tail.Tail.Head == 4); Assert.True(seqr.Tail.Tail.Tail.Tail.Head == 5); } [Fact] public void InitTest() { int counter = 0; Func run = x => { counter++; return x; }; var seq = Seq.generate(5, x => run((x + 1) * 2)); var fst = seq.Take(1).Head; Assert.True(counter == 1); Assert.True(fst == 2); var snd = seq.Skip(1).Take(1).Head; Assert.True(counter == 2); Assert.True(snd == 4); var thd = seq.Skip(2).Take(1).Head; Assert.True(counter == 3); Assert.True(thd == 6); var fth = seq.Skip(3).Take(1).Head; Assert.True(counter == 4); Assert.True(fth == 8); var fit = seq.Skip(4).Take(1).Head; Assert.True(counter == 5); Assert.True(fit == 10); var sum = seq.Sum(); Assert.True(counter == 5); Assert.True(sum == 30); } [Fact] public void EquivalentOfTheInitTestWithIEnumerable() { int counter = 0; Func run = x => { counter++; return x; }; var seq = List.generate(5, x => run((x + 1) * 2)); var fst = seq.Take(1).First(); Assert.True(counter == 1); Assert.True(fst == 2); var snd = seq.Skip(1).Take(1).First(); // Assert.True(counter == 2); equals 3 by this point Assert.True(snd == 4); var thd = seq.Skip(2).Take(1).First(); // Assert.True(counter == 3); equals 6 by this point Assert.True(thd == 6); var fth = seq.Skip(3).Take(1).First(); // Assert.True(counter == 4); equals 10 by this point (double what the InitTest needs!) Assert.True(fth == 8); var fit = seq.Skip(4).Take(1).First(); //Assert.True(counter == 5); equals 15 by this point (treble what the InitTest needs!) Assert.True(fit == 10); var sum = seq.Sum(); // Assert.True(counter == 5); equals 20 by this point(four times what the InitTest needs!!!) Assert.True(sum == 30); } [Fact] public void TailsTestIterative() { var seq = Seq(1, 2, 3, 4, 5); var seqs = Seq.tails(seq); Assert.True(seqs.Head == Seq(1, 2, 3, 4, 5)); Assert.True(seqs.Tail.Head == Seq(2, 3, 4, 5)); Assert.True(seqs.Tail.Tail.Head == Seq(3, 4, 5)); Assert.True(seqs.Tail.Tail.Tail.Head == Seq(4, 5)); Assert.True(seqs.Tail.Tail.Tail.Tail.Head == Seq.create(5)); Assert.True(seqs.Tail.Tail.Tail.Tail.Tail.IsEmpty); } [Fact] public void TailsTestRecursive() { var seq = Seq(1, 2, 3, 4, 5); var seqs = Seq.tailsr(seq); Assert.True(seqs.Head == Seq(1, 2, 3, 4, 5)); Assert.True(seqs.Tail.Head == Seq(2, 3, 4, 5)); Assert.True(seqs.Tail.Tail.Head == Seq(3, 4, 5)); Assert.True(seqs.Tail.Tail.Tail.Head == Seq(4, 5)); Assert.True(seqs.Tail.Tail.Tail.Tail.Head == Seq.create(5)); Assert.True(seqs.Tail.Tail.Tail.Tail.Tail.IsEmpty); } [Fact] public void CountTests() { int counter = 0; Func run = x => { counter++; return x; }; var seq1 = Seq.generate(5, x => run((x + 1) * 2)); var seq2 = 1.Cons(seq1); var cnt1 = seq1.Count; var cnt2 = seq2.Count; Assert.True(counter == 5); Assert.True(cnt1 == 5); Assert.True(cnt2 == 6); var seq3 = Seq.generate(5, x => run((x + 1) * 2)); var seq4 = 1.Cons(seq3); var cnt3 = seq4.Count; var cnt4 = seq3.Count; Assert.True(counter == 10); Assert.True(cnt4 == 5); Assert.True(cnt3 == 6); } /// /// Runs 1000 tasks that sums the same lazy Seq to /// make sure we get the same result as a synchronous /// sum. /// [Fact] public async Task ParallelTests() { var sum = Range(1, 10000).Sum(); var seq = toSeq(Range(1, 10000)); var tasks = new List>(); foreach(var x in Range(1, 1000)) { tasks.Add(Task.Run(() => seq.Sum())); } await Task.WhenAll(tasks.ToArray()); var results = tasks.Select(t => t.Result).ToArray(); seq.Iter((i, x) => Assert.True(x != 0, $"Invalid value in the sequence at index {i}")); foreach (var task in tasks) { Assert.True(task.Result == sum, $"Result is {task.Result}, should be: {sum}"); } } } ================================================ FILE: LanguageExt.Tests/SeqTypes/SeqListTests.cs ================================================ using System.Collections.Generic; using Xunit; using static LanguageExt.Prelude; namespace LanguageExt.Tests.SeqTypes { public class SeqListTests { [Fact] public void Take_ZeroFromNonempty_Empty() { var seq = toSeq(new List { 0 }); var actual = seq.Take(0); Assert.Equal(actual, SeqEmpty.Default); } [Fact] public void Take_NegativeFromNonempty_Empty() { var seq = toSeq(new List { 0 }); var actual = seq.Take(-1); Assert.Equal(actual, SeqEmpty.Default); } [Fact] public void Skip_NegativeFromNonempty_Unchanged() { var expected = toSeq(new List { 0 }); var actual = expected.Skip(-1); Assert.Equal(actual, expected); } } } ================================================ FILE: LanguageExt.Tests/SeqTypes/SeqTests.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using static LanguageExt.Seq; using Xunit; namespace LanguageExt.Tests; public class SeqTests { [Fact] public void ObjectExists() { var x = "test"; Assert.True(Seq(x).Count() == 1); Assert.True(Seq(x).Head == x); } [Fact] public void ObjectNull() { string? x = null; Assert.Equal(0, toSeq(x).Count()); } [Fact] public void EnumerableExists() { var x = new[] { "a", "b", "c" }; var y = toSeq(x); var res = toSeq(x).Tail().AsEnumerable().ToList(); Assert.True(toSeq(x).Count == 3); Assert.True(toSeq(x).Head == "a"); Assert.True(toSeq(x).Tail.Head == "b"); Assert.True(toSeq(x).Tail.Tail.Head == "c"); } [Fact] public void EnumerableNull() { string[]? x = null; Assert.True(toSeq(x).Count == 0); } [Fact] public void TakeTest() { IEnumerable Numbers() { for (int i = 0; i < 100; i++) { yield return i; } } var seq = Numbers().AsIterable().ToSeq(); var a = seq.Take(5).Sum(); Assert.True(a == 10); var b = seq.Skip(5).Take(5).Sum(); Assert.True(b == 35); } [Fact] public void FoldTest() { var input = Seq(1, 2, 3, 4, 5); var output1 = Foldable.fold((s, x) => s + x, "", input); Assert.Equal("12345", output1); } [Fact] public void FoldBackTest() { var input = Seq(1, 2, 3, 4, 5); var output1 = Foldable.foldBack((s, x) => s + x, "", input); Assert.Equal("54321", output1); } [Fact] public void FoldWhileTest() { var input = Seq(10, 20, 30, 40, 50); var output1 = Foldable.foldWhile((s, x) => s + x, x => x.Value < 40, "", input); Assert.Equal("102030", output1); var output2 = Foldable.foldWhile((s, x) => s + x, s => s.State.Length < 6, "", input); Assert.Equal("102030", output2); var output3 = Foldable.foldWhile((s, x) => s + x, x => x.Value < 40, 0, input); Assert.Equal(60, output3); var output4 = Foldable.foldWhile((s, x) => s + x, s => s.State < 60, 0, input); Assert.Equal(60, output4); } [Fact] public void FoldBackWhileTest() { var input = Seq(10, 20, 30, 40, 50); var output1 = Foldable.foldBackWhile((s, x) => s + x, x => x.Value >= 40, "", input); Assert.Equal("5040", output1); var output2 = Foldable.foldBackWhile((s, x) => s + x, s => s.State.Length < 4, "", input); Assert.Equal("5040", output2); var output3 = Foldable.foldBackWhile((s, x) => s + x, x => x.Value >= 40, 0, input); Assert.Equal(90, output3); var output4 = Foldable.foldBackWhile((s, x) => s + x, s => s.State < 90, 0, input); Assert.Equal(90, output4); } [Fact] public void FoldUntilTest() { var input = Seq(10, 20, 30, 40, 50); var output1 = Foldable.foldUntil((s, x) => s + x, x => x.Value >= 40, "", input); Assert.Equal("102030", output1); var output2 = Foldable.foldUntil((s, x) => s + x, s => s.State.Length >= 6, "", input); Assert.Equal("102030", output2); var output3 = Foldable.foldUntil((s, x) => s + x, x => x.Value >= 40, 0, input); Assert.Equal(60, output3); var output4 = Foldable.foldUntil((s, x) => s + x, s => s.State >= 60, 0, input); Assert.Equal(60, output4); } [Fact] public void FoldBackUntilTest() { var input = Seq(10, 20, 30, 40, 50); var output1 = Foldable.foldBackUntil((s, x) => s + x, x => x.Value < 40, "", input); Assert.Equal("5040", output1); var output2 = Foldable.foldBackUntil((s, x) => s + x, s => s.State.Length >= 4, "", input); Assert.Equal("5040", output2); var output3 = Foldable.foldBackUntil((s, x) => s + x, x => x.Value < 40, 0, input); Assert.Equal(90, output3); var output4 = Foldable.foldBackUntil((s, x) => s + x, s => s.State >= 90, 0, input); Assert.Equal(90, output4); } [Fact] public void EqualityTest_BothNull() { Seq x = default; Seq y = default; var eq = x == y; Assert.True(eq); } [Fact] public void EqualityTest_LeftNull() { Seq x = default; Seq y = Seq(1, 2, 3); var eq = x == y; Assert.False(eq); } [Fact] public void EqualityTest_RightNull() { Seq x = Seq(1, 2, 3); Seq y = default; var eq = x == y; Assert.False(eq); } [Fact] public void EqualityTest() { Seq x = Seq(1, 2, 3); Seq y = Seq(1, 2, 3); var eq = x == y; Assert.True(eq); } /// /// Test ensures that lazily evaluated Seq returns the same as strictly evaluated when multiple enumerations occur /// [Fact] public void GetEnumeratorTest() { var res1 = empty(); var res2 = empty(); var a = new List { 1, 2, 3, 4 }.AsIterable(); var s1 = a.ToSeq(); var s2 = a.ToSeq().Strict(); foreach (var s in s1) { if (s == 2) { s1.FoldWhile("", (x, y) => "", x => x.Value != 3); } res1 = res1.Add(s); } foreach (var s in s2) { if (s == 2) { s2.FoldWhile("", (x, y) => "", x => x.Value != 3); } res2 = res2.Add(s); } var eq = res1 == res2; Assert.True(eq); } [Fact] public void AddTest() { var a = Seq("a"); var b = a.Add("b"); var c = a.Add("c"); Assert.Equal("a", string.Join("|", a)); Assert.Equal("a|b", string.Join("|", b)); Assert.Equal("a|c", string.Join("|", c)); } [Fact] public void ConsTest() { var a = Seq1("a"); var b = "b".Cons(a); var c = "c".Cons(a); Assert.Equal("a", string.Join("|", a)); Assert.Equal("b|a", string.Join("|", b)); Assert.Equal("c|a", string.Join("|", c)); } [Fact] public void InitStrictTest() { var sa = Seq(1, 2, 3, 4, 5); var sb = sa.Init; // [1,2,3,4] var sc = sb.Init; // [1,2,3] var sd = sc.Init; // [1,2] var se = sd.Init; // [1] var sf = se.Init; // [] Assert.True(sb == Seq(1, 2, 3, 4)); Assert.True(sc == Seq(1, 2, 3)); Assert.True(sd == Seq(1, 2)); Assert.True(se == Seq(1)); Assert.True(sf == Empty); } [Fact] public void InitLazyTest() { var sa = toSeq(Range(1, 5)); var sb = sa.Init; // [1,2,3,4] var sc = sb.Init; // [1,2,3] var sd = sc.Init; // [1,2] var se = sd.Init; // [1] var sf = se.Init; // [] Assert.True(sb == Seq(1, 2, 3, 4)); Assert.True(sc == Seq(1, 2, 3)); Assert.True(sd == Seq(1, 2)); Assert.True(se == Seq(1)); Assert.True(sf == Empty); } [Fact] public void InitConcatTest() { var sa = toSeq(Range(1, 2)) + toSeq(Range(3, 3)); var sb = sa.Init; // [1,2,3,4] var sc = sb.Init; // [1,2,3] var sd = sc.Init; // [1,2] var se = sd.Init; // [1] var sf = se.Init; // [] Assert.True(sb == Seq(1, 2, 3, 4)); Assert.True(sc == Seq(1, 2, 3)); Assert.True(sd == Seq(1, 2)); Assert.True(se == Seq(1)); Assert.True(sf == Empty); } [Fact] public void HashTest() { var s1 = Seq("test"); var s2 = Seq("test"); Assert.True(s1.GetHashCode() == s2.GetHashCode()); } [Fact] public void TakeWhileTest() { var str = "

The

".AsIterable(); Assert.Equal(" ", String.Join("", str.ToSeq().TakeWhile(ch => ch == ' '))); } [Fact] public void TakeWhileIndex() { var str = "

The

".AsIterable(); Assert.Equal(" ", String.Join("", str.ToSeq().TakeWhile((ch, index) => index != 26))); } [Fact] public void TakeWhile_HalfDefaultCapacityTest() { var str = "1234".AsIterable(); Assert.Equal("1234", String.Join("", str.ToSeq().TakeWhile(ch => true))); } [Fact] public void TakeWhileIndex_HalfDefaultCapacityTest() { var str = "1234".AsIterable(); Assert.Equal("1234", String.Join("", str.ToSeq().TakeWhile((ch, index) => true))); } [Fact] public void GeneratingZeroGivesEmptySequence() { var actual = generate(0, _ => unit); Assert.Equal(Seq(), actual); } [Fact] public void TakingOneAfterGeneratingZeroGivesEmptySequence() { var actual = generate(0, _ => unit).Take(1); Assert.Equal(Seq(), actual); } [Theory] [InlineData(0, 1)] [InlineData(1, 2)] [InlineData(2, 3)] [InlineData(3, 4)] [InlineData(4, 5)] [InlineData(5, 6)] [InlineData(6, 7)] [InlineData(7, 8)] [InlineData(8, 9)] [InlineData(9, 10)] [InlineData(10, 20)] [InlineData(100, 1000)] [InlineData(1000, 10000)] public void TakingNAfterGeneratingMoreThanNGivesLengthNSequence(int actualLength, int tryToTake) { var expect = toSeq(generate(actualLength, identity)).Strict(); var actual = generate(actualLength, identity).Take(tryToTake); Assert.Equal(expect, actual); } [Fact] public void SeqConcatTest() { var seq1 = (from x in new[] { 1 }.AsIterable() select x).ToSeq(); var seq2 = (from x in new[] { 2 }.AsIterable() select x).ToSeq(); var seq3 = (from x in new[] { 3 }.AsIterable() select x).ToSeq(); Assert.Equal( Seq(1, 2, 3), seq1 .Concat(seq2) .Concat(seq3)); } [Fact] public void Concat_strict_and_strict_then_index_test() { var s1 = Seq(1, 2, 3); var s2 = Seq(4, 5, 6); var s3 = s1.Concat(s2); Assert.Equal(1, s3[0]); Assert.Equal(2, s3[1]); Assert.Equal(3, s3[2]); Assert.Equal(4, s3[3]); Assert.Equal(5, s3[4]); Assert.Equal(6, s3[5]); } [Fact] public void Concat_lazy_and_strict_then_index_test_1() { var s1 = Range(1, 3).ToSeq(); var s2 = Seq(4, 5, 6); var s3 = s1.Concat(s2); Assert.Equal(1, s3[0]); Assert.Equal(2, s3[1]); Assert.Equal(3, s3[2]); Assert.Equal(4, s3[3]); Assert.Equal(5, s3[4]); Assert.Equal(6, s3[5]); } [Fact] public void Concat_lazy_and_strict_then_index_test_2() { var s1 = Range(1, 3).ToSeq(); var s2 = Seq(4, 5, 6); var s3 = s1.Concat(s2); Assert.Equal(6, s3[5]); Assert.Equal(5, s3[4]); Assert.Equal(4, s3[3]); Assert.Equal(3, s3[2]); Assert.Equal(2, s3[1]); Assert.Equal(1, s3[0]); } [Fact] public void Concat_lazy_and_lazy_then_index_test_1() { var s1 = Range(1, 3).ToSeq(); var s2 = Range(4, 3).ToSeq(); var s3 = s1.Concat(s2); Assert.Equal(1, s3[0]); Assert.Equal(2, s3[1]); Assert.Equal(3, s3[2]); Assert.Equal(4, s3[3]); Assert.Equal(5, s3[4]); Assert.Equal(6, s3[5]); } [Fact] public void Concat_lazy_and_lazy_then_index_test_2() { var s1 = Range(1, 3).ToSeq(); var s2 = Range(4, 3).ToSeq(); var s3 = s1.Concat(s2); Assert.Equal(6, s3[5]); Assert.Equal(5, s3[4]); Assert.Equal(4, s3[3]); Assert.Equal(3, s3[2]); Assert.Equal(2, s3[1]); Assert.Equal(1, s3[0]); } [Fact] public void CheckItems() { var xs = Seq(); Assert.True(xs.Count == 0); xs = Seq(0); Assert.True(xs.Count == 1); Assert.True(xs[0] == 0); xs = Seq(0, 1); Assert.True(xs.Count == 2); Assert.True(xs[0] == 0); Assert.True(xs[1] == 1); xs = Seq(0, 1, 2); Assert.True(xs.Count == 3); Assert.True(xs[0] == 0); Assert.True(xs[1] == 1); Assert.True(xs[2] == 2); xs = Seq(0, 1, 2, 3); Assert.True(xs.Count == 4); Assert.True(xs[0] == 0); Assert.True(xs[1] == 1); Assert.True(xs[2] == 2); Assert.True(xs[3] == 3); xs = Seq(0, 1, 2, 3, 4); Assert.True(xs.Count == 5); Assert.True(xs[0] == 0); Assert.True(xs[1] == 1); Assert.True(xs[2] == 2); Assert.True(xs[3] == 3); Assert.True(xs[4] == 4); xs = Seq(0, 1, 2, 3, 4, 5); Assert.True(xs.Count == 6); Assert.True(xs[0] == 0); Assert.True(xs[1] == 1); Assert.True(xs[2] == 2); Assert.True(xs[3] == 3); Assert.True(xs[4] == 4); Assert.True(xs[5] == 5); xs = Seq(0, 1, 2, 3, 4, 5, 6); Assert.True(xs.Count == 7); Assert.True(xs[0] == 0); Assert.True(xs[1] == 1); Assert.True(xs[2] == 2); Assert.True(xs[3] == 3); Assert.True(xs[4] == 4); Assert.True(xs[5] == 5); Assert.True(xs[6] == 6); xs = Seq(0, 1, 2, 3, 4, 5, 6, 7); Assert.True(xs.Count == 8); Assert.True(xs[0] == 0); Assert.True(xs[1] == 1); Assert.True(xs[2] == 2); Assert.True(xs[3] == 3); Assert.True(xs[4] == 4); Assert.True(xs[5] == 5); Assert.True(xs[6] == 6); Assert.True(xs[7] == 7); xs = Seq(0, 1, 2, 3, 4, 5, 6, 7, 8); Assert.True(xs.Count == 9); Assert.True(xs[0] == 0); Assert.True(xs[1] == 1); Assert.True(xs[2] == 2); Assert.True(xs[3] == 3); Assert.True(xs[4] == 4); Assert.True(xs[5] == 5); Assert.True(xs[6] == 6); Assert.True(xs[7] == 7); Assert.True(xs[8] == 8); xs = Seq(0, 1, 2, 3, 4, 5, 6, 7, 8, 9); Assert.True(xs.Count == 10); Assert.True(xs[0] == 0); Assert.True(xs[1] == 1); Assert.True(xs[2] == 2); Assert.True(xs[3] == 3); Assert.True(xs[4] == 4); Assert.True(xs[5] == 5); Assert.True(xs[6] == 6); Assert.True(xs[7] == 7); Assert.True(xs[8] == 8); Assert.True(xs[9] == 9); xs = Seq(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10); Assert.True(xs.Count == 11); Assert.True(xs[0] == 0); Assert.True(xs[1] == 1); Assert.True(xs[2] == 2); Assert.True(xs[3] == 3); Assert.True(xs[4] == 4); Assert.True(xs[5] == 5); Assert.True(xs[6] == 6); Assert.True(xs[7] == 7); Assert.True(xs[8] == 8); Assert.True(xs[9] == 9); Assert.True(xs[10] == 10); xs = Seq(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11); Assert.True(xs.Count == 12); Assert.True(xs[0] == 0); Assert.True(xs[1] == 1); Assert.True(xs[2] == 2); Assert.True(xs[3] == 3); Assert.True(xs[4] == 4); Assert.True(xs[5] == 5); Assert.True(xs[6] == 6); Assert.True(xs[7] == 7); Assert.True(xs[8] == 8); Assert.True(xs[9] == 9); Assert.True(xs[10] == 10); Assert.True(xs[11] == 11); xs = Seq(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12); Assert.True(xs.Count == 13); Assert.True(xs[0] == 0); Assert.True(xs[1] == 1); Assert.True(xs[2] == 2); Assert.True(xs[3] == 3); Assert.True(xs[4] == 4); Assert.True(xs[5] == 5); Assert.True(xs[6] == 6); Assert.True(xs[7] == 7); Assert.True(xs[8] == 8); Assert.True(xs[9] == 9); Assert.True(xs[10] == 10); Assert.True(xs[11] == 11); Assert.True(xs[12] == 12); } [Fact] void SeqHashCodeRegression() { // GetHashCode is internally used to compare Seq values => has to be equal irrespective of creation method var originalStrictTail = Seq(2, 3); var lazyTailSeq = Set(1, 2, 3).Tail().ToSeq(); var lazySeqTail = Set(1, 2, 3).ToSeq().Tail; var lazyPattern = Set(1, 2, 3).Case is (int _, Seq tail) ? tail : throw new ("invalid"); Assert.Equal(originalStrictTail.GetHashCode(), lazyTailSeq.GetHashCode()); Assert.Equal(originalStrictTail, lazyTailSeq); Assert.Equal(originalStrictTail.GetHashCode(), lazySeqTail.GetHashCode()); Assert.Equal(originalStrictTail, lazySeqTail); Assert.Equal(originalStrictTail.GetHashCode(), lazyPattern.GetHashCode()); Assert.Equal(originalStrictTail, lazyPattern); } [Fact] public void SequenceParallelRandomDelayTest() { var input = Seq(1, 2, 3, 2, 5, 1, 1, 2, 3, 2, 1, 2, 4, 2, 1, 5, 6, 1, 3, 6, 2); var ma = input.Select(DoDelay).Traverse(identity).As(); var res = from v in ma.Run().As() select v switch { Either>.Right (var seq) => seq.SequenceEqual(input), _ => false }; Assert.True(res.Run()); } static EitherT DoDelay(int seconds) { return liftIO(async () => await F(seconds)); static async ValueTask> F(int seconds) { await Task.Delay(seconds * 100); return seconds; } } } ================================================ FILE: LanguageExt.Tests/SerialisationTests.cs ================================================ using System; using Xunit; using Newtonsoft.Json; using System.Runtime.Serialization; using LanguageExt.Common; namespace LanguageExt.Tests { public class SerialisationTests { [Fact] public void SetTest() { var set = Set("test5", "test2", "test1", "test3", "test4"); var json = JsonConvert.SerializeObject(set); set = JsonConvert.DeserializeObject>(json); var lst = JsonConvert.DeserializeObject>(json); Assert.True(set.Count == 5); Assert.True(set.Contains("test1")); Assert.True(set.Contains("test2")); Assert.True(set.Contains("test3")); Assert.True(set.Contains("test4")); Assert.True(set.Contains("test5")); } [Fact] public void LstTest() { var list = List("test5", "test2", "test1", "test3", "test4"); var json = JsonConvert.SerializeObject(list); list = JsonConvert.DeserializeObject>(json); Assert.True(list.Count == 5); Assert.True(list[0] == "test5"); Assert.True(list[1] == "test2"); Assert.True(list[2] == "test1"); Assert.True(list[3] == "test3"); Assert.True(list[4] == "test4"); } [Fact] public void SeqTest() { var seq = Seq("test5", "test2", "test1", "test3", "test4"); var json = JsonConvert.SerializeObject(seq); seq = JsonConvert.DeserializeObject>(json); Assert.True(seq.Count == 5); Assert.True(seq[0] == "test5"); Assert.True(seq[1] == "test2"); Assert.True(seq[2] == "test1"); Assert.True(seq[3] == "test3"); Assert.True(seq[4] == "test4"); } [Fact] public void OptionTest() { var some = Some("test"); var none = Option.None; var someText = JsonConvert.SerializeObject(some); var noneText = JsonConvert.SerializeObject(none); var some2 = JsonConvert.DeserializeObject>(someText); var none2 = JsonConvert.DeserializeObject>(noneText); Assert.True(some == some2); Assert.True(none == none2); } [Fact] public void ActionTypeTest() { var x = new ActionType("Test1"); var y = new ActionType("Test2"); var z = new ActionType("Test3"); Assert.False(x == y); Assert.True(x != y); } [Serializable] public record ActionType(string Value); [Fact] public void ErrorSerialisationTest() { var settings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Objects }; var error = Error.New("Test"); var json = JsonConvert.SerializeObject(error, settings); var error1 = JsonConvert.DeserializeObject(json, settings); Assert.True(error == error1); } } } ================================================ FILE: LanguageExt.Tests/SetTests.cs ================================================ using L = LanguageExt; using static LanguageExt.Prelude; using static LanguageExt.Map; using Xunit; using System; using System.Linq; using LanguageExt.ClassInstances; namespace LanguageExt.Tests { public class SetTests { [Fact] public void SetKeyTypeTests() { var set = Set("one", "two", "three"); Assert.True(set.Contains("one")); Assert.True(set.Contains("ONE")); Assert.True(set.Contains("two")); Assert.True(set.Contains("Two")); Assert.True(set.Contains("three")); Assert.True(set.Contains("thREE")); } [Fact] public void EqualsTest() { var a = Set(1, 2); var b = Set(1, 2, 3); Assert.False(Set(1, 2, 3).Equals(Set())); Assert.False(Set().Equals(Set(1, 2, 3))); Assert.True(Set().Equals(Set())); Assert.True(Set(1).Equals(Set(1))); Assert.True(Set(1, 2).Equals(Set(1, 2))); Assert.False(Set(1, 2).Equals(Set(1, 2, 3))); Assert.False(Set(1, 2, 3).Equals(Set(1, 2))); } [Fact] public void SetGeneratorTest() { var m1 = Set(); m1 = m1.Add(100); Assert.True(m1.Count == 1 && m1.Contains(100)); } [Fact] public void SetAddInOrderTest() { var m = Set(1); m.Find(1).IfNone(() => failwith("Broken")); m = Set(1, 2); m.Find(1).IfNone(() => failwith("Broken")); m.Find(2).IfNone(() => failwith("Broken")); m = Set(1, 2, 3); m.Find(1).IfNone(() => failwith("Broken")); m.Find(2).IfNone(() => failwith("Broken")); m.Find(3).IfNone(() => failwith("Broken")); m = Set(1, 2, 3, 4); m.Find(1).IfNone(() => failwith("Broken")); m.Find(2).IfNone(() => failwith("Broken")); m.Find(3).IfNone(() => failwith("Broken")); m.Find(4).IfNone(() => failwith("Broken")); m = Set(1, 2, 3, 4, 5); m.Find(1).IfNone(() => failwith("Broken")); m.Find(2).IfNone(() => failwith("Broken")); m.Find(3).IfNone(() => failwith("Broken")); m.Find(4).IfNone(() => failwith("Broken")); m.Find(5).IfNone(() => failwith("Broken")); } [Fact] public void SetAddInReverseOrderTest() { var m = Set(2, 1); m.Find(1).IfNone(() => failwith("Broken")); m.Find(2).IfNone(() => failwith("Broken")); m = Set(3, 2, 1); m.Find(1).IfNone(() => failwith("Broken")); m.Find(2).IfNone(() => failwith("Broken")); m.Find(3).IfNone(() => failwith("Broken")); m = Set(4, 3, 2, 1); m.Find(1).IfNone(() => failwith("Broken")); m.Find(2).IfNone(() => failwith("Broken")); m.Find(3).IfNone(() => failwith("Broken")); m.Find(4).IfNone(() => failwith("Broken")); m = Set(5, 4, 3, 2, 1); m.Find(1).IfNone(() => failwith("Broken")); m.Find(2).IfNone(() => failwith("Broken")); m.Find(3).IfNone(() => failwith("Broken")); m.Find(4).IfNone(() => failwith("Broken")); m.Find(5).IfNone(() => failwith("Broken")); } [Fact] public void MapAddInMixedOrderTest() { var m = Set(5, 1, 3, 2, 4); m.Find(1).IfNone(() => failwith("Broken")); m.Find(2).IfNone(() => failwith("Broken")); m.Find(3).IfNone(() => failwith("Broken")); m.Find(4).IfNone(() => failwith("Broken")); m.Find(5).IfNone(() => failwith("Broken")); m = Set(1, 3, 5, 2, 4); m.Find(1).IfNone(() => failwith("Broken")); m.Find(2).IfNone(() => failwith("Broken")); m.Find(3).IfNone(() => failwith("Broken")); m.Find(4).IfNone(() => failwith("Broken")); m.Find(5).IfNone(() => failwith("Broken")); } [Fact] public void SetRemoveTest() { var m = Set(1, 3, 5, 2, 4); m.Find(1).IfNone(() => failwith("Broken 1")); m.Find(2).IfNone(() => failwith("Broken 2")); m.Find(3).IfNone(() => failwith("Broken 3")); m.Find(4).IfNone(() => failwith("Broken 4")); m.Find(5).IfNone(() => failwith("Broken 5")); Assert.True(m.Count == 5); m = m.Remove(4); Assert.True(m.Count == 4); Assert.True(m.Find(4).IsNone); m.Find(1).IfNone(() => failwith("Broken 1")); m.Find(2).IfNone(() => failwith("Broken 2")); m.Find(3).IfNone(() => failwith("Broken 3")); m.Find(5).IfNone(() => failwith("Broken 5")); m = m.Remove(1); Assert.True(m.Count == 3); Assert.True(m.Find(1).IsNone); m.Find(2).IfNone(() => failwith("Broken 2")); m.Find(3).IfNone(() => failwith("Broken 3")); m.Find(5).IfNone(() => failwith("Broken 5")); m = m.Remove(2); Assert.True(m.Count == 2); Assert.True(m.Find(2).IsNone); m.Find(3).IfNone(() => failwith("Broken 3")); m.Find(5).IfNone(() => failwith("Broken 5")); m = m.Remove(3); Assert.True(m.Count == 1); Assert.True(m.Find(3).IsNone); m.Find(5).IfNone(() => failwith("Broken 5")); m = m.Remove(5); Assert.True(m.Count == 0); Assert.True(m.Find(5).IsNone); } [Fact] public void MassAddRemoveTest() { int max = 100000; var items = IterableExtensions.AsIterable(Range(1, max)).Map(_ => Guid.NewGuid()).ToLst(); var m = toSet(items); Assert.True(m.Count == max); foreach (var item in items) { Assert.True(m.Contains(item)); m = m.Remove(item); Assert.False(m.Contains(item)); max--; Assert.True(m.Count == max); } } [Fact] public void SetOrdSumTest() { var m1 = Set("one", "two"); var m2 = Set("three"); var sum = m1 + m2; Assert.Equal(sum, m1.AddRange(m2)); Assert.Equal(m2, sum.Clear() + m2); } [Fact] public void SetUnionTest1() { var x = Set((1, 1), (2, 2), (3, 3)); var y = Set((1, 1), (2, 2), (3, 3)); var z = Set.union(x, y); Assert.True(z == Set((1, 1), (2, 2), (3, 3))); } [Fact] public void SetUnionTest2() { var x = Set((1, 1), (2, 2), (3, 3)); var y = Set((4, 4), (5, 5), (6, 6)); var z = Set.union(x, y); Assert.True(z == Set((1, 1), (2, 2), (3, 3), (4, 4), (5, 5), (6, 6))); } [Fact] public void SetIntesectTest1() { var x = Set((2, 2), (3, 3)); var y = Set((1, 1), (2, 2)); var z = Set.intersect(x, y); Assert.True(z == Set((2, 2))); } [Fact] public void SetExceptTest() { var x = Set((1, 1), (2, 2), (3, 3)); var y = Set((1, 1)); var z = Set.except(x, y); Assert.True(z == Set((2, 2), (3, 3))); } [Fact] public void SetSymmetricExceptTest() { var x = Set((1, 1), (2, 2), (3, 3)); var y = Set((1, 1), (3, 3)); var z = Set.symmetricExcept(x, y); Assert.True(z == Set((2, 2))); } [Fact] public void SliceTest() { var m = Set(1, 2, 3, 4, 5); var x = m.Slice(1, 2); Assert.True(x.Count == 2); Assert.True(x.Contains(1)); Assert.True(x.Contains(2)); var y = m.Slice(2, 4); Assert.True(y.Count == 3); Assert.True(y.Contains(2)); Assert.True(y.Contains(3)); Assert.True(y.Contains(4)); var z = m.Slice(4, 5); Assert.True(z.Count == 2); Assert.True(z.Contains(4)); Assert.True(z.Contains(5)); } [Fact] public void MinMaxTest() { var m = Set(1, 2, 3, 4, 5); Assert.True(m.Min == 1); Assert.True(m.Max == 5); var me = Set(); Assert.True(me.Min == None); Assert.True(me.Max == None); } [Fact] public void FindPredecessorWhenKeyExistsTest() { var m = Set(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15); Assert.True(m.FindPredecessor(1) == None); Assert.True(m.FindPredecessor(2) == 1); Assert.True(m.FindPredecessor(3) == 2); Assert.True(m.FindPredecessor(4) == 3); Assert.True(m.FindPredecessor(5) == 4); Assert.True(m.FindPredecessor(6) == 5); Assert.True(m.FindPredecessor(7) == 6); Assert.True(m.FindPredecessor(8) == 7); Assert.True(m.FindPredecessor(9) == 8); Assert.True(m.FindPredecessor(10) == 9); Assert.True(m.FindPredecessor(11) == 10); Assert.True(m.FindPredecessor(12) == 11); Assert.True(m.FindPredecessor(13) == 12); Assert.True(m.FindPredecessor(14) == 13); Assert.True(m.FindPredecessor(15) == 14); } [Fact] public void FindPredecessorWhenKeyNotExistsTest() { var m = Set(1, 3, 5, 7, 9, 11, 13, 15); Assert.True(m.FindPredecessor(1) == None); Assert.True(m.FindPredecessor(2) == 1); Assert.True(m.FindPredecessor(3) == 1); Assert.True(m.FindPredecessor(4) == 3); Assert.True(m.FindPredecessor(5) == 3); Assert.True(m.FindPredecessor(6) == 5); Assert.True(m.FindPredecessor(7) == 5); Assert.True(m.FindPredecessor(8) == 7); Assert.True(m.FindPredecessor(9) == 7); Assert.True(m.FindPredecessor(10) == 9); Assert.True(m.FindPredecessor(11) == 9); Assert.True(m.FindPredecessor(12) == 11); Assert.True(m.FindPredecessor(13) == 11); Assert.True(m.FindPredecessor(14) == 13); Assert.True(m.FindPredecessor(15) == 13); } [Fact] public void FindExactOrPredecessorWhenKeyExistsTest() { var m = Set(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15); Assert.True(m.FindExactOrPredecessor(1) == 1); Assert.True(m.FindExactOrPredecessor(2) == 2); Assert.True(m.FindExactOrPredecessor(3) == 3); Assert.True(m.FindExactOrPredecessor(4) == 4); Assert.True(m.FindExactOrPredecessor(5) == 5); Assert.True(m.FindExactOrPredecessor(6) == 6); Assert.True(m.FindExactOrPredecessor(7) == 7); Assert.True(m.FindExactOrPredecessor(8) == 8); Assert.True(m.FindExactOrPredecessor(9) == 9); Assert.True(m.FindExactOrPredecessor(10) == 10); Assert.True(m.FindExactOrPredecessor(11) == 11); Assert.True(m.FindExactOrPredecessor(12) == 12); Assert.True(m.FindExactOrPredecessor(13) == 13); Assert.True(m.FindExactOrPredecessor(14) == 14); Assert.True(m.FindExactOrPredecessor(15) == 15); } [Fact] public void FindExactOrPredecessorWhenKeySometimesExistsTest() { var m = Set(1, 3, 5, 7, 9, 11, 13, 15); Assert.True(m.FindExactOrPredecessor(1) == 1); Assert.True(m.FindExactOrPredecessor(2) == 1); Assert.True(m.FindExactOrPredecessor(3) == 3); Assert.True(m.FindExactOrPredecessor(4) == 3); Assert.True(m.FindExactOrPredecessor(5) == 5); Assert.True(m.FindExactOrPredecessor(6) == 5); Assert.True(m.FindExactOrPredecessor(7) == 7); Assert.True(m.FindExactOrPredecessor(8) == 7); Assert.True(m.FindExactOrPredecessor(9) == 9); Assert.True(m.FindExactOrPredecessor(10) == 9); Assert.True(m.FindExactOrPredecessor(11) == 11); Assert.True(m.FindExactOrPredecessor(12) == 11); Assert.True(m.FindExactOrPredecessor(13) == 13); Assert.True(m.FindExactOrPredecessor(14) == 13); Assert.True(m.FindExactOrPredecessor(15) == 15); } [Fact] public void FindSuccessorWhenKeyExistsTest() { var m = Set(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15); Assert.True(m.FindSuccessor(1) == 2); Assert.True(m.FindSuccessor(2) == 3); Assert.True(m.FindSuccessor(3) == 4); Assert.True(m.FindSuccessor(4) == 5); Assert.True(m.FindSuccessor(5) == 6); Assert.True(m.FindSuccessor(6) == 7); Assert.True(m.FindSuccessor(7) == 8); Assert.True(m.FindSuccessor(8) == 9); Assert.True(m.FindSuccessor(9) == 10); Assert.True(m.FindSuccessor(10) == 11); Assert.True(m.FindSuccessor(11) == 12); Assert.True(m.FindSuccessor(12) == 13); Assert.True(m.FindSuccessor(13) == 14); Assert.True(m.FindSuccessor(14) == 15); Assert.True(m.FindSuccessor(15) == None); } [Fact] public void FindSuccessorWhenKeyNotExistsTest() { var m = Set(1, 3, 5, 7, 9, 11, 13, 15); Assert.True(m.FindSuccessor(1) == 3); Assert.True(m.FindSuccessor(2) == 3); Assert.True(m.FindSuccessor(3) == 5); Assert.True(m.FindSuccessor(4) == 5); Assert.True(m.FindSuccessor(5) == 7); Assert.True(m.FindSuccessor(6) == 7); Assert.True(m.FindSuccessor(7) == 9); Assert.True(m.FindSuccessor(8) == 9); Assert.True(m.FindSuccessor(9) == 11); Assert.True(m.FindSuccessor(10) == 11); Assert.True(m.FindSuccessor(11) == 13); Assert.True(m.FindSuccessor(12) == 13); Assert.True(m.FindSuccessor(13) == 15); Assert.True(m.FindSuccessor(14) == 15); Assert.True(m.FindSuccessor(15) == None); } [Fact] public void FindExactOrSuccessorWhenKeyExistsTest() { var m = Set(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15); Assert.True(m.FindExactOrSuccessor(1) == 1); Assert.True(m.FindExactOrSuccessor(2) == 2); Assert.True(m.FindExactOrSuccessor(3) == 3); Assert.True(m.FindExactOrSuccessor(4) == 4); Assert.True(m.FindExactOrSuccessor(5) == 5); Assert.True(m.FindExactOrSuccessor(6) == 6); Assert.True(m.FindExactOrSuccessor(7) == 7); Assert.True(m.FindExactOrSuccessor(8) == 8); Assert.True(m.FindExactOrSuccessor(9) == 9); Assert.True(m.FindExactOrSuccessor(10) == 10); Assert.True(m.FindExactOrSuccessor(11) == 11); Assert.True(m.FindExactOrSuccessor(12) == 12); Assert.True(m.FindExactOrSuccessor(13) == 13); Assert.True(m.FindExactOrSuccessor(14) == 14); Assert.True(m.FindExactOrSuccessor(15) == 15); } [Fact] public void FindExactOrSuccessorWhenKeySometimesExistsTest() { var m = Set(1, 3, 5, 7, 9, 11, 13, 15); Assert.True(m.FindExactOrSuccessor(1) == 1); Assert.True(m.FindExactOrSuccessor(2) == 3); Assert.True(m.FindExactOrSuccessor(3) == 3); Assert.True(m.FindExactOrSuccessor(4) == 5); Assert.True(m.FindExactOrSuccessor(5) == 5); Assert.True(m.FindExactOrSuccessor(6) == 7); Assert.True(m.FindExactOrSuccessor(7) == 7); Assert.True(m.FindExactOrSuccessor(8) == 9); Assert.True(m.FindExactOrSuccessor(9) == 9); Assert.True(m.FindExactOrSuccessor(10) == 11); Assert.True(m.FindExactOrSuccessor(11) == 11); Assert.True(m.FindExactOrSuccessor(12) == 13); Assert.True(m.FindExactOrSuccessor(13) == 13); Assert.True(m.FindExactOrSuccessor(14) == 15); Assert.True(m.FindExactOrSuccessor(15) == 15); } [Fact] public void CaseTest() { // seq1 tests here just for reference { Assert.True(Seq().Case is not var (_, _) and not {} ); } { Assert.True(Seq1(1).Case is not var (_, _) and 1); } { Assert.True(Seq(1, 2).Case is (1, Seq xs) && xs == Seq1(2)); } { Assert.True(Set().Case is not var (_, _) and not {} ); } { Assert.True(Set(1).Case is not var (_, _) and 1); } { Assert.True(Set(1, 2).Case is (1, Seq xs) && xs == Seq1(2)); } { Assert.True(Set().Case is not var (_, _) and not {} ); } { Assert.True(Set(1).Case is not var (_, _) and 1); } { Assert.True(Set(1, 2).Case is (1, Seq xs) && xs == Seq1(2)); } } } } ================================================ FILE: LanguageExt.Tests/StackTests.cs ================================================ using static LanguageExt.Prelude; using static LanguageExt.List; using static LanguageExt.Stack; using Xunit; namespace LanguageExt.Tests { public class StackTests { [Fact] public void EmptyStackPeek() { var test = Stack(); var res = trypeek(test); Assert.True(res.IsNone); } [Fact] public void EmptyStackPop() { var test = Stack(); var res = map(trypop(test), (stack, value) => value); Assert.True(res.IsNone); } [Fact] public void Popping1() { var test = Stack(1, 2, 3, 4, 5); Popping5(test); } [Fact] public void Popping2() { var test = Stack(); test = push(test, 1); test = push(test, 2); test = push(test, 3); test = push(test, 4); test = push(test, 5); Popping5(test); } void Popping5(Stck test) { test = map(trypop(test), (stack, value) => { Assert.True(value.IsSome); return stack; }); test = map(trypop(test), (stack, value) => { Assert.True(value.IsSome); return stack; }); test = map(trypop(test), (stack, value) => { Assert.True(value.IsSome); return stack; }); test = map(trypop(test), (stack, value) => { Assert.True(value.IsSome); return stack; }); peek(test, Some: v => Assert.True(v == 1), None: () => Assert.False(true) ); } [Fact] public void CollectionFunctions() { var stack = toStack(Range(1,100)); Assert.True(exists(stack, v => v == 50)); Assert.True(length(stack) == 100); Assert.True(length(takeWhile(stack, v => v != 10)) == 90); Assert.True(length(take(stack, 10)) == 10); Assert.True(head(take(stack, 1)) == 100); } [Fact] public void RecursiveSumTest() { var values = toStack(Range(1, 10)); var res = Sum(values); Assert.True(res == sum(values)); } public int Sum(Stck stack) => pop(stack, Some: (newstack, value) => value + Sum(newstack), None: () => 0 ); } } ================================================ FILE: LanguageExt.Tests/Streaming/Source.Tests.cs ================================================ using Xunit; namespace LanguageExt.Tests.Streaming; public class SourceTests { [Fact] public void Empty_source_should_not_produce_values() { // Arrange var source = Source.empty(); var value = source.FoldReduce(0, (s, _) => s + 1).Run(); Assert.Equal(0, value); } [Fact] public void Pure_source_should_produce_single_value() { // Arrange var value = 42; var source = Source.pure(value); var output = source.Collect().Run(); // Act and Assert Assert.True(output == [42]); } [Fact] public void Map_should_transform_values() { // Arrange var source = Source.pure(42).Map(x => x * 2); var output = source.Collect().Run(); // Act and Assert Assert.True(output == [84]); } [Fact] public void Where_should_filter_values() { // Arrange var source = Source.pure(42).Where(x => x > 50); var output = source.Collect().Run(); // Act and Assert Assert.True(output == []); } [Fact] public void Add_should_concatenate_sources() { // Arrange var source1 = Source.pure(1); var source2 = Source.pure(2); // Act var concat = source1 + source2; var output = concat.Collect().Run(); // Assert Assert.Equal(2, output.Count); Assert.True(output == [1, 2]); } [Fact] public void Zip_should_combine_two_sources() { // Arrange var source1 = Source.pure(1); var source2 = Source.pure("A"); // Act var zipped = source1.Zip(source2); var output = zipped.Collect().Run(); // Assert Assert.True(output == [(1, "A")]); } [Fact] public void Or_operator_should_merge_two_sources() { // Arrange var source1 = Source.lift([1, 2, 3]); var source2 = Source.lift([10, 20, 30]); // Act var merged = source1 | source2; var output = merged.Collect().Run(); // Assert that the output has 3 items lower than 10 Assert.True(output.Filter(x => x < 10).Count == 3); // Assert that the output has 3 items greater than 10 Assert.True(output.Filter(x => x >= 10).Count == 3); // Assert that the output has the lower items in-order Assert.True(output.Filter(x => x < 10) == [1, 2, 3]); // Assert that the output has the upper items in-order Assert.True(output.Filter(x => x >= 10) == [10, 20, 30]); } } ================================================ FILE: LanguageExt.Tests/Streaming/SourceT.Tests.cs ================================================ using Xunit; namespace LanguageExt.Tests.Streaming; public class SourceTTests { [Fact] public void Empty_source_should_not_produce_values() { // Arrange var source = SourceT.empty(); var value = source.FoldReduce(0, (s, _) => s + 1).Run(); Assert.Equal(0, value); } [Fact] public void Pure_source_should_produce_single_value() { // Arrange var value = 42; var source = SourceT.pure(value); var output = source.Collect().Run(); // Act and Assert Assert.True(output == [42]); } [Fact] public void Map_should_transform_values() { // Arrange var source = SourceT.pure(42).Map(x => x * 2); var output = source.Collect().Run(); // Act and Assert Assert.True(output == [84]); } [Fact] public void Where_should_filter_values() { // Arrange var source = SourceT.pure(42).Where(x => x > 50); var output = source.Collect().Run(); // Act and Assert Assert.True(output == []); } [Fact] public void Add_should_concatenate_sources() { // Arrange var source1 = SourceT.pure(1); var source2 = SourceT.pure(2); // Act var concat = source1 + source2; var output = concat.Collect().Run(); // Assert Assert.Equal(2, output.Count); Assert.True(output == [1, 2]); } [Fact] public void Zip_should_combine_two_sources() { // Arrange var source1 = SourceT.pure(1); var source2 = SourceT.pure("A"); // Act var zipped = source1.Zip(source2); var output = zipped.Collect().Run(); // Assert Assert.True(output == [(1, "A")]); } [Fact] public void Or_operator_should_merge_two_sources() { // Arrange var source1 = SourceT.lift([1, 2, 3]); var source2 = SourceT.lift([10, 20, 30]); // Act var merged = source1 | source2; var output = merged.Collect().Run(); // Assert that the output has 3 items lower than 10 Assert.True(output.Filter(x => x < 10).Count == 3); // Assert that the output has 3 items greater than 10 Assert.True(output.Filter(x => x >= 10).Count == 3); // Assert that the output has the lower items in-order Assert.True(output.Filter(x => x < 10) == [1, 2, 3]); // Assert that the output has the upper items in-order Assert.True(output.Filter(x => x >= 10) == [10, 20, 30]); } } ================================================ FILE: LanguageExt.Tests/Sys/Diag/ActivityTests.cs ================================================ using Xunit; using System; using System.Threading.Tasks; using LanguageExt.Sys.Diag; using LanguageExt.Sys.Test; using System.Diagnostics; using FluentAssertions; using LanguageExt.Common; using LanguageExt.UnsafeValueAccess; namespace LanguageExt.Tests.Sys.Diag; using A = Activity; public static class ActivityTests { static readonly ActivitySource Source = new(nameof(ActivityTests)); static ActivityTests() => ActivitySource.AddActivityListener( new ActivityListener { ShouldListenTo = source => source.Name == nameof(ActivityTests), Sample = (ref ActivityCreationOptions _) => ActivitySamplingResult.AllData }); static T ArrangeAndAct(this K, T> effect) { using var rt = Runtime.New(); var rt1 = rt with { Env = rt.Env with { Activity = new ActivityEnv(Source, default, default) } }; return effect.As().Run(rt1, EnvIO.New()).ThrowIfFail(); } [Fact(DisplayName = "An activity span can be created and effect run within")] public static void Case1() => A.span("test", SuccessEff("a")).ArrangeAndAct().Should().Be("a"); [Fact(DisplayName = "The trace state can be read")] public static void Case2() { var result = A.span("test", from _ in A.setTraceState("test") from r in A.traceState select r) .ArrangeAndAct(); result.IsSome.Should().BeTrue(); result.Case.Should().Be("test"); } [Fact(DisplayName = "The trace id state can be read")] public static void Case3() { var result = A.span("test", A.traceId) .ArrangeAndAct(); result.IsSome.Should().BeTrue(); result.Case.Should().NotBeNull(); } [Fact(DisplayName = "The baggage can be set and read")] public static void Case4() { var baggage = A.span( "test", from _ in A.addBaggage("a", "b") from result in A.baggage select result ) .ArrangeAndAct(); Assert.True(baggage.Find("a").ForAll(v => v == "b")); } [Fact(DisplayName = "The tags can be set and read")] public static void Case5() { var baggage = A.span( "test", from _1 in A.addTag("a", "b") from result in A.tags select result) .ArrangeAndAct(); Assert.True(baggage.Find("a").ForAll(v => v == "b")); } [Fact(DisplayName = "The tag objects can be read")] public static void Case6() { var baggage = A.span( "test", from _ in A.addTag("a", 1) from result in A.tagObjects select result) .ArrangeAndAct(); Assert.True(baggage.Find("a").ForAll(v => v is 1)); } [Fact(DisplayName = "The context can be read")] public static void Case7() { var result = A.span("test", A.context).ArrangeAndAct(); result.IsSome.Should().BeTrue(); } [Fact(DisplayName = "The duration can be read")] public static void Case8() { var result = A.span("test", A.duration).ArrangeAndAct(); result.IsSome.Should().BeTrue(); } static readonly ActivityEvent TestEvent = new("some event"); [Fact(DisplayName = "The events can be read")] public static void Case9() { var events = A.span("test", from _ in A.addEvent(TestEvent) from result in A.events select result) .ArrangeAndAct(); Assert.False(events.IsEmpty); events.AsIterable().Head.ValueUnsafe().Should().Be(TestEvent); } [Fact(DisplayName = "The id can be read")] public static void Case10() { var result = A.span("test", A.id).ArrangeAndAct(); result.IsSome.Should().BeTrue(); } [Fact(DisplayName = "The kind can be read")] public static void Case11() { var result = A.span("test", ActivityKind.Consumer, A.kind) .ArrangeAndAct(); result.IsSome.Should().BeTrue(); result.Case.Should().Be(ActivityKind.Consumer); } [Fact(DisplayName = "The links can be read")] public static void Case12() { var r = A.span( "a", from co in A.context.As() from context in co.ToEff((Error)"context should be set") from result in A.span( "b", ActivityKind.Consumer, context, default, Seq(new ActivityLink(context)), DateTimeOffset.Now, A.links ) select result ) .ArrangeAndAct(); Assert.False(r.IsEmpty); } [Fact(DisplayName = "The current can be read and is not None in a span")] public static void Case13() { var result = A.span("test", A.current).ArrangeAndAct(); result.IsSome.Should().BeTrue(); } [Fact(DisplayName = "The current can be read and is None outside a span")] public static void Case14() { var result = A.current.ArrangeAndAct(); result.IsNone.Should().BeTrue(); } [Fact(DisplayName = "The parent id can be read and is not None in a nested span")] public static void Case15() { var id = A.span( "a", from co in A.context.As() from context in co.ToEff((Error)"context should be set") from result in A.span( "b", ActivityKind.Client, context, default, default, default, A.parentId ) select result ) .ArrangeAndAct(); id.IsSome.Should().BeTrue(); } [Fact(DisplayName = "The parent id can be read and is None in a single level span")] public static void Case16() { var id = A.span("a", A.parentId).ArrangeAndAct(); id.IsNone.Should().BeTrue(); } [Fact(DisplayName = "The parent span id can be read and is not None in a nested span")] public static void Case17() { var id = A.span( "a", from co in A.context.As() from context in co.ToEff((Error)"context should be set") from result in A.span( "b", ActivityKind.Client, context, default, default, default, A.parentSpanId ) select result ) .ArrangeAndAct(); id.IsSome.Should().BeTrue(); } [Fact(DisplayName = "The recorded can be read")] public static void Case18() { var result = A.span("test", A.recorded).ArrangeAndAct(); result.IsSome.Should().BeTrue(); } [Fact(DisplayName = "The display name can be read")] public static void Case19() { var result = A.span("test", A.displayName).ArrangeAndAct(); result.IsSome.Should().BeTrue(); } [Fact(DisplayName = "The operation name can be read")] public static void Case20() { var result = A.span("test", A.operationName) .ArrangeAndAct(); result.IsSome.Should().BeTrue(); } [Fact(DisplayName = "The root id can be read")] public static void Case21() { var result = A.span("test", A.rootId).ArrangeAndAct(); result.IsSome.Should().BeTrue(); } [Fact(DisplayName = "The span id can be read")] public static void Case22() { var result = A.span("test", A.spanId).ArrangeAndAct(); result.IsSome.Should().BeTrue(); } [Fact(DisplayName = "The state time can be read")] public static void Case23() { var result = A.span("test", A.startTimeUTC) .ArrangeAndAct(); result.IsSome.Should().BeTrue(); } [Fact(DisplayName = "Test span overload 1")] public static void Case24() { var (kind, tags) = A.span( "test", ActivityKind.Client, HashMap(("1", "a" as object), ("2", "b")), A.kind.Zip(A.tags)) .ArrangeAndAct(); kind.IsSome.Should().BeTrue(); kind.Case.Should().Be(ActivityKind.Client); Assert.True(tags.Find("1").ForAll(v => v is "a")); Assert.True(tags.Find("2").ForAll(v => v is "b")); } [Fact(DisplayName = "Test span overload 2")] public static void Case25() => A.span("test", SuccessEff(unit)).ArrangeAndAct(); [Fact(DisplayName = "Test span overload 3")] public static void Case26() { var result = A.span("test", ActivityKind.Consumer, A.kind.As()).ArrangeAndAct(); result.Case.Should().Be(ActivityKind.Consumer); } [Fact(DisplayName = "Test span overload 4")] public static void Case27() { var (kind, tags) = A.span("test", ActivityKind.Client, HashMap(("1", "a" as object), ("2", "b")), A.kind.Zip(A.tags)) .ArrangeAndAct(); Assert.True(kind.IsSome); Assert.Equal(ActivityKind.Client, kind.Case); Assert.True(tags.Find("1").ForAll(v => v is "a")); Assert.True(tags.Find("2").ForAll(v => v is "b")); } [Fact(DisplayName = "Test span overload 5")] public static void Case28() { var (kind, tags) = A.span( "A", from co in A.context.As() from context in co.ToEff((Error)"context should be set") from result in A.span( "B", ActivityKind.Client, context, HashMap(("1", "a" as object), ("2", "b")), Seq.Empty, DateTimeOffset.Now, A.kind.Zip(A.tags)) select result ) .ArrangeAndAct(); kind.IsSome.Should().BeTrue(); kind.Case.Should().Be(ActivityKind.Client); Assert.True(tags.Find("1").ForAll(v => v is "a")); Assert.True(tags.Find("2").ForAll(v => v is "b")); } } ================================================ FILE: LanguageExt.Tests/Sys/IO/DirectoryTests.cs ================================================ using System.IO; using System.Threading.Tasks; using LanguageExt.Sys.IO; using Xunit; using Runtime = LanguageExt.Sys.Test.Runtime; namespace LanguageExt.Tests.Sys.IO; public class DirectoryTests { [Fact] public async Task EnumerateFilesTest() { using var rt = Runtime.New(); var path = Path.Combine(Path.GetTempPath(), "subdir"); var expected = Path.Combine(path, "file"); var computation = from dir in Directory.create(path) from file in File.writeAllText(expected, string.Empty) from files in Directory.enumerateFiles(path) select files; var actual = (await computation.RunAsync(rt)).ThrowIfFail(); Assert.True(actual == Seq(expected)); } } ================================================ FILE: LanguageExt.Tests/TESTING.cs ================================================ // TODO: Delete this or turn it into some real tests using System; using System.IO; using System.Threading.Tasks; using LanguageExt.Common; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace LanguageExt; public static class Testing { public static void Test1() { var m1 = ReaderT.lift(Some(123)); var m2 = ReaderT.lift(Some(123)); var mt = ReaderT.lift(Some(unit)); var m0 = from w in Pure(123) from x in m1 from _ in unless(true, mt) from y in m2 from z in asks((string env) => env.Length) from e in ask() from n in ReaderT.Lift(Some("Paul")) select $"{e} {n}: {w + x + y + z}"; var m3 = from w in Pure(123) from x in m1 from y in m2 from z in Some(100) from e in ask() select $"{e}: {w + x + y + z}"; var r1 = m3.As().Run("Hello"); var m4 = from x in m1 from y in m2 from e in ask() select $"{e}: {x + y}"; var r2 = m4.As().Run("Hello").As(); } public static void Test2() { var m1 = Reader.Pure(123); var m2 = Reader.Pure(123); var m3 = from x in m1 from y in m2 from e in ask() select $"{e}: {x + y}"; var m4 = from x in m1 from y in m2 from e in ask() from z in Pure(234) select $"{e}: {x + y}"; } public static void Test3() { var m1 = ReaderT.lift(Right(123)); var m2 = ReaderT.lift(Right(123)); var m3 = from w in Pure(123) from x in m1 from y in m2 from z in Right(100) from e in ask() select $"{e}: {w + x + y + z}"; var r1 = m3.Run("Hello"); var m4 = from x in m1 from y in m2 from z in Left("fail") from e in ask() select $"{e}: {x + y + z}"; var r2 = m4.Run("Hello"); } public static void Test4() { var m1 = App.Pure(123); var m2 = App.Pure(123); var m3 = App.Fail(Error.New("fail")); var m4 = from w in Pure(234) from x in m1 from y in m2 from z in m3 from r in App.rootFolder from t in liftIO(async () => await File.ReadAllTextAsync($"{r}\\test.txt")) select $"{t}: {w + x + y + z}"; var r1 = m4.Run(new AppConfig("", "")).As(); } public static void Test6() { var m1 = ReaderT.lift(IdentityT.lift(IO.pure(123))); var m2 = ReaderT.lift(IdentityT.lift(IO.pure(123))); var m0 = from w in Pure(123) from p in ReaderT.ask, string>() from x in IO.pure("Hello") from i in ReaderT>.liftIO(IO.pure("Hello")) from j in IO.pure("Hello").Fork() from r in envIO from y in m2 select $"{p} {y} {j}"; var value = m0.Run("Hello").As().Value.As().Run(EnvIO.New()); } public static void Test7() { var m1 = ReaderT.lift(IO.pure(123)); var m2 = ReaderT.lift(IO.pure(123)); var m0 = from w in Pure(123) from q in m1 from f in use(() => File.Open("c:\\test.txt", FileMode.Open)) from p in ReaderT.ask() from x in IO.pure("Hello") from i in ReaderT.liftIO(IO.pure("Hello")) from j in IO.pure("Hello").Fork() from r in envIO from y in m2 select $"{p} {y} {j}"; var value = m0.Run("Hello").As(); } public static void Test8() { var m1 = OptionT.lift(ReaderT.lift(IO.pure(123))); var m2 = OptionT.lift(ReaderT.lift(IO.pure(123))); var m0 = from w in Pure(123) from q in m1 from f in use(() => File.Open("c:\\test.txt", FileMode.Open)) from p in ask() from i in liftIO(IO.pure("Hello")) from j in IO.pure("Hello").Fork() from r in envIO from _ in release(f) from y in m2 select $"{w} {f} {i}"; var value = m0.Match(Some: v => $"foo {v}", None: () => "bar").As() .Run("Paul").As() .Run(); OptionT, Env> ask() => OptionT.lift(ReaderT.ask()); OptionT, A> liftIO
(IO ma) => OptionT.liftIO, A>(ma); } /*public static void Test9() { var m1 = OptionT.lift(StateT.lift(IO.pure(100))); var m2 = OptionT.lift(StateT.lift(IO.pure(200))); var m0 = from w in Pure(123) from q in m1 from x in StateT.get() from i in OptionT.liftIO, string>(IO.pure("Hello")) from j in IO.pure("Hello").Fork() //from k in m1.ForkIO() -- Can't work, because OptionT is not MonadUnliftIO from k in m1.Run().Run("state").As().Fork() // But we can manually unpack from _ in StateT.put(x) from r in envIO from y in m2 select $"{w} {j} {i}"; var value = m0.Match(Some: v => $"foo {v}", None: () => "bar").As() .Run("Paul").As() .Run(); }*/ public static void Test10() { var m1 = StateT.lift(OptionT.lift(IO.pure(100))); var m2 = StateT.lift(OptionT.lift(IO.pure(200))); var m0 = from w in Pure(123) from q in m1 from x in StateT.get, string>() from i in StateT>.liftIO(IO.pure("Hello")) from j in IO.pure("Hello").Fork() from _ in StateT.put, string>(x) from r in envIO from y in m2 select $"{w} {j} {i}"; var value = m0.Run("Paul").As() .Match(Some: v => $"value: {v.Value}, state: {v.State}", None: () => "bar").As() .Run(); } static void TravTest1() { var xs = Seq(1, 2, 3, 4, 5); var r1 = Traversable.traverse(x => Some(x), xs); var ys = Seq(Some(1).Kind(), Some(2).Kind(), Option.None.Kind(), Some(4).Kind(), Option.None.Kind()); var r2 = Traversable.sequenceM(ys); } } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Generalised IO // public static class GeneralIO where M : Monad { public static K readAllText(string path) => M.LiftIOMaybe(liftIO(async _ => await File.ReadAllTextAsync(path))); } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Maybe test // public class Maybe : Monad { public static Maybe Just(A value) => new Just(value); public static K Bind(K ma, Func> f) => ma.As().Bind(f); public static K Recur(A value, Func>> f) => Monad.unsafeRecur(value, f); public static K Map(Func f, K ma) => ma.As().Map(f); public static K Pure(A value) => Just(value); public static K Apply(K> mf, K ma) => mf.As().Bind(f => ma.As().Map(f)); public static K Apply(K> mf, Memo ma) => mf.As().Bind(f => ma.Value.As().Map(f)); public static K Action(K ma, K mb) => ma.As().Bind(_ => mb); public static K LiftIOMaybe(IO ma) => throw new NotImplementedException(); } public abstract record Maybe : K { public static readonly Maybe Nothing = new Nothing(); public abstract Maybe Map(Func f); public abstract Maybe Bind(Func> f); public virtual Maybe Bind(Func> f) => Bind(x => f(x).As()); public Maybe SelectMany(Func> bind, Func project) => Bind(x => bind(x).Map(y => project(x, y))); public Maybe SelectMany(Func> bind, Func project) => SelectMany(x => bind(x).As(), project); } public record Just(A Value) : Maybe { public override Maybe Map(Func f) => new Just(f(Value)); public override Maybe Bind(Func> f) => f(Value); } public record Nothing : Maybe { public override Maybe Map(Func f) => Maybe.Nothing; public override Maybe Bind(Func> f) => Maybe.Nothing; } public static class MaybeExt { public static Maybe As(this K ma) => (Maybe)ma; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // App test // // Domain monad public record App(ReaderT, A> runReader) : K; // Application environment public record AppConfig(string ConnectionString, string RootFolder); public static class AppExtensions { public static App As(this K ma) => (App)ma; public static Either Run(this K ma, AppConfig config) => ma.As().runReader.Run(config).As(); } public class App : Fallible, Deriving.MonadT>, Either>, Deriving.MonadIO>>, Deriving.Readable>> { public static App Pure(A value) => Applicative.pure(value).As(); public static App Fail(Error error) => Fallible.error(error).As(); public static K Catch(K fa, Func Predicate, Func> Fail) => throw new NotImplementedException(); public static App connectionString => Readable.asks(env => env.ConnectionString).As(); public static App rootFolder => Readable.asks(env => env.RootFolder).As(); public static K>, A> Transform(K fa) => fa.As().runReader; public static K CoTransform(K>, A> fa) => new App(fa.As()); static K Fallible.Fail(Error error) => new App(ReaderT.lift, A>(Left(error).As())); } ================================================ FILE: LanguageExt.Tests/TrackingHashMapTests.cs ================================================ using static LanguageExt.Prelude; using static LanguageExt.HashMap; using Xunit; using System; using System.Linq; using LanguageExt.ClassInstances; namespace LanguageExt.Tests { public class TrackingHashMapTests { [Fact] public void itemLensGetShouldGetExistingValue() { var expected = "3"; var map = TrackingHashMap((1, "1"), (2, "2"), (3, "3"), (4, "4"), (5, "5")); var actual = TrackingHashMap.item(3).Get(map); Assert.Equal(expected, actual); } [Fact] public void itemLensGetShouldThrowExceptionForNonExistingValue() { Assert.Throws(() => { var map = TrackingHashMap((1, "1"), (2, "2"), (3, "3"), (4, "4"), (5, "5")); var actual = TrackingHashMap.item(10).Get(map); }); } [Fact] public void itemOrNoneLensGetShouldGetExistingValue() { var expected = "3"; var map = TrackingHashMap((1, "1"), (2, "2"), (3, "3"), (4, "4"), (5, "5")); var actual = TrackingHashMap.itemOrNone(3).Get(map); Assert.Equal(expected, actual); } [Fact] public void itemOrNoneLensGetShouldReturnNoneForNonExistingValue() { var expected = Option.None; var map = TrackingHashMap((1, "1"), (2, "2"), (3, "3"), (4, "4"), (5, "5")); var actual = TrackingHashMap.itemOrNone(10).Get(map); Assert.Equal(expected, actual); } } } ================================================ FILE: LanguageExt.Tests/TraitTests/AlternativeLawTests.cs ================================================ using Xunit; using LanguageExt; using LanguageExt.Common; namespace LanguageExt.Tests.TraitTests; public class AlternativeLawTests { [Fact] public void Arr() => AlternativeLaw.assert(); [Fact] public void HashSet() => AlternativeLaw.assert(); [Fact] public void Iterable() => AlternativeLaw.assert(); [Fact] public void Lst() => AlternativeLaw.assert(); [Fact] public void Seq() => AlternativeLaw.assert(); [Fact] public void EffRT() { bool eq(K, int> vx, K, int> vy) => vx.Run(unit).Equals(vy.Run(unit)); AlternativeLaw>.assert(eq); } [Fact] public void Eff() { bool eq(K vx, K vy) => vx.Run().Equals(vy.Run()); AlternativeLaw.assert(eq); } [Fact] public void IO() { bool eq(K vx, K vy) => vx.RunSafe().Equals(vy.RunSafe()); AlternativeLaw.assert(eq); } [Fact] public void Fin() => AlternativeLaw.assert(); [Fact] public void FinT() => AlternativeLaw>.assert(); [Fact] public void Option() => AlternativeLaw(A[] Values) : K; public static class FListExtensions { public static FList As(this K self) => (FList)self; } public class FList : Foldable { public static FList New(params A[] values) => new (values); public static Fold FoldStep(K ta, S initialState) { var ix = 0; var ma = ta.As().Values; var count = ma.Length; return go(initialState); Fold go(S state) => ix == count ? Fold.Done(state) : Fold.Loop(state, ma[ix++], go); } public static Fold FoldStepBack(K ta, S initialState) { var ma = ta.As().Values; var count = ma.Length; var ix = count; return go(initialState); Fold go(S state) => ix == 0 ? Fold.Done(state) : Fold.Loop(state, ma[--ix], go); } } ================================================ FILE: LanguageExt.Tests/TraitTests/FunctorLawTests.cs ================================================ using Xunit; using LanguageExt.Common; using LE = LanguageExt; namespace LanguageExt.Tests.TraitTests; public class FunctorLawTests { [Fact] public void Arr() { Arr fa = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; FunctorLaw.assert(fa); } [Fact] public void HashMap() { HashMap fa = [("one", 1), ("two", 2), ("three", 3), ("four", 4), ("five", 5)]; FunctorLaw>.assert(fa); } [Fact] public void HashSet() { HashSet fa = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; FunctorLaw.assert(fa); } [Fact] public void Iterable() { Iterable fa = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; FunctorLaw.assert(fa); } [Fact] public void IterableNE() { var fa = LE.IterableNE.create(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); FunctorLaw.assert(fa); } [Fact] public void Lst() { Lst fa = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; FunctorLaw.assert(fa); } [Fact] public void Map() { Map fa = [("one", 1), ("two", 2), ("three", 3), ("four", 4), ("five", 5)]; FunctorLaw>.assert(fa); } [Fact] public void Seq() { Seq fa = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; FunctorLaw.assert(fa); } [Fact] public void EffRT() { bool eq(K, int> vx, K, int> vy) => vx.Run(unit).Equals(vy.Run(unit)); var ma = Eff.Pure(1); var mx = Eff.Fail(Errors.Closed); FunctorLaw>.assert(ma, eq); FunctorLaw>.assert(mx, eq); } [Fact] public void Eff() { bool eq(K vx, K vy) => vx.Run().Equals(vy.Run()); var ma = Eff.Pure(1); var mx = Eff.Fail(Errors.Closed); FunctorLaw.assert(ma, eq); FunctorLaw.assert(mx, eq); } [Fact] public void IO() { bool eq(K vx, K vy) => vx.RunSafe().Equals(vy.RunSafe()); var ma = LE.IO.pure(1); var mx = LE.IO.fail(Errors.Closed); FunctorLaw.assert(ma, eq); FunctorLaw.assert(mx, eq); } [Fact] public void StreamT() { // TODO: Restore /* static Seq toSeq(StreamT s) => s.Head().Run() .Match(Some: x => x.Cons(toSeq(s.Tail())), None: []); static bool eq(K, int> vx, K, int> vy) => toSeq(vx.As()) == toSeq(vy.As()); var ma = StreamT.Pure(1); var mb = StreamT.Lift([1, 2, 3, 4, 5 , 6, 7, 8, 9, 10]); FunctorLaw>.assert(ma, eq); FunctorLaw>.assert(mb, eq); */ } [Fact] public void Either() { var fa = Right(1); var fx = Left("failed"); FunctorLaw>.assert(fa); FunctorLaw>.assert(fx); } [Fact] public void EitherT() { var fa = LE.EitherT.Right(1); var fx = LE.EitherT.Left("failed"); FunctorLaw>.assert(fa); FunctorLaw>.assert(fx); } [Fact] public void Fin() { var fa = LE.Fin.Succ(1); var fx = LE.Fin.Fail(Errors.TimedOut); FunctorLaw.assert(fa); FunctorLaw.assert(fx); } [Fact] public void FinT() { var fa = LE.FinT.Succ(1); var fx = LE.FinT.Fail(Errors.TimedOut); FunctorLaw>.assert(fa); FunctorLaw>.assert(fx); } [Fact] public void Option() { var fa = LE.Option.Some(1); var fx = Option.None; FunctorLaw) A()).CompareTo((A) A())); } public static IEnumerable CompareTo_Specialized_B_Data() => DataArray(new (AorB, AorB, int) [] { (B(0), B(0), 0), (B(0), B(1), -1), (B(1), B(0), 1), }); [Theory] [MemberData(nameof(CompareTo_Specialized_B_Data), new object[] { })] void CompareTo_Specialized_B(AorB left, AorB right, int expected) { Assert.Equal(expected, ((IComparable) left).CompareTo((B) right)); } public static IEnumerable EqualsTest_Data() => DataArray(new (AorB, AorB, bool) [] { (A(), A(), true), (A(), B(0), false), (B(0), A(), false), (B(0), B(0), true), (B(0), B(1), false), (B(1), B(0), false), }); [Theory] [MemberData(nameof(EqualsTest_Data), new object[] { })] public void EqualsTest(AorB left, AorB right, bool expected) => Assert.Equal(expected, left.Equals(right)); } } */ ================================================ FILE: LanguageExt.Tests/UnitsOfMeasureTests.cs ================================================ using System; using Xunit; using static LanguageExt.UnitsOfMeasure; namespace LanguageExt.Tests; public class UnitsOfMeasureTests { [Fact] public void PreludeLengthEqualityTest() { Assert.True(100 *cm == 1 *m); Assert.True(1 *km == 1000 *m); } [Fact] public void PreludeLengthEqualityTest3() { Assert.True(1 *yard == 3 *feet); } [Fact] public void PreludeLengthEqualityTest4() { Assert.True(12 *inches == 1 *feet); } [Fact] public void PreludeLengthCompareTest1() { Assert.True(1 *mile > 1 *km); } [Fact] public void PreludeLengthScalarTest2() { Assert.True(1 *km / 500 == 2 *metres); } [Fact] public void LengthEqualityTest() { Assert.True(100.Centimetres() == 1.Metres()); Assert.True(1.Kilometres() == 1000.Metres()); } [Fact] public void LengthEqualityTest2() { Length length = 1000.Millimetres(); Assert.True(length.Metres == 1.0); Assert.True(length.Millimetres == 1000.0); } [Fact] public void LengthEqualityTest3() { Assert.True(1.Yards() == 3.Feet()); } [Fact] public void LengthEqualityTest4() { Assert.True(12.Inches() == 1.Feet()); } [Fact] public void LengthCompareTest1() { Assert.True(1.Miles() > 1.Kilometres()); } [Fact] public void LengthScalarTest1() { Assert.True(1.Miles() * 10 == 10.Miles()); } [Fact] public void LengthScalarTest2() { Assert.True(1.Kilometres() / 500 == 2.Metres()); } [Fact] public void OperatorTests() { Length len = 1 *km; double val = len / (1 *m); // Divide by 1 metre to get a dimensionless value Assert.True(val == 1000.0); val = len / (1000 *m); Assert.True(val == 1.0); } [Fact] public void LengthCompareTest2() { Assert.True(100 *mm < 2 *m); } [Fact] public void LengthArithmetic1() { Length length = 1000 *mm + 1 *m; Assert.True(length == 2 *m); } [Fact] public void LengthArithmetic2() { Length length = 1 *cm + 10 *mm; Assert.True(length == 2 *cm); } [Fact] public void TimeEqualityTest() { Assert.True(60 *sec == 1 *min); Assert.True(60 *mins == 1 *hr); } [Fact] public void AreaTest1() { var a = 1000 *cm * 8 *m; var b = 80 *m2; Assert.True(a == b); } [Fact] public void SpeedTest1() { Velocity v = 100 *m /s; Length l = v * 2 *sec; double r = l / (1 *m); Assert.True(l == 200 * m); Assert.True(r == 200.0); } [Fact] public void SpeedTest2() { Velocity v = 100 *mph; Time t = 50 *miles / v; Length l = v * (4 *hours); Assert.True(t == 30 *mins); Assert.True(l == 400 *miles); } [Fact] public void AccelTest1() { Accel g = 9.8 *m /s /s; Accel g2 = 9.8 *ms2; Velocity vel = g * 5 *sec; Length len = vel * 5 *sec; Assert.True(vel.MetresPerSecond == 49.0); Assert.True(len.Metres == 245.0); } [Fact] public void AccelObjectEquals_Both0_True() => AssertTypeObjectEquals(0 * m / s / s); [Fact] public void AreaObjectEquals_Both0_True() => AssertTypeObjectEquals(0 * m * m); [Fact] public void LengthObjectEquals_Both0_True() => AssertTypeObjectEquals(0 * m); [Fact] public void TimeObjectEquals_Both0_True() => AssertTypeObjectEquals(this Eff ma) { using var rt = Runtime.New(); ma.Run(rt, EnvIO.New()).AssertFail(); } public static void AssertFail(this Eff ma, string userMessage) { using var rt = Runtime.New(); ma.Run(rt, EnvIO.New()).AssertFail(userMessage); } public static void AssertSucc(this Eff ma) { using var rt = Runtime.New(); ma.Run(rt, EnvIO.New()).AssertSucc(); } public static void AssertSucc(this Eff ma, string userMessage) { using var rt = Runtime.New(); ma.Run(rt, EnvIO.New()).AssertSucc(userMessage); } public static void AssertFail(this Eff ma, Error expected) { using var rt = Runtime.New(); ma.Run(rt, EnvIO.New()).AssertFail(expected); } public static void AssertFail(this Eff ma, Error expected, string userMessage) { using var rt = Runtime.New(); ma.Run(rt, EnvIO.New()).AssertFail(expected, userMessage); } public static void AssertSucc(this Eff ma, A expected) { using var rt = Runtime.New(); ma.Run(rt, EnvIO.New()).AssertSucc(expected); } public static void AssertSucc(this Eff ma, A expected, string userMessage) { using var rt = Runtime.New(); ma.Run(rt, EnvIO.New()).AssertSucc(expected, userMessage); } public static void AssertFail(this Eff ma, Func predicate) { using var rt = Runtime.New(); ma.Run(rt, EnvIO.New()).AssertFail(predicate); } public static void AssertFail(this Eff ma, Func predicate, string userMessage) { using var rt = Runtime.New(); ma.Run(rt, EnvIO.New()).AssertFail(predicate, userMessage); } public static void AssertSucc(this Eff ma, Func predicate) { using var rt = Runtime.New(); ma.Run(rt, EnvIO.New()).AssertSucc(predicate); } public static void AssertSucc(this Eff ma, Func predicate, string userMessage) { using var rt = Runtime.New(); ma.Run(rt, EnvIO.New()).AssertSucc(predicate, userMessage); } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Eff helpers // public static void AssertFail(this Eff ma) => ma.Run().AssertFail(); public static void AssertFail(this Eff ma, string userMessage) => ma.Run().AssertFail(userMessage); public static void AssertSucc(this Eff ma) => ma.Run().AssertSucc(); public static void AssertSucc(this Eff ma, string userMessage) => ma.Run().AssertSucc(userMessage); public static void AssertFail(this Eff ma, Error expected) => ma.Run().AssertFail(expected); public static void AssertFail(this Eff ma, Error expected, string userMessage) => ma.Run().AssertFail(expected, userMessage); public static void AssertSucc(this Eff ma, A expected) => ma.Run().AssertSucc(expected); public static void AssertSucc(this Eff ma, A expected, string userMessage) => ma.Run().AssertSucc(expected, userMessage); public static void AssertFail(this Eff ma, Func predicate) => ma.Run().AssertFail(predicate); public static void AssertFail(this Eff ma, Func predicate, string userMessage) => ma.Run().AssertFail(predicate, userMessage); public static void AssertSucc(this Eff ma, Func predicate) => ma.Run().AssertSucc(predicate); public static void AssertSucc(this Eff ma, Func predicate, string userMessage) => ma.Run().AssertSucc(predicate, userMessage); } ================================================ FILE: LanguageExt.XUnitExt/EitherExtensions.cs ================================================ using Xunit; namespace LanguageExt; public static class EitherExtensions { public static void AssertLeft(this Either ma) => AssertLeft(ma, "Expected to be in a Left state"); public static void AssertLeft(this Either ma, string userMessage) => Assert.True(ma.IsLeft, userMessage); public static void AssertRight(this Either ma) => AssertRight(ma, "Expected to be in a Right state"); public static void AssertRight(this Either ma, string userMessage) => Assert.True(ma.IsRight, userMessage); public static void AssertLeft(this Either ma, L expected) => AssertLeft(ma, expected, $"Expected to be in a Left state with a value of {expected}"); public static void AssertLeft(this Either ma, L expected, string userMessage) => Assert.True(ma.IsLeft && (expected?.Equals((L?)ma) ?? false), userMessage); public static void AssertRight(this Either ma, R expected) => AssertRight(ma, expected, $"Expected to be in a Right state with a value of {expected}"); public static void AssertRight(this Either ma, R expected, string userMessage) => Assert.True(ma.IsRight && (expected?.Equals((R?)ma) ?? false), userMessage); public static void AssertLeft(this Either ma, Func predicate) => AssertLeft(ma, predicate, "Expected to be in a Left state with a predicate that returns true"); public static void AssertLeft(this Either ma, Func predicate, string userMessage) => Assert.True(ma.IsLeft && predicate((L)ma), userMessage); public static void AssertRight(this Either ma, Func predicate) => AssertRight(ma, predicate, "Expected to be in a Right state with a predicate that returns true"); public static void AssertRight(this Either ma, Func predicate, string userMessage) => Assert.True(ma.IsRight && predicate((R)ma), userMessage); } ================================================ FILE: LanguageExt.XUnitExt/ErrorExtensions.cs ================================================ using LanguageExt.Common; using static LanguageExt.Prelude; namespace LanguageExt; public static partial class AssertExt { /// /// Asserts that the action throws an `Error` /// public static Unit Throws(Error error, Func action) { try { action(); throw new Exception("Expected error: " + error + ", but got none"); } catch (Exception e) { if (Error.New(e).Is(error)) { return unit; } else { throw new Exception("Expected error: " + error + ", but got: " + e); } } } /// /// Asserts that the action throws an `Error` /// public static Unit Throws(Error error, Action action) { try { action(); throw new Exception("Expected error: " + error + ", but got none"); } catch (Exception e) { if (Error.New(e).Is(error)) { return unit; } else { throw new Exception("Expected error: " + error + ", but got: " + e); } } } } ================================================ FILE: LanguageExt.XUnitExt/FinExtensions.cs ================================================ using LanguageExt.Common; using Xunit; namespace LanguageExt; public static class FinExtensions { public static void AssertFail(this Fin ma) => AssertFail(ma, "Expected to be in a Fail state"); public static void AssertFail(this Fin ma, string userMessage) => Assert.True(ma.IsFail, userMessage); public static void AssertSucc(this Fin ma) => AssertSucc(ma, "Expected to be in a Succ state"); public static void AssertSucc(this Fin ma, string userMessage) => Assert.True(ma.IsSucc, userMessage); public static void AssertFail(this Fin ma, Error expected) => AssertFail(ma, expected, $"Expected to be in a Fail state with a value of {expected}"); public static void AssertFail(this Fin ma, Error expected, string userMessage) => Assert.True(ma.IsFail && expected.Is((Error)ma), userMessage); public static void AssertSucc(this Fin ma, A expected) => AssertSucc(ma, expected, $"Expected to be in a Succ state with a value of {expected}"); public static void AssertSucc(this Fin ma, A expected, string userMessage) => Assert.True(ma.IsSucc && (expected?.Equals((A?)ma) ?? false), userMessage); public static void AssertFail(this Fin ma, Func predicate) => AssertFail(ma, predicate, "Expected to be in a Fail state with a predicate that returns true"); public static void AssertFail(this Fin ma, Func predicate, string userMessage) => Assert.True(ma.IsFail && predicate((Error)ma), userMessage); public static void AssertSucc(this Fin ma, Func predicate) => AssertSucc(ma, predicate, "Expected to be in a Succ state with a predicate that returns true"); public static void AssertSucc(this Fin ma, Func predicate, string userMessage) => Assert.True(ma.IsSucc && predicate((A)ma), userMessage); } ================================================ FILE: LanguageExt.XUnitExt/IOExtensions.cs ================================================ using LanguageExt.Common; using LanguageExt.Sys.Test; using Xunit; namespace LanguageExt; public static class IOExtensions { public static void AssertFail(this IO ma) => ma.AssertFail(_ => true, "Expected to be in a Fail state"); public static void AssertFail(this IO ma, string userMessage) => ma.AssertFail(_ => true, userMessage); public static void AssertSucc(this IO ma) => ma.AssertSucc(_ => true, "Expected to be in a Succ state"); public static void AssertSucc(this IO ma, string userMessage) => ma.AssertSucc(_ => true, userMessage); public static void AssertFail(this IO ma, Error expected) => ma.AssertFail(e => e.Is(expected), e => $"Expected to be in a Fail state with an Error that equals '{expected}', instead got: '{e}'"); public static void AssertFail(this IO ma, Error expected, string userMessage) => ma.AssertFail(e => e.Is(expected), userMessage); public static void AssertSucc(this IO ma, A expected) => ma.AssertSucc(x => expected?.Equals(x) ?? false, x => $"Expected to be in a Succ state with a result that equals {expected}, instead got: {x}"); public static void AssertSucc(this IO ma, A expected, string userMessage) => ma.AssertSucc(x => expected?.Equals(x) ?? false, userMessage); public static void AssertFail(this IO ma, Func predicate) { try { ma.Run().Ignore(); } catch (Exception e) { Assert.True(predicate(Error.New(e)), "Expected to be in a Fail state with a predicate that returns true"); } } public static void AssertFail(this IO ma, Func predicate, string userMessage) { try { ma.Run().Ignore(); } catch (Exception e) { Assert.True(predicate(Error.New(e)), userMessage); } } public static void AssertFail(this IO ma, Func predicate, Func userMessage) { try { ma.Run().Ignore(); } catch (Exception e) { Assert.True(predicate(e), userMessage(e)); } } public static void AssertSucc(this IO ma, Func predicate) => Assert.True(predicate(ma.Run()), "Expected to be in a Succ state with a predicate that returns true"); public static void AssertSucc(this IO ma, Func predicate, string userMessage) => Assert.True(predicate(ma.Run()), userMessage); public static void AssertSucc(this IO ma, Func predicate, Func userMessage) { var x = ma.Run(); Assert.True(predicate(x), userMessage(x)); } } ================================================ FILE: LanguageExt.XUnitExt/LanguageExt.XUnitExt.csproj ================================================  net10.0 enable enable true / ================================================ FILE: LanguageExt.XUnitExt/OptionExtensions.cs ================================================ using Xunit; namespace LanguageExt; public static class OptionExtensions { public static void AssertNone(this Option ma) => AssertNone(ma, "Expected to be in a None state"); public static void AssertNone(this Option ma, string userMessage) => Assert.True(ma.IsNone, userMessage); public static void AssertSome(this Option ma) => AssertSome(ma, "Expected to be in a Some state"); public static void AssertSome(this Option ma, string userMessage) => Assert.True(ma.IsSome, userMessage); public static void AssertSome(this Option ma, A expected) => AssertSome(ma, expected, $"Expected to be in a Some state with a value of {expected}"); public static void AssertSome(this Option ma, A expected, string userMessage) => Assert.True(ma.IsSome && expected.Equals((A)ma), userMessage); public static void AssertSome(this Option ma, Func predicate) => AssertSome(ma, predicate, "Expected to be in a Some state with a predicate that returns true"); public static void AssertSome(this Option ma, Func predicate, string userMessage) => Assert.True(ma.IsSome && predicate((A)ma), userMessage); } ================================================ FILE: LanguageExt.XUnitExt/README.nuget.md ================================================ # LanguageExt.XUnitExt Extensions for XUnit that are aware of language-ext types. ================================================ FILE: LanguageExt.XUnitExt/SourceExtensions.cs ================================================ namespace LanguageExt; public static partial class AssertExt { } ================================================ FILE: Major Version Release Notes/Version 2/README.md ================================================ # Version 2 Release Notes Version 2.0 of Language-Ext is now in released. This is a major overhaul of every type in the system. I have also broken out the `LanguageExt.Process` actor system into its own repo, it is now named *Echo*, so if you're using that you should [head over to the repo](https://github.com/louthy/echo-process) and follow that. It's still in alpha - it's feature complete, it just needs more testing - so it's lagging behind at the moment. If you're using both lang-ext Core and the Echo Process system then wait until the Echo Process system is released before migrating to the new Core. Version 2.0 of Language-Ext actually just started out as a branch where I was trying out a new technique for doing ad-hoc polymorphism in C# (think somewhere between Haskell typeclasses and Scala implicits). I didn't expect it to lead to an entire re-write. So a word of warning, there are many areas that I know will be breaking changes, but some I don't. Breaking changes will 99% of the time be compile time errors (rather than changes in behaviour that silently affect your code). So although I don't expect any major issues, For any large projects I would put aside an afternoon to fix up any compilation breakages. Often the breakages are for things like rectifying naming inconsistencies (for example some bi-map functions were named `Map`, some named `BiMap`, they're all now `BiMap`), another example is that all collection types (`Lst`, `Map`, etc.) are now structs. So any code that does this will fail to compile: ```c Map x = null; ``` The transformer extensions have been overhauled too (they provided overloads for nested monadic types, `Option>` for example). If you were cheating trying to get directly at values by calling `Lift` or `LiftUnsafe`, well, now you can't. It was a bad idea that was primarily to make the old transformer types work. So they're gone. The overloads of `Select` and `SelectMany` are more restricted now, because combining different monadic types could lead to some type resolution issues with the compiler. You will now need to lift your types into the context of the LINQ expression (there are now lots of extensions to do that: `ToOption`, `ToTry`, etc.) > For the problems you will inevitablity have with upgrading to language-ext 2.0, you will also have an enormous amount of new benefits and possibilities. My overriding goal with this library is to try and provide a safer environment in which to write C#. Version 1 was mostly trying to protect the programmer from null and mutable state. Version 2 is very much focussed on improving our lot when implementing abstract types. Inheritance based polymorphism is pretty much accepted to be the worst performer in the polymorphic world. Our other option is parametric polymorphism (generics). With this release I have facilitated [ad-hoc polymorphism](https://en.wikipedia.org/wiki/Ad_hoc_polymorphism) with a little known technique in C#. So for the first time it's possible to write numeric methods once for all numeric types or do structural equality testing that you can rely on. Also there is support for the much more difficult higher-order polymorphic types like `Monad`. LanguageExt 2.0 provides a fully type-safe and efficient approach to working with higher order types. So yes, you can now write functions that take monads, or functors, or applicatives, and return specialised values (rather than abstract or dynamic values). Instead of writing a function that takes an `Option`, you can write one that takes any monadic type, bind them, join them, map them, and return the concrete type that you pushed in. Of course without compiler or runtime support for higher-order generics some hoops need to be jumped through (and I'm sure there will be some Haskell purist losing their shit over the approach). But at no point is the integrity of your types affected. Often the technique requires quite a large amount of generic argument typing, but if you want to write the super generic code, it's now possible. I don't know of any other library that provides this functionality. This has allowed the transformer extensions to become more powerful too (because the code generator that emits them can now use the type-class/instance system). The `Writer` monad can now work with any output type (as long as it has a `Monoid` instance), so it's not limited to telling its output to an `IEnumerable`, it can be a `Lst`, a `string`, an `int`, or whatever `Monoid` you specify. > Personally I find this very elegant and exciting. It has so much potential, but many will be put off by the amount of generic args typing they need to do. If anybody from the Rosyln team is reading this, please for the love of god help out with the issues around constraints and excessive specifying of generic arguments. The power is here, but needs more support. Scroll down to the section on Ad-hoc polymorphism for more details. ## Documentation [Full API documentation can be found here](https://louthy.github.io/language-ext/index.htm) ## Bug fixes * Fix for `Lst.RemoveAt(index)` - certain tree arrangements caused this function to fail * Fix for `HSet` (now `HashSet`) constructor bug - constructing with an enumerable always failed ## New features - LanguageExt.Core ### New collection types: Type | Description --------------------------------------------|-------------- `Seq` | Cons-like, singly-linked list `HashSet` | Ordering is done by `GetHashCode()`. Existence testing is with `EqualityComparer.Default.Equals(a,b)` `HashMap` | Ordering is done by `GetHashCode()`. Existence testing is with `EqualityComparer.Default.Equals(a,b)` `HashSet where EqA : struct, Eq` | Ordering is done by `GetHashCode()`. Existence testing is with `default(EqA).Equals(a,b)` `HashMap` | Ordering is done by `GetHashCode()`. Existence testing is with `default(EqA).Equals(a,b)` `Set where OrdA : struct, Ord` | Ordering is done by `default(OrdA).Compare(a,b)`. Existence testing is with `default(OrdA).Equals(a,b)` `Map` | Ordering is done by `default(OrdA).Compare(a,b)`. Existence testing is with `default(OrdA).Equals(a,b)` `Arr` | Immutable array. Has the same access speed as the built-in array type, but with immutable cells. Modification is expensive, due to the entire array being copied per operation (although for very small arrays this would be more efficient than `Lst` or `Set`). `Lst where PredList : struct, Pred` | This allows lists to run a predicate on the `Count` property of the list after construction. `Lst where PredItem : struct, Pred` | This allows lists to run a predicate on the `Count` property of the list after construction and on items as they're being added to the list. As you can see above there are new type-safe key versions of `Set`, `HashSet`, `Map`, and `HashMap`. Imagine you want to sort the value of a set of strings in a case-insensitive way (without losing information by calling `value.ToLower()`). ```c# var map = Set(...) ``` The resulting type would be incompatible with: ```c# Set, or Set ``` And is therefore more type-safe than just using Set. [Examples](https://github.com/louthy/language-ext/blob/type-classes/LanguageExt.Tests/SetTests.cs) The two new predicate versions of `Lst` allow for properties of the list to travel with the type. So for example this shows how you can enforce a list to be non-empty: ```c# public int Product(Lst list) => list.Fold(1, (s, x) => s * x); ``` There are implicit conversion operators between `Lst` and `Lst`, and between `Lst` and `Lst`. They don't need to reallocate the collection, but converting to a more constrained type will cause the validation to run. This is very light for constructing `Lst`, but will cause every item in the list to be validated for `Lst`. And so it's possible to do this: ```c# Lst list = List(); var res = Product(list); // ArgumentOutOfRangeException ``` That will throw an `ArgumentOutOfRangeException` because the list is empty. Whereas this is fine: ```c# Lst list = List(1, 2, 3, 4, 5); var res = Product(list); // 120 ``` To construct the predicate list types directly, call: ```c# Lst list = List(1, 2, 3, 4, 5); ``` The second type of predicate `Lst` is `Lst`. `PredItem` is a predicate that's run against every item being added to the list. Once the item is in the list it won't be checked again (because it's an immutable list). For example, this is a `Lst` that can't be empty and won't accept `null` items. ```c# var x = List, string>("1", "2", "3"); ``` Obviously declaring types like this gets quite bulky quite quickly. So only using them for method arguments is definitely a good approach: ```c# public string Divify(Lst, string> items) => String.Join(items.Map(x => $"
{x}
")); ``` Then `Divify` can be invoked thus: ```c# var res = Divify(List("1", "2", "3")); // "
1
2
3
" ``` But as mentioned above, the implicit conversion from `Lst` to `Lst, string>` will run the `NonNullItems` predicate for each item in the `Lst`. Built-in are some standard `Pred` implementations: * `AnySize` - Always succeeds * `CountRange` - Limits the `Count` to be >= MIN and <= MAX * `MaxCount` - As above but with no lower bound * `NonEmpty` - List must have at least one item And by default there are lots of `Pred
` implementations. See the `NewType` discussion later. ### Seq A new feature is the type [`Seq`](https://github.com/louthy/language-ext/tree/master/LanguageExt.Core/DataTypes/Seq) which derives from [`ISeq`](https://github.com/louthy/language-ext/blob/master/LanguageExt.Core/DataTypes/Seq/ISeq.cs), which in turn is an `IEnumerable`. It works very much like `cons` in functional languages (although not a real cons, as that's not a workable solution in C#). It's a singly-linked list, with two key properties: `Head` which is the item at the head of the sequence, and `Tail` which is the rest of the sequence. You can convert any existing collection type (as well as `IEnumerable` types) to a `Seq` by calling the `Seq` constructor: ```c# var seq1 = Seq(List(1, 2, 3, 4, 5)); // Lst -> Seq var seq2 = Seq(Arr(1, 2, 3, 4, 5)); // Arr -> Seq var seq3 = Seq(new [] {1, 2, 3, 4, 5}); // A[] -> Seq ... ``` As well as construct them directly: ```c# var seq1 = Seq(1, 2, 3, 4, 5); var seq2 = 1.Cons(2.Cons(3.Cons(4.Cons(5.Cons(Empty))))); ``` In practice if you're using `Cons` you don't need to provide `Empty`: ```c# var seq = 1.Cons(); // Creates a sequence with one item in ``` The primary benefits are: * Immutable * Thread-safe * Much lighter weight than using `Lst` (although `Lst` is very efficient, it is still an AVL tree behind the scenes) * Maintains the original type for `Lst`, `Arr`, and arrays. So if you construct a `Seq` from one of those types, the `Seq` then gains extra powers for random lookups (`Skip`), and length queries (`Count`). * If you construct a `Seq` with an `IEnumerable` then it maintains its laziness, but also __guarantees that each item in the original `IEnumerable` is only ever enumerated once.__ * Obviously easier to type `Seq` than `IEnumerable`! * `Count` works by default for all non-`IEnumerable` sources. If you construct with an `IEnumerable` then `Count` will only cause a single evaluation of the underlying `IEnumerable` (and subsequent access to the seq for anything else doesn't cause additional evaluation) * Efficient pattern matching on the `Seq`, which again doesn't cause multiple evaluations of the underling collection. `Seq` has a bigger interface than `IEnumerable` which allows for various bespoke optimisations depending on the underlying collection; which is especially powerful for the LINQ operators like `Skip`, `Take`, `TakeWhile`, ```c# public interface ISeq : IEnumerable, IEquatable>, IComparable> { /// /// Head of the sequence /// A Head { get; } /// /// Head of the sequence /// Option HeadOrNone(); /// /// Tail of the sequence /// Seq Tail { get; } /// /// True if this cons node is the Empty node /// bool IsEmpty { get; } /// /// Returns the number of items in the sequence /// /// Number of items in the sequence int Count { get; } /// /// Match empty sequence, or multi-item sequence /// /// Return value type /// Match for an empty list /// Match for a non-empty /// Result of match function invoked B Match( Func Empty, Func, B> Tail); /// /// Match empty sequence, or one item sequence, or multi-item sequence /// /// Return value type /// Match for an empty list /// Match for a non-empty /// Result of match function invoked B Match( Func Empty, Func Head, Func, B> Tail); /// /// Match empty sequence, or multi-item sequence /// /// Return value type /// Match for an empty list /// Match for a non-empty /// Result of match function invoked B Match( Func Empty, Func, B> Seq); /// /// Match empty sequence, or one item sequence, or multi-item sequence /// /// Return value type /// Match for an empty list /// Match for a non-empty /// Result of match function invoked B Match( Func Empty, Func Head, Func, B> Tail); /// /// Map the sequence using the function provided /// /// /// Mapping function /// Mapped sequence Seq Map(Func f); /// /// Map the sequence using the function provided /// /// /// Mapping function /// Mapped sequence Seq Select(Func f); /// /// Filter the items in the sequence /// /// Predicate to apply to the items /// Filtered sequence Seq Filter(Func f); /// /// Filter the items in the sequence /// /// Predicate to apply to the items /// Filtered sequence Seq Where(Func f); /// /// Monadic bind (flatmap) of the sequence /// /// Bound return value type /// Bind function /// Flatmapped sequence Seq Bind(Func> f); /// /// Monadic bind (flatmap) of the sequence /// /// Bound return value type /// Bind function /// Flatmapped sequence Seq SelectMany(Func> bind, Func project); /// /// Fold the sequence from the first item to the last /// /// State type /// Initial state /// Fold function /// Aggregated state S Fold(S state, Func f); /// /// Fold the sequence from the last item to the first /// /// State type /// Initial state /// Fold function /// Aggregated state S FoldBack(S state, Func f); /// /// Returns true if the supplied predicate returns true for any /// item in the sequence. False otherwise. /// /// Predicate to apply /// True if the supplied predicate returns true for any /// item in the sequence. False otherwise. bool Exists(Func f); /// /// Returns true if the supplied predicate returns true for all /// items in the sequence. False otherwise. If there is an /// empty sequence then true is returned. /// /// Predicate to apply /// True if the supplied predicate returns true for all /// items in the sequence. False otherwise. If there is an /// empty sequence then true is returned. bool ForAll(Func f); /// /// Skip count items /// Seq Skip(int count); /// /// Take count items /// Seq Take(int count); /// /// Iterate the sequence, yielding items if they match the predicate /// provided, and stopping as soon as one doesn't /// /// A new sequence with the first items that match the /// predicate Seq TakeWhile(Func pred); /// /// Iterate the sequence, yielding items if they match the predicate /// provided, and stopping as soon as one doesn't. An index value is /// also provided to the predicate function. /// /// A new sequence with the first items that match the /// predicate Seq TakeWhile(Func pred); } ``` __Breaking changes__ * Previously there were functions in the `Prelude` called `seq` which took many types and turned them into `IEnumerable`. Now they're constructing `Seq` I have renamed them to `Seq` in line with other constructor functions. * Where sensible in the rest of the API I have changed `AsEnumerable()` to return a `Seq`. This is for types which are unlikely to have large sequences (like `Option`, `Either`, `Try`, etc.); You may find casting issues in those situations, but the types returned are obviously more useful, so it feels like a win. Let me know if you have issues (All the major types have a `ToSeq()` method where appropriate, for convenience). * In `LanguageExt.Parsec` any parsers that previously returned `IEnumerable` now return `Seq`; I noticed when using the `Parsec` library extensively that I would often use `ToArray()` or `Freeze()` to force strict evaluation of a `IEnumerable` result. Now you don't have to, but you may see some casting issues. * `Match` and `match` for `IEnumerable` previously allowed for matching up to six items in six separate lambdas. They're now gone because I doubt anybody uses them, and they're slightly unwieldy. Now they use `Seq` behind the scenes to remove any multiple evaluations during the matching process: ```c# static int Sum(Seq seq) => seq.Match( () => 0, x => x, (x, xs) => x + Sum(xs)); ``` ### Non-nullable types: In the ongoing quest to make it safer to write C# code, these types are all now structs and therefore can't be `null`: ```c# Stck, Que, Lst, Map, Map, HashMap, HashMap, Set, Set HashSet, HashSet, Que, Arr ``` This means you can create a member field and not initialise it and everything will 'just work': ```c# static class Test { public static Map Foo; } Assert.True(Test.Foo == Map.empty()); Assert.True(Test.Foo == default(Map); ``` ### Serialisation fixes `Map`, `Lst`, `Set`, `Option`, `Either`, etc. All have serialisers that work with Json.NET (finally). Examples: https://github.com/louthy/language-ext/blob/type-classes/LanguageExt.Tests/SerialisationTests.cs ### Lazy `Option` and `OptionUnsafe` `Option` and `OptionUnsafe` have until now been strict types. They can now support both strict and lazy behaviour in the same type. For example: ```c# var option = from w in Some(_ => 5) from x in Some(_ => 10) from y in Some(_ => 15) from z in Some(_ => 20) select w + x + y + z; // At this point the w + x + y + z expression hasn't run ``` Any function that returns a concrete non-Option type will force the expression to be evaluated: ```c# var result = option.IfNone(0); // 50 ``` The result is memoised and therefore subsequent calls to `Match` or similar won't re-evaluate the expression. The strict behaviour is unaltered: ```c# var option = from w in Some(5) from x in Some(10) from y in Some(15) from z in Some(20) select w + x + y + z; // At this point the w + x + y + z expression *has* run ``` If strict and lazy Options are combined, then the whole expression becomes lazy: ```c# var option = from w in Some(5) from x in Some(_ => 10) // lazy expression from y in Some(15) from z in Some(20) select w + x + y + z; // At this point the w + x + y + z expression hasn't run ``` Note if you want to write functions that return lazy options, then you will need to wrap them in `Optional` or `Some`: ```c# Option LazyOptionFoo() => Optional(_ => ... ); ``` > Either and EitherUnsafe will have lazy functionality soon ### `NewType` NewType has gained an extra generic argument. So this: ```c# class Metres : NewType { ... } ``` Becomes: ```c# class Metres : NewType { ... } ``` That makes lots of functionality more type-safe for `NewType` derived types. For example monadic and functor operators like `Select`, `Map`, `Bind`, `SelectMany` can now return `Metres` rather than `NewType`. Which is very important for the integrity of the type. There is a variant that takes an additional generic argument `PRED`. Which is constrained to be a `struct` of `Pred`. This is called in the base constructor: ```c# if (!default(PRED).True(value)) throw new ArgumentOutOfRangeException(nameof(value), value); ``` So you should be able to see that this allows validation to be embedded into the type. Here's an example from the new Process system client code: ```c# public class ClientConnectionId : NewType> { public ClientConnectionId(string value) : base(value) { } } ``` `ClientConnectionId` is like a session token, and `StrLen` means the string must be `10` to `100` chars long. It is defined thus: ```c# public struct StrLen : Pred where NMin : struct, Const where NMax : struct, Const { [Pure] public bool True(string value) => Range.Is.True(value?.Length ?? 0); } ``` `NMin` and `NMax` are constrained to be `Const`, and from the example above are `I10` and `I100`. They're defined as: ```c# public struct I10 : Const { public int Value => 10; } public struct I100 : Const { public int Value => 100; } ``` You can define your own `struct` type that derives from `Pred` to provide any common validation primatives that you like. And defined constants by deriving from `Const`. These are the built in predicate types: * `True` - Always succeeds, no matter what's passed * `False` - Always fails, no matter what's passed * `Equal` - Value `A` must be equal to `CONST`. `EQ` is a `Eq` instance * `Exists` - `Term1 - Term5` are predicates. The test passes if `A` is true when applied to any of the terms. * `ForAll` - `Term1 - Term5` are predicates. The test passes if `A` is true when applied to all of the terms. * `GreaterThan` - Value `A` must be greater than `CONST`. `ORD` is an `Ord` instance. * `GreaterOrEq` - Value `A` must be greater than or equal to `CONST`. `ORD` is an `Ord` instance. * `LessThan` - Value `A` must be less than `CONST`. `ORD` is an `Ord` instance. * `LessOrEq` - Value `A` must be less than or equal to `CONST`. `ORD` is an `Ord` instance. * `Range` - Value `A` must be in the range `MIN` to `MAX`. `ORD` is an `Ord` instance. Constants are in the `LanguageExt.ClassInstances.Const` namespace. Integers are prefixed with `I`; the most commonly used are already created: `0` to `256`, then powers of two and hundreds, thousands, etc. `Double` constants are prefixed with `D`. Only `D0`, `D1`, and `DNeg1` exist. `Char` constants are `'A'- 'Z'`, `'a' - 'z'`, `'0' - '9'`, `ChSpace`, `ChTab`, `ChCR`, `ChLF`. By embedding the validation into the type, there is no 'get out of jail free' cards where a loophole can be found in the type (or sub-type). And it also becomes fundamentally a different type to (for example) `NewType>` _(See the `` at the end)_; so any function that wants to work with a client connection token must accept either `ClientConnectionId` or its base type of `NewType>`. It gives a small glimpse into the world of dependently typed languages, I think this is a pretty powerful concept for improving the safety of C# types in general (with no runtime costs), and takes the original `NewType` idea to the next level. There is now an explicit cast operator. So for the `Metres` example above: ```c# Metres m = Metres.New(100); double x = (double)m * 2.0; ``` ### `NumType` With the new type-classes and class-instances (see later), it's now possible to write generic code for numeric-types. And so I have created a variant of `NewType` called `NumType`. Numeric types like `int` are the kind of types that are very commonly made into `NewType` derived types (along with `string`), but previously there wasn't a good story for doing arithmetic on those types. Now with the `NumType` it is possible. They work in exactly the same way as `NewTypes`, but you must specify a `Num` class-instance (below it's `TDouble`): ```c# public class Metres : NumType { public Metres(double x) : base(x) {} } ``` That gives you these extras over `NewType`: ```c# operator+ operator* operator/ operator- Product() Divide() Plus() Subtract() Abs() Signum() Min() Max() Sum() ``` As with `NewType` you can also use a predicate: ```c# public class Age : NumType> { public Age(int x) : base(x) {} } ``` ### `FloatType` Even more specialised than `NumType` and `NewType` in that it only accepts class-instances from the type-class `Floating`. It adds functionality that are only useful with floating-point number types (along with the functionality from `NumType` and `NewType`): ``` Exp(), Sqrt(), Log(), Pow(A exp), LogBase(A y), Sin(), Cos(), Asin(), Acos(), Atan(), Sinh() Cosh(), Tanh(), Asinh(), Acosh(), Atanh() ``` ### `ValueTuple` and `Tuple` A new feature of C# 7 is syntax for tuples. Instead of: ```c# Tuple.Create(a, b) ``` You can now: ```c# (a, b) ``` You can also give them names: ```c# (K Key, V Value) ``` So wherever in lang-ext tuples are used, they now accept `ValueTuple`. `ValueTuple` is the `struct` version of `Tuple` that C# is using to back the new syntax. This is particularly nice for `Map`: ```c# var map = Map( (1, "Paul"), (2, "Steve"), (3, "Stan"), (4, "Tanzeel"), (5, "Dan"), (6, "Andreas")); ``` Also `Map` now derives from `IEnumerable<(K Key, V Value)>`. Tuples look like they're going to be a lot more important in C# going forwards, so I have created extension methods and `Prelude` functions for `Tuple` and `ValueTuple` with up to 7 items. ```c# // Add an item to a tuple var (a,b,c) = (1, 2).Add(3); var (a,b,c,d) = (1, 2).Add(3).Add("Hello"); ``` If your tuple contains Semigroups (like `Lst` and `int` for example) then you can call `Append`: ```c# var (a, b) = append, TInt, Lst, int>( (List(1,2,3), 3), (List(4,5,6,7), 4)); // ([1,2,3,4,5,6,7], 7) ``` Or: ```c# var list = append, Lst>( (List(1,2,3), List(4,5,6,7)) ); // [1,2,3,4,5,6,7] ``` `Head` and `Tail`: ```c# var a = ("a", 123, true).Head(); // "a" var bc = ("a", 123, true).Tail(); // (123, true) ``` `Sum` and `Product`: ```c# var a = (100, 200, 300).Sum(); // 600 var b = (10, 10, 10).Product(); // 1000 ``` `Contains`: ```c# var a = (1,2,3,4,5).Contains(3); // true ``` Mapping: ```c# x = x.Map( tuple => tuple ); x = x.BiMap(a => a * 2, b => b * 3); x = x.TriMap(a => a * 2, b => b * 3, c => c + " add"); x = x.QuadMap(a => a * 2, b => b * 3, c => "over", d => "ride"); // etc. x = x.MapFirst(a => a * 2); // just maps the first item and leaves the rest alone x = x.MapSecond(b => b * 3); x = x.MapThird(c => c + " add"); x = x.MapFourth(d => "change"); // etc. var (a, b) = (100, "text").MapFirst(x => x * 2); // (200, "text") ``` Also: ```c# Iter, Fold, BiFold, BiFoldBack, TriFold, TriFoldBack, etc. ``` ### `Cond` `Cond` allows for building conditional expressions that can be used fluently. It also seamlessly steps between synchronous and asynchronous behaviour without any need for ceremony. Here's a simple example: ```c# var cond = Cond(x => x == 4) .Then(true) .Else(false); ``` That can be run like so: ```c# bool result = cond(4); // True bool result = cond(0); // False ``` Or, ```c# bool result = 4.Apply(cond); // True bool result = 0.Apply(cond); // False ``` Here's a slightly more complex example: ```c# var vowels = Subj().Map(Char.ToLower) .Any(x => x == 'a', x => x == 'e', x => x == 'i', x => x == 'o', x => x == 'u') .Then("Is a vowel") .Else("Is a consonant"); var x = vowels('a'); // "Is a vowel" ``` This can then be tagged onto anything that returns a char or a `Task`: ```c# var res = GetCharFromRemoteServer().Apply(vowels); // Task ``` See the [pull request](https://github.com/louthy/language-ext/pull/179) for the discussion that led to this feature. Thanks to [@ncthbrt](https://github.com/ncthbrt) for the suggestion and initial implementation. ### Range Continuing the super generic theme, there is now a new `Range` type that can handle any type (as long as they have `Monoid` and `Ord` instances): ```c# public class Range : IEnumerable where SELF : Range where MonoidOrdA : struct, Monoid, Ord, Arithmetic { ... } ``` As with `NewType`, `FloatType`, and `NumType`, anything that derives from it should provide itself as the first generic argument. This is the definition of `IntegerRange`: ```c# public class IntegerRange : Range { IntegerRange(int min, int max, int step) : base(min, max, step) { } } ``` Everything else is handled in the base class, so it's trivial to add your own. As before they implement `IEnumerable`, and are lazy. They now support `Overlaps(SELF range)` and `InRange(A x)`. There are two constructor functions: `Range.FromMinMax(min, max, step)` and `Range.FromCount(min, count, step)`. There are several provided implementations: `BigIntegerRange`, `CharRange`, `DecimalRange`, `DoubleRange`, `FloatRange`, `IntegerRange`, `LongRange`, `ShortRange`. #### `Try` and `TryOption` `Try` and `TryOption` (the lazy monads that catch exceptions) have been improved to memoise everything they do. So once you run a `Try`, running the same reference again won't re-invoke the computation, it will just return the previously cached value. This can be useful in LINQ expressions especially. #### `TryAsync` There is a new monadic type called `TryAsync` which, as you may have guessed, is an asynchronous version of `Try`. `Try` and `TryAsync` are delegate types that are invoked when you call extension methods like `Match` on them. By default the `TryAsync` evaluation extension methods will wrap the invocation in a `Task` and will catch any exceptions thrown. ```c# TryAsync LongRunningOp() => TryAsync(() => 10); int x = await LongRunningOp().Match( Succ: y => y * 2, Fail: ex => 0 ); ``` Unfortunately you must wrap the operation in a `TryAsync(() => ...)` because the compiler can't infer the result-type like it can with `Try`. However you can promote a `Try` to a `TryAsync`: ```c# Try LongRunningOp() => () => 10; int x = await LongRunningOp().ToAsync().Match( Succ: y => y * 2, Fail: ex => 0 ); ``` Or use any of the new `Async` extension methods added to `Try`: ```c# Try LongRunningOp() => () => 10; int x = await LongRunningOp().MatchAsync( Succ: y => y * 2, Fail: ex => 0 ); ``` Every single method of `Try` now has an `Async` variant. Also any method of `Try` or `TryAsync` that takes a `Func` (for example `Map(Try x, Func f)`), now has a variant that allows a `Task` to be returned instead. For `Try` methods this will either promote the result to be a `TryAsync` (as is the case with `Map`), or a `Task` (as is the case with `Fold`). This makes it trivial to deal with asynchronous results, as the `Try` or `TryAsync` will automatically perform the correct synchronisation required to extract a valid result. For functions where operations could run in parallel then the type will again handle that automatically. For example if you use the new `Applicative` functionality, this is possible: ```c# // Placeholder functions. Imagine they're doing some work to get some remote // data lists. TryAsync> GetListOneFromRemote() => TryAsync(List(1, 2, 3)); TryAsync> GetListTwoFromRemote() => TryAsync(List(4, 5, 6)); // Combines two lists and sorts them. public static IEnumerable CombineAndOrder(Lst x, Lst y) => from item in (x + y) orderby item select item; // Uses the fact that TryAsync is an applicative, and therefore has the // apply function available. The apply function will run all three parts // of the applicative asynchronously, will handle errors from any term, // and will then apply them to the CombineAndOrder function. public TryAsync> GetRemoteListsAndCombine() => apply( CombineAndOrder, GetListOneFromRemote(), GetListTwoFromRemote()); [Fact] public async void ListCombineTest() { var res = await GetRemoteListsAndCombine().IfFail(Enumerable.Empty()); var arr = res.ToArray(); Assert.True(arr[0] == 1); Assert.True(arr[1] == 2); Assert.True(arr[2] == 3); Assert.True(arr[3] == 4); Assert.True(arr[4] == 5); Assert.True(arr[5] == 6); } ``` #### `TryOptionAsync` As `Try` has got its `TryAsync` pairing, so has `TryOption` now got `TryOptionAsync`. The interface is almost exactly the same as `TryAsync`. Here are some quick examples: ```c# // Some example method prototypes public TryOptionAsync LongRunningAsyncTaskOp() => TryOptionAsync(10); public Task> LongRunningAsyncOp() => Task.FromResult(TryOption(10)); public TryOption LongRunningOp() => TryOption(10); public Task MapToTask(int x) => Task.FromResult(x * 2); public int MapTo(int x) => x * 2; public async void Test() { TryOptionAsync j = LongRunningOp().ToAsync(); TryOptionAsync k = LongRunningAsyncOp().ToAsync(); // These run synchronously int a = LongRunningOp().IfNoneOrFail(0); TryOption b = LongRunningOp().Map(MapTo); // These run asynchronously int u = await LongRunningAsyncTaskOp().IfNoneOrFail(0); int v = await LongRunningAsyncOp().IfNoneOrFail(0); int x = await LongRunningOp().IfNoneOrFailAsync(0); TryOptionAsync y1 = LongRunningOp().MapAsync(MapTo); TryOptionAsync y2 = LongRunningOp().MapAsync(MapToTask); int z1 = await y1.IfNoneOrFail(0); int z2 = await y2.IfNoneOrFail(0); } ``` ### Ad-hoc polymorphism Ad-hoc polymorphism has long been believed to not be possible in C#. However with some cunning it is. Ad-hoc polymorphism allows programmers to add traits to a type later. For example in C# it would be amazing if we had an interface called `INumeric` for numeric types like `int`, `long`, `double`, etc. The reason this doesn't exist is if you write a function like: ```c# INumeric Add(INumeric x, INumeric y) => x + y; ``` Then it would cause boxing. Which is slow (well, slower). I can only assume that's why it wasn't added by the BCL team. Anyway, it's possible to create a numeric type, very much like a type-class in Haskell, and ad-hoc _instances_ of the numeric _type-class_ that allow for generic numeric operations without boxing. > From now on I will call them type-classes and class-instances, or just instances. This is not exactly the same as Haskell's type-classes. If anything it's closer to Scala's implicits. However to make it easier to discuss them I will steal from Haskell's lexicon. #### `Num` So for example, this is how to create a number type-class: ```c# public interface Num { A Add(A x, A b); A Subtract(A x, A b); ... } ``` Notice how there are two arguments to `Add` and `Subtract`. Normally if I was going to implement this `interface` the left-hand-side of the `Add` and `Subtract` would be `this`. I will implement the _ad-hoc_ class-instance to demonstrate why that is: ```c# public struct TInt : Num { public int Add(int x, int b) => x + y; public int Subtract(int x, int b) => x + y; ... } ``` See how `TInt` is a `struct`? Structs have a useful property in C# in that they can't be `null`. So we can invoke the operations like so: ```c# int r = default(TInt).Add(10, 20); ``` The important thing to note is that `default(TInt)` gets optimisied out in a release build, so there's no cost to the invocation of `Add`. The `Add` and `Subtract` methods both take `int` and return `int`. So therefore there's no boxing at all. If we now implement `TFloat`: ```c# public struct TFloat : Num { public float Add(float x, float b) => x + y; public float Subtract(float x, float b) => x + y; ... } ``` Then we can see how a general function could be written to take any numeric type: ```c# public A DoubleIt(A x) where NumA : struct, Num => default(NumA).Add(x, x); ``` The important bit is the `NumA` generic argument, and the constraint of `struct, Num`. That allows us to call `default(NumA)` to get the type-class instance and invoke `Add`. And so this can now be called by: ```c# int a = DoubleIt(5); // 10 double b = DoubleIt(5.25); // 10.5 ``` By expanding the amount of operations that the `Num` type-class can do, you can perform any numeric operation you like. If you like you can add new numeric types (say for complex numbers, or whatever), where the rules of the type are kept in the _ad-hoc_ instance. Luckily you don't need to do that, because I have created the `Num` type (in the `LanguageExt.TypeClasses` namespace), as well as `Floating` (with all of the operations from `Math`; like `Sin`, `Cos`, `Exp`, etc.). `Num` also has a base-type of `Arithmetic` which supports `Plus`, `Subtract`, `Product`, `Negate`. This is for types which don't need the full spec of the `Num` type. I have also mapped all of the core numeric types to instances: `TInt`, `TShort`, `TLong`, `TFloat`, `TDouble`, `TDecimal`, `TBigInt`, etc. So it's possible to write truly generic numeric code once. > There's no getting around the fact that providing the class-instance in the generic arguments list is annoying (and later you'll see how annoying). The Roslyn team are looking into a type-classes like feature for a future version of C# (variously named: 'Concepts' or 'Shapes'). So this will I'm sure be rectified, and when it is, it will be implemented exactly as I am using them here. > > Until then the pain of providing the generic arguments must continue. You do however get a _super-powered C#_ in the mean-time. > > The need to write this kind of super-generic code is rare; but when you need it, _you need it_ - and right now this is simply the most powerful way. #### `Eq` Next up is `Eq`. Equality testing in C# is an absolute nightmare. From the different semantics of `Eqauls` and `==`, to `IEqualityComparer`, and the enormous hack which is `EqualityComparer.Default` (which doesn't blow up at compile-time if your code is wrong). The `Eq` type-class looks like this: ```c# public interface Eq { bool Equals(A x, A y); int GetHashCode(A x); } ``` There are `Eq` prefixed instances for all common types (`EqInt`, `EqString`, `EqGuid` etc.), as well as for all of the types in this library (`EqLst`, `EqSet`, `EqTry`, etc). All of the numeric types (`TInt`, `TDouble`, etc.) also implement `Eq`. To make it slightly prettier to use in code, you can use the `Prelude` `equals` function: ```c# bool x = equals(1, 1); // True ``` Or use `default` as shown before: ```c# bool x = default(EqInt).Equals(1, 1); // True ``` One final way is: ```c# bool x = EqInt.Inst.Equals(1, 1); ``` `Inst` is defined on all of the instances in lang-ext, but it's not an 'official feature'. Anybody could implement an ad-hoc implementation of `Eq` and not provide an `Inst`. For example you may call this directly: ```c# bool x = EqLst.Inst.Equals(List(1,2,3), List(1,2,3)); // true ``` Because you may be concerned about calling: ```c# bool x = List(1,2,3) == List(1,2,3); // ? ``` ... as all C# programmers are at some point, because we have no idea most of the time whether `==` does what we think it should. > Just FYI `List(1,2,3) == List(1,2,3)` does work properly! As do all types in language-ext. There are two variants of the immutable `HashSet` in language-ext: ```c# HashSet HashSet where EqA : struct, Eq ``` What's interesting about the second one is that the equality _definition_ is baked into the type. So this: ```c# HashSet ``` Is not compatible with: ```c# HashSet ``` And if you think about that, it's right. The strings that are used as keys in the `HashSet` do not have the same properties as the strings in `HashSet`. So even though they're both strings, they have different semantics (which cause wildly different behaviour for things like set intersection, unions, etc.) Now compare that to `HashSet` in the BCL, or `ImmutableHashSet` in `System.Collections.Immutable`, where two different sets with different `IEqualityComparer` types injected will cause undefined results when used together. > That's hopefully a small glimpse into the potential for improving type-safeness in C#. #### `Ord` `Ord` is for ordering. i.e. a `IComparable` replacement. By the way, these names `Eq`, `Ord`, `Num`, are all lifted from Haskell. I much prefer the short concise names that still convey meaning than the bulky and often clumsy names of the BCL. This is `Ord`, it derives from `Eq` ```c# public interface Ord : Eq { int Compare(A x, A y); } ``` Usage should be self-explanatory now, but the important thing to note here is that because 'type classes' are just interfaces, they can also have an inheritance hierarchy. This is a slightly more complex example: ```c# public struct OrdArray : Ord where ORD : struct, Ord { public int Compare(A[] mx, A[] my) { if (ReferenceEquals(mx, my)) return 0; if (ReferenceEquals(mx, null)) return -1; if (ReferenceEquals(my, null)) return 1; var cmp = mx.Length.CompareTo(my.Length); if (cmp == 0) { for(var i = 0; i < mx.Length; i++) { cmp = default(ORD).Compare(mx[i], my[i]); if (cmp != 0) return cmp; } return 0; } else { return cmp; } } public bool Equals(A[] x, A[] y) => default(EqArray).Equals(x, y); public int GetHashCode(A[] x) => hash(x); } ``` The `OrdArray` which is an `Ord`, does itself also take an `ORD` generic argument, which allows the contents of the array to be compared: ```c# int x = OrdArray.Inst.Compare(Array(1,2), Array(1,2)); // 0 ``` #### `Semigroup` This is where we start going a little more abstract. Semigroups are a feature of category theory, which is soooo not important for this discussion. They represent an associative binary operation, which can be invoked by calling `Append`. ```c# public interface Semigroup { A Append(A x, A y); } ``` Positive numbers (for example) form a semigroup. I won't dwell on it too long, because although the `Append` function is super-useful, nearly everything that falls into the `Semigroup` category is also a `Monoid`... #### `Monoid` A monoid has something that a semigroup doesn't, and that's the concept of identity (often meaning 'empty' or 'zero'). It looks like this: ```c# public interface Monoid : Semigroup { A Empty(); } ``` This comes with some helper functions in `LanguageExt.TypeClass`: ```c# public static partial class TypeClass { public static A mempty() where MONOID : struct, Monoid => default(MONOID).Empty(); public static A mconcat(IEnumerable xs) where MONOID : struct, Monoid => xs.Fold(mempty(), (s, x) => append(s, x)); public static A mconcat(params A[] xs) where MONOID : struct, Monoid => xs.Fold(mempty(), (s, x) => append(s, x)); } ``` Now the semigroup `Append` comes to life. Examples of monoids are: `TInt`, `MLst`, `TString`, etc. i.e. ```c# var x = mconcat("Hello", " ", "World"); // "Hello World" var y = mconcat, Lst>(List(1), List(2, 3)); // [1,2,3] var z = mconcat(1, 2, 3, 4, 5); // 15 ``` The `Empty()` function is what provides the _identity value_ for the concat operations. So for `string` it's `""`, for `Lst` it's `[]` and for `int` it's `0`. So a monoid is a semigroup with a _zero_. It's surprising how much _stuff_ just starts working when you know your type is a monoid. For example in language-ext version 1 there is a monadic type called `Writer`. The writer monad collects a _log_ as well as returning the bound value. In version 1 the log had to be an `IEnumerable`, which isn't super flexible. In language-ext version 2 the type looks like this: ```c# public class Writer where MonoidW : struct, Monoid { ... } ``` So now it can be a running numeric total, or a `Lst`, or a `Set`, or whatever monoid _you_ dream up. ### Higher-kinds Higher-order polymorphism would allow us to define a type like so: ```c# public interface MyType> { M Foo(M ma); } ``` Where not only is the `A` parametric, but so it `M`. So for example if I wanted to implement `MyType` for `Option` I could do: ```c# public class MyOptionType : MyType> { public Option Foo(Option ma) => ...; } ``` It would be soooo nice if C# (well, the _immutable_ CLR) would support this. But it doesn't. So we need to find ways around it. The way I am using for language-ext is: ```c# public interface MyType { MB Foo(MA ma); } public class MyOptionType : MyType, A> { public MB Foo(Option ma) => ...; } ``` #### `Monad` This is where some of the difficulties come in. How do we return an `MB` if we don't know what it is? This is a problem for the `Monad` type. This is a simplified version: ```c# public interface Monad { MB Bind(MA ma, Func bind); MA Return(A a); MA Fail(Exception e = null); } ``` Looking at the prototype for `Bind` it seems at first glance that the `bind` argument will give us the `MB` value we want. But an `Option` might be in a `None` state, in which case it shouldn't run `bind`. ```c# public MB Bind(Option ma, Func bind) => ma.IsSome ? bind(ma.Value) : ??? ; // What do we return here? ``` The key is to use constraints. But it also requires an extra generic paramter for `Bind`: ```c# public interface Monad { MB Bind(MA ma, Func bind) where MonadB : struct, Monad; MA Return(A a); MA Fail(Exception e = null); } ``` So we now know that `MonadB` is a class-instance of the `Monad` type-class. So we can now do this: ```c# public MB Bind(Option ma, Func f) where MonadB : struct, Monad => ma.IsSome ? f(ma.Value) : default(MonadB).Fail(); ``` The eagle eyed reader will notice that this actually allows binding to any resulting monad (not just `Option`). I'm not an academic, but I'm sure there will be someone throwing their toys out of their prams at this point. However, it works, it's type-safe, and it's efficient. [The actual definition of `Monad`](https://louthy.github.io/language-ext/LanguageExt.Core/LanguageExt.TypeClasses/Monad_Env_Out_MA_A.htm) is more complex than this, in order to unify monadic types that take arguments (`Reader` and `State`) and monads that carry internal state (`Writer` and `State`), as well as to support asynchronous monads (`TryAsync` and `TryOption`). I won't muddy the waters too much right now, but unified and type-safe they are. There are no hacks. You should see that the `Return` and `Fail` functions are trivial to implement: ```c# public Option Return(A a) => Optional(a); public Option Fail(Exception e = null) => None; ``` What that means is that any function that has been constrained by a monad instance can create new instances of them: ```c# public M CreateNewIntegerMonad(int x) where MonadInt : struct, Monad => default(MonadInt).Return(x); ``` This is one of the key breakthroughs. Imagine trying to create a `Monad` type the _old way_: ```c# public interface Monad { Monad Bind(Func> bind); } public class Option : Monad { public Monad Bind(Monad ma, Func> bind) => IsSome ? bind(Value) : None; } public Monad CreateNewIntegerMonad(int x) => ????; // How? ``` Maybe we could parameterise it? ```c# public Monad CreateNewIntegerMonad(int x) where M : Monad => ????; // We still can't call new M(x) ``` But that doesn't work either because we still can't call `new M(x)`. Being able to paramterise generic functions at the point where you know the concrete types (and therefore know the concrete class-instance) means that the generic functions can invoke the instance functions to create the concrete types. Here's a super generic example of a function that takes two monad arguments, they're both of the same type, and their bound values are `Num`. ```c# public static MA Add(MA ma, MA mb) where MonadA : struct, Monad where NumA : struct, Num => default(MonadA).Bind(ma, a => default(MonadA).Bind(mb, b => default(MonadA).Return(default(NumA).Plus(a, b)))); ``` You may notice that the two `Bind` calls followed by the `Return` are basically a much less attractive version of this: ```c# from a in ma from b in mb select default(NumA).Plus(a, b); ``` And so I can now add two options: ```c# var x = Some(10); var y = Some(20); var z = Option.None; var r1 = Add, Option, TInt, int>(x, y); // Some(30) var r2 = Add, Option, TInt, int>(x, z); // None Assert.True(r1 == Some(30)); Assert.True(r2 == None); ``` Or two lists: ```c# var x = List(1, 2, 3); var y = List(4, 5, 6); var z = List(); var r1 = Add, Lst, TInt, int>(x, y); var r2 = Add, Lst, TInt, int>(x, z); Assert.True(r1 == List(5, 6, 7, 6, 7, 8, 7, 8, 9)); Assert.True(r2 == z); ``` Or any two monads. They will follow the built in rules for the type, and produce concrete values efficiently and without any boxing or dynamic casting. ### Transformer types Because using the super-generic stuff is hard, and most of the time not needed. I have kept the transformer types, but they're now implemented in terms of the instance types. There is a new [`MonadTrans`](https://louthy.github.io/language-ext/LanguageExt.Core/LanguageExt/MonadTrans_OuterMonad_OuterType_InnerMonad_InnerType_A.htm) type-class and a default instance called [`Trans`](https://louthy.github.io/language-ext/LanguageExt.Core/LanguageExt/Trans_OuterMonad_OuterType_InnerMonad_InnerType_A.htm). For every pair of nested monads: `Lst>`, `Try>`, etc. there are the following extension methods (this is for `Arr>`): ```c# A SumT(this Arr> ma); int CountT(this Arr> ma); Arr> BindT(this Arr> ma, Func> f); Lst> Traverse(this Arr> ma, Func f); Lst> Sequence(this Arr> ma); Arr> MapT(this Arr> ma, Func f); S FoldT(this Arr> ma, S state, Func f); S FoldBackT(this Arr> ma, S state, Func f); bool ExistsT(this Arr> ma, Func f); bool ForAllT(this Arr> ma, Func f); Unit IterT(this Arr> ma, Action f); Arr> FilterT< A>(this Arr> ma, Func pred); Arr> Where(this Arr> ma, Func pred); Arr> Select(this Arr> ma, Func f); Arr> SelectMany( this Arr> ma, Func> bind, Func project); Arr> PlusT(this Arr> x, Arr> y) where NUM : struct, Num; Arr> SubtractT(this Arr> x, Arr> y) where NUM : struct, Num; Arr> ProductT(this Arr> x, Arr> y) where NUM : struct, Num => ApplyT(default(NUM).Product, x, y); Arr> DivideT(this Arr> x, Arr> y) where NUM : struct, Num; AppendT(this Arr> x, Arr> y) where SEMI : struct, Semigroup; int CompareT(this Arr> x, Arr> y) where ORD : struct, Ord; bool EqualsT(this Arr> x, Arr> y) where EQ : struct, Eq; Arr> ApplyT(this Func fab, Arr> fa); Arr> ApplyT(this Func fabc, Arr> fa, Arr> fb); ``` The number of functions has increased dramatically. Some of the special ones are `Traverse` and `Sequence` which flips the inner and outer types. So for example: ```c# Lst> x = List(Some(1), Some(2), Some(3), Some(4), Some(5)); Option> y = x.Sequence(); Assert.True(y == Some(List(1, 2, 3, 4, 5))); ``` As you can see, the list is now inside the option. ```c# Lst> x = List(Some(1), Some(2), Some(3), None, Some(5)); Option> y = x.Sequence(); Assert.True(y == None); ``` In this case there is a `None` in the `Lst` so when the `Lst>` becomes a `Option>` the rules of the `Option` take over, and one `None` means all `None`. This can be quite useful for `Either`: ```c# var x = List>(1, 2, 3, 4, "error"); var y = x.Sequence(); Assert.True(y.IsLeft && y == "error"); ``` This collects the first error it finds, or returns `Right` if there is no error. `Traverse` is the same as `Sequence` except it applies a mapping function to each bound value as it's transforming the types. Here's an example that runs 6 tasks in parallel, and collects their results: ```c# var start = DateTime.UtcNow; var f1 = Task.Run(() => { Thread.Sleep(3000); return 1; }); var f2 = Task.Run(() => { Thread.Sleep(3000); return 2; }); var f3 = Task.Run(() => { Thread.Sleep(3000); return 3; }); var f4 = Task.Run(() => { Thread.Sleep(3000); return 4; }); var f5 = Task.Run(() => { Thread.Sleep(3000); return 5; }); var f6 = Task.Run(() => { Thread.Sleep(3000); return 6; }); var res = await List(f1, f2, f3, f4, f5, f6).Traverse(x => x * 2); Assert.True(toSet(res) == Set(2, 4, 6, 8, 10, 12)); var ms = (int)(DateTime.UtcNow - start).TotalMilliseconds; Assert.True(ms < 3500, $"Took {ms} ticks"); ``` So there is a List of Tasks that becomes a single awaitable Task of List. As well as the extensions, there are also static classes for the transformer types. There is one for each type of monad. So for example, `Option` has a `LanguageExt.OptionT` type. Whenever you have a pair of nested monads, and `Option` is the inner monad, then you would use `OptionT`: ```c# var ma = List(Some(1),Some(2),Some(3),Some(4),Some(5)); var total = OptionT.foldT(ma, 0, (s, x) => s + x); // 15 var total = OptionT.sumT(ma); // 15 var mb = OptionT.filterT(ma, x > 3); // List(Some(3), Some(4)) ``` I could go on endlessly about the new types. There are so many. But for the release notes I think I should wrap it up. It's worth taking a look at the API documentation for the [type-classes](https://louthy.github.io/language-ext/LanguageExt.Core/LanguageExt.TypeClasses/index.htm) and the [instances](https://louthy.github.io/language-ext/LanguageExt.Core/LanguageExt.ClassInstances/index.htm) ### IL.Ctor Although this doesn't really fit with the core message of the library, it is something used internally in the project, and because it's super useful, I've decided to make it `public` rather than `private`. There are 4 functions for building a `Func<...,R>` which invokes the constructor for a type. ```c# var ticks = new DateTime(2017, 1, 1).Ticks; // Builds a delegate to call new DateTime(long); var ctor = IL.Ctor(); DateTime res = ctor(ticks); Assert.True(res.Ticks == ticks); ``` The generated delegate doesn't use reflection, IL is emitted directly. So invocation is as fast as calling `new DateTime(ticks)`. The four `Ctor` functions are for up to four arguments. The `NewType` system uses it to build a fast strongly-typed factory function: ```c# public abstract class NewType : IEquatable, IComparable where PRED : struct, Pred where NEWTYPE : NewType { protected readonly A Value; /// /// Constructor function /// public static readonly Func New = IL.Ctor(); ... } ``` As you can see, it would be impossible to call `new NEWTYPE(x)` from the `NewType` base-class. So for example: ```c# class Metres : NewType { public Metres(float x) : base(x) {} } var ms = Metres.New(100); ``` ================================================ FILE: Major Version Release Notes/Version 2/version-2-migration-notes.md ================================================ ### language-ext migration notes > `error CS0305: Using the generic type 'NewType' requires 2 type arguments` `NewType` and the new `NumType` and `FloatType` now have an additional generic argument. So if you defined your `NewType` in `v1` as this: ```c# public class Metres : NewType { public Metres(int x) : base(x) {} } ``` Then you should add an extra generic argument to the inherited `NewType`: ```c# public class Metres : NewType { public Metres(int x) : base(x) {} } ``` This allows the sub-type to be used in the base-type's methods (like `Select`, `Map`, `Where`, etc.) so that they can return `Metres` rather than `NewType`. > `The type 'ValueTuple<>' is defined in an assembly that is not referenced. You must add a reference to assembly 'System.ValueTuple,` There is new C# language support for tuples ("like", "so"). Language-Ext adds loads of extension methods to make them even more useful. However the underlying typle `System.ValueTuple` must be added to your projects from nu-get. > `error CS0305: Using the generic type 'Trans' requires 5 type arguments` The namespace `LanguageExt.Trans` is now deprecated. Remove any usings. > `error CS1750: A value of type '' cannot be used as a default parameter because there are no standard conversions to type ` Many types have become structs (like `Map`, `Lst`, etc). So if you see this error remove the ` = null`. If you need to assign anything to remove the warnings then either use `default`, or the built-in empty constructors. e.g. ```c# Map xs = Map(); // If you're using static LanguageExt.Prelude Map xs = Map.empty(); Map xs = default(Map); ``` > `'Prelude.Map(Tuple, params Tuple[])' cannot be inferred from the usage. Try specifying the type arguments explicitly.` The constructor functions for `Map`, `Set`, `List`, etc. have all been standardised. If you previously passed an `IEnumerable<...>` to any one of them, then you'll now need to either call `TYPE.createRange(...)` on the type, or `toTYPE(...)`. For example: ```c# IEnumerable<(int, string)> xs = new [] { (1, "A"), (2, "B"), (1, "C") }; var m1 = toMap(xs); var m2 = Map.createRange(xs); ``` > `error CS0104: 'HashSet<>' is an ambiguous reference between 'System.Collections.Generic.HashSet' and 'LanguageExt.HashSet'` `HSet` has been renamed, because it's a terrible name. And so having a reference to `System.Collections.Generic` and `LanguageExt` in the same code file will cause ambiguities that the linker can't resolve. Remove one of the usings, and then use aliases for the type you want. i.e. ```c# using G = System.Collections.Generic; var hs = new G.HashSet(); ``` > `error CS0122: 'NewType.Value' is inaccessible due to its protection level` Previously the `Value` property was public. If you want to make it public you should now opt-in by exposing the `protected` `Value` property. Alternatives are to use an explicit cast to the bound value type to get at the `Value`, or use `Map(x => ...)` ================================================ FILE: Major Version Release Notes/Version 5/Migration War Stories/README.md ================================================ # Migration from V4 to V5 notes ## Migrating `EffectsExamples` from the `Samples` folder ### `Menu.cs` * Removed: `struct` constraint * Removed: `HasCancel` constraint * Changed: `HasFile` constraint to `Has, FileIO>` * Changed: `HasTextRead` constraint to `Has, TextReadIO>` * Changed: `HasTime` constraint to `Has, TimeIO>` * Changed: `HasConsole` constraint to `Has, ConsoleIO>` * Search and replace: `Aff` with `Console, RT>` * Replace: `.Sequence` with `.Traverse` (and put `.As()` a the end) * Added: `.As()` to the end of the first `from` in `clearConsole` * Changed: `ToAff()` to `ToEff()` * Added: `.As()` to the end of the first `from` in `logError` * Changed: `Time` to `Time, RT>` * Added: `using static LanguageExt.UnitsOfMeasure;` * Added: `.As()` to the end of the first `from` in `showComplete` * Changed: `Seq(...)` to `[...]` ### `CancelExample.cs` * Removed: `HasCancel` constraint * Changed: `HasConsole` constraint to `Has, ConsoleIO>` * Search and replace: `Aff` with `Console, RT>` * Changed: `k.Key == ConsoleKey.Enter ? cancel() : unitEff` to `k.Key == ConsoleKey.Enter ? cancel : unitIO` ### `ErrorAndGuardExample.cs` * Removed: `HasCancel` constraint * Changed: `HasConsole` to `Has, ConsoleIO>` * Search and replace: `Console` with `Console, RT>` * Added: `using static LanguageExt.UnitsOfMeasure;` * Replace: `@catch` with `@catchM` when catching Effs (and changed predicate to use `ex.Is()`) ### `FoldTest.cs` * Removed: `struct` constraint * Removed: `HasCancel` constraint * Changed: `HasConsole` to `Has, ConsoleIO>` * Search and replace: `Aff` to `Effect, Unit>` * Added: `.As()` to the end of the first `RunEffect()` ### `ForkCancelExample.cs` * Search and replace: `Aff` constraint * Changed: `HasConsole` constraint to `Has, ConsoleIO>` * Changed: `HasTime` constraint to `Has, TimeIO>` * Added: `using static LanguageExt.UnitsOfMeasure;` * Search and replace: `Console` with `Console, RT>` * Change: `fork` now returns `ForkIO`, so update to use `ForkIO.Cancel` ### `QueueExample.cs` * Removed: `HasCancel` constraint * Changed: `HasConsole` to `Has, ConsoleIO>` * Replace: `Console` with `Console, RT>` * Changed: `EnqueueEff` with `EnqueueM` and appended `.As()` * Changed: `Queue` to `Queue, *>` * Changed: `Consumer` to `Consumer<*, Eff, *>` * Changed: `Pipe` to `Pipe<*, *, Eff, *>` * Changed: `Producer` to `Producer<*, Eff, *>` * Changed: `FailEff(Errors.Cancelled)` with `Fail(Errors.Cancelled)` * Added: `.As()` to `fork` ### `RetryExample.cs` * Removed: `HasCancel` constraint * Changed: `HasConsole` to `Has, ConsoleIO>` * Replace: `Console` with `Console, RT>` ### `TextFileChunkStreamExample.cs` * Removed: `struct` constraint * Removed: `HasCancel` constraint * Changed: `HasConsole` to `Has, ConsoleIO>` * Changed: `HasFile` to `Has, FileIO>` * Changed: `HasTextRead` to `Has, TextReadIO>` * Replace: `Aff` with `Console, RT>` * Changed: `Consumer` to `Consumer<*, Eff, *>` * Changed: `Pipe` to `Pipe<*, *, Eff, *>` * Changed: `Effect` to `Effect, *>` ### `TextFileLineStreamExample.cs` * Removed: `struct` constraint * Removed: `HasCancel` constraint * Changed: `HasConsole` to `Has, ConsoleIO>` * Changed: `HasFile` to `Has, FileIO>` * Changed: `HasTextRead` to `Has, TextReadIO>` * Replace: `Aff` with `Console, RT>` * Changed: `Consumer` to `Consumer<*, Eff, *>` * Changed: `Pipe` to `Pipe<*, *, Eff, *>` * Changed: `Effect` to `Effect, *>` ### `TimeExample.cs` * Added: `using static LanguageExt.UnitsOfMeasure;` * Removed: `struct` constraint * Removed: `HasCancel` constraint * Replace: `HasConsole` constraint to `Has, ConsoleIO>` * Replace: `HasTime` constraint to `Has, TimeIO>` * Replace: `Time.now` to `Time, RT>.now` * Replace: `Console` with `Console, RT>` ### `TimeoutExample.cs` * Added: `using static LanguageExt.UnitsOfMeasure;` * Removed: `struct` constraint * Removed: `HasCancel` constraint * Replace: `HasConsole` constraint to `Has, ConsoleIO>` * Replace: `HasTime` constraint to `Has, TimeIO>` * Replace: `Aff localEff(rt => rt.SetActivity(act.Activity), operation)); ``` to: ```c# from a in startActivity(name, activityKind, activityTags, activityLinks, startTime) from r in localEff(rt => rt.WithActivity(a), operation) select r; ``` 3. Changed `Aff` to `Eff` 4. Deleted duplicate methods (due to `Aff` being renamed to `Eff`) 5. Changed `Eff(rt => ...` to `lift((RT rt) => ...` 6. Changed `cancelToken()` to `cancelToken` 7. Changed `Producer` to `Producer, A>` ================================================ FILE: Major Version Release Notes/Version 5/README.md ================================================ # Version 5.0.0 Major Release Notes **_WORK IN PROGRESS NOTES_** Version 5 of language-ext is huge. It is both massive in terms of new capabilities delivered but also in _breaking changes_ that will require you to spend some time fixing up compilation failures and making judgements about your code. An upgrade to `v5` should be considered a significant undertaking for a large code-base - please make sure you have the time before upgrading. I have made copious notes below to help you understand the changes and to ease the migration. If, during the migration you encounter issues that are not in these notes, please let me know so I can update them for everybody else. Further contextual information about the updates can be [found on my blog](https://paullouth.com/higher-kinds-in-c-with-language-ext/) -- it digs a little deeper into the new capabilities and hopefully explains why these changes are necessary. Language-ext is 10 years old this year, so please consider this my _once per decade refresh_. It is very much my .NET Framework to .NET Core moment. # Contents * [Motivations](#motivations) * [Empower the users](#empower-the-users) * [Wage war on `async` (green threads)](#wage-war-on-async-green-threads) * [Leverage modern C# features](#leverage-modern-c-features) * [New Features](#new-features) * [Higher-kinded polymorphism](#higher-kinded-polymorphism) * [Introduction](#introduction) * [Traits](#traits) * [Functor]() * [Applicative]() * [Monad]() * [Foldable]() * [Fallible]() * [Traversable]() * [SemigroupK]() * [MonoidK]() * [SemiAlternative]() * [Alternative]() * [Free monads]() * [IO monad]() * [Auto-resource managment]() (`use` / `release`) * [Streaming effects]() * [Monad transformers (MonadT)]() * [OptionT]() * [EitherT]() * [FinT]() * [ValidationT]() * [Reader and ReaderT]() * [Writer and WriterT]() * [State and StateT]() * [StreamT]() * [All computation based monads rewritten to use transformers]() * [Infinite recursion in monads]() * [`Pure` / `Fail` monads]() * [Lifting]() * [Improved guards]() * [Nullable annotations]() * [Collection initialisers]() * [Breaking changes](#breaking-changes) * [netstandard2.0 no longer supported](#netstandard20-no-longer-supported) * [`Seq1` made `[Obsolete]`](#seq1-made-obsolete) * ['Trait' types now use static interface methods](#trait-types-now-use-static-interface-methods) * [The 'higher-kind' trait types have all been refactored](#the-higher-kind-trait-types-have-all-been-refactored) * [The `Semigroup` and `Monoid` types have been refactored](#the-semigroupa-and-monoida-types-have-been-refactored) * [The static `TypeClass` class has been renamed `Trait`](#the-static-typeclass-class-has-been-renamed-trait) * [`Apply` extensions that use raw `Func` removed](#apply-extensions-that-use-raw-func-removed) * [Manually written `Sequence` extension methods have been removed (#1)](#manually-written-sequence-extension-methods-have-been-removed-1) * [Manually written `Sequence` extension methods have been removed (#2)](#manually-written-sequence-extension-methods-have-been-removed-2) * [Manually written `Sequence` extension methods have been removed (#3)](#manually-written-traverse-extension-methods-have-been-removed-3) * [`ToComparer` doesn't exist on the `Ord` trait any more](#tocomparer-doesnt-exist-on-the-orda-trait-any-more) * [Renamed `LanguageExt.ClassInstances.Sum`](#renamed-languageextclassinstancessum) * [`Guard` has become `Guard`](#guarde-has-become-guarde-a) * [Existing uses of `HasCancel` should be replaced with `HasIO`](#existing-uses-of-hascancelrt-should-be-replaced-with-hasiort) * [`UnitsOfMeasaure` namespace converted to a static class](#unitsofmeasaure-namespace-converted-to-a-static-class) * [`Either` doesn't support `IEnumerable` any more](#either-doesnt-support-ienumerableeitherdata-any-more) * [`Either` 'bi' functions have their arguments flipped](#either-bi-functions-have-their-arguments-flipped) * [Nullable (struct) extensions removed](#nullable-struct-extensions-removed) * [Support for `Tuple` and `KeyValuePair` removed](#support-for-tuple-and-keyvaluepair-removed) * [Types removed outright](#types-removed-outright) * [`Some`](#somea) * [`OptionNone`](#optionnone) * [`EitherUnsafe`](#eitherunsafel-r) * [`EitherLeft`](#eitherleftl) * [`EitherRight`](#eitherrightl) * [`Try`](#trya) * [`TryOption`](#tryoptiona) * [`TryAsync`](#tryasynca) * [`TryOptionAsync`](#tryoptionasynca) * [`Result`](#resulta) * [`OptionalResult`](#optionalresulta) * [Async extensions for `Option`](#async-extensions-for-optiona) * [`ExceptionMatch`, `ExceptionMatchAsync`, `ExceptionMatchOptionalAsync`](#exceptionmatch-exceptionmatchasync-exceptionmatchoptionalasync) * [`NewType`, `NumType`, `FloatType`]() * [Libraries removed outright](#libraries-removed-outright) * [`LanguageExt.SysX`](#languageextsysx) * [`LanguageExt.CodeGen`](#languageextcodegen) * [`LanguageExt.Transformers`](#languageexttransformers) # Motivations * Empower the users * Wage war on async (green threads) * Support for transducers * Once per decade refresh ## Empower the users If you spend any time with Haskell or other languages with higher-kinds, you'll notice a heavy use of higher-kinded polymorphism. From functors, applicatives, monads, foldables, traversables, monad transformers, etc. This flavour of polymorphism allows for compositional superpowers that we can only dream of in C#. _Composition of pure functional components_ always leads to new, more capable components, that are also **automatically** pure. This is the true power of pure functional composition; unlike OO composition that just collects potential complexity, pure functional composition abstracts away from it. You can trust the composition to be sound. Much of this library up until this point has been trying to give you the capability to build these compositions. It's been achieved so far by lots of typing by me and lots of code-gen. For example, the `LanguageExt.Transformers` library was a code-genned mass of source-code that combined every monadic type with every other monadic type so that you can work on them nested. It was 200,000 lines of generated code - and was getting exponentially worse. I've also written 100s (maybe 1000s) of `Select` and `SelectMany` implementations that give you the impression that C# has generalised monads. But it doesn't, it's just a lot of typing. If you create your own monadic type it won't magically work with any existing language-ext types and you can't write general functions that accept any monad and have it just work. > And that is very much because C# doesn't support higher-kinded polymorphism. That means that you pretty much only get to use what I provide. That's not acceptable to me. I want to empower everybody to be able to leverage pure functional composition in the same way that can be done in Haskell (and other languages that have higher-kinds). A series of articles on higher-kinds [features on on my blog](https://paullouth.com/higher-kinds-in-c-with-language-ext/). ## Wage war on `async` (green threads) If I continued the way I was before then every monadic type would have an `*Async` variant, as would every method and function. This was getting out of hand. For something like an IO/effect monad where you could also have an optional error-type and optional runtime-type that meant 8 types. Each with 1000s of lines of code to define them. Then, when you think there's 20 or so 30 or so monadic types, it becomes a big maintenence problem. There's also issues around consistency between each type (making sure everything has a `MapAsync`, `BindAsync`, etc.) - as well as making sure sync types can work with async types, etc. So, as of now, this library stands against 'declarative async' - i.e. we are adopting a _'green threads mentality'_. That is we will not be giving you `*Async` variants of anything. All IO computation types (`IO` and `Eff`) will support the _lifting_ of both synchronous and asynchronous functions, but you won't see evidence of asynchronicity in any type-signatures. Those types each have a `Run()` function which _appear_ to run synchronously, i.e. they don't return a `Task`. In fact, they don't run synchronously, they run _concurrently_. Internally, they use similar mechanics to `Task` to yield time to your current thread whilst waiting for their own IO operations to complete. So, calling `operation.Run()` is the same as calling `await operation.RunAsync()` - you just don't need the rest of your code infected by `async`. When you want an operation not to run concurrently, but in parallel instead (i.e. queue the work to be run on the next available `ThreadPool` thread), you can call `operation.Fork()`. It supports fire-and-forget, so `operation.Fork().Run()` returns immediately, or you can await the result: ```c# var forkedOperation = from f in operation.Fork() // runs in parallel from r in f.Await // use the ForkIO to await the result select r; // This will yield the current thread to allow concurrency, whilst the forked // operation runs on another thread. var result = forkedOperation.Run(); ``` To lift an existing `Task` based function into these types you can just call: * `liftIO(Func> function)` * `liftIO(Func> function)` Which are both in the `Prelude` and allow an `IO` operation to be lifted into any monad that supports IO. `EnvIO` gives you access to the `CancellationToken`, `CancellationTokenSource`, and `SynchronizationConrext`. For example: ```c# var operation = from text in liftIO(env => File.ReadAllTextAsync(path, env.Token)) let lines = text.Split("\r\n").ToSeq() select lines; ``` We can then run that operation: ```c# Seq result = operation.Run(); ``` And we get a concrete `Seq` - not a `Task>`, but the operation ran concurrently. ## Leverage modern C# features The library has been held back by the need to support .NET Framework. As of now this library is .NET (formerly known as .NET Core) only. Instantly jumping to .NET 8.0 (which has Long Term Support). This opens up: static interface members (which allows the trait/ad-hoc polymorphism support to get a power-up) and collection initialisers for all of the immutable collections - amongst others. # New Features ## Higher-kinded polymorphism ### Introduction So, what is higher-kinded polymorphism? Think of a function like this: ```c# static Option AddOne(Option mx) => mx.Map(x => x + 1); ``` That's a function that takes an `Option` and leverages its `Map` function to add one to the value inside. The `Map` function makes it a 'Functor'. Functors map. But, if functors map, and all functors have a `Map` method, why can't I write: ```c# static F AddOne(F mx) where F : Functor => mx.Map(x => x + 1); ``` It's because we can only make the *lower-kind* polymorphic. For example, I can write a function like this: ```c# static Option Show(Option mx) => mx.Map(x => x.ToString()); ``` Where the lower-kind, the `A`, is parametric - but not the _higher-kind_ (the `F` in the previous example). You might think we could just do this with interfaces: ```c# interface Functor { Functor Map(Func f); } public class Option : Functor { // ... } public class Seq : Functor { // ... } ``` On the surface, that looks like we can then just accept `Functor` and call map on it. The problem is that we really shouldn't mix and match different types of functor (same with monads, applicatives, etc). We've lost the information on what's inside the functor. Every time we call `Map` on `Option` it stops being an `Option`, so we can't then call `Bind`, or any other useful functions, we're stuck doing `Map` forever. ## Traits C# has recently [introduced static interface members](https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/tutorials/static-virtual-interface-members). It allows us to create ['trait types'](https://en.wikipedia.org/wiki/Trait_%28computer_programming%29). Users of language-ext know this approach from the TypeClasses and ClassInstances technique. But, with static interface methods the approach has become much more elegant and usable. > language-ext is now .NET 8 only - mostly to leverage static interface methods So, now what we can do is define a really simple type: ```c# public interface K ``` This one type will change your life! Remember, we could't create a higher-kinded type like `F`, but we can create this: `K`. It has no members, it's a completely empty interface, we simply use this as a 'marker' for our types so that they can leverage higher-kinded polymorphism. If you look at any of the major types in language-ext `v5` you'll see the `K` type being used (`K` is short for 'Kind'): This is `Option`: ```c# public readonly struct Option : K { ... } ``` It locks its `F` type to be `Option` (notice the lack of an `A`, it's just `Option`). If you then go and look at `Option`, you'll notice it inherits some really interesting interfaces! ```c# public class Option : Monad` inherits from `K`, so we can just downcast it and call the `Option` implementation of `Map`. The `As()` method does the downcast from `K` to `Option`. > You may think downcasting is a bit risky here, but really nothing else should inherit from `K`. Doing so only makes sense for `Option`. I think the risk of a casting issue is close to zero. So, just to be clear. Every type, like `Option`, `Seq`, `Eff`, etc. has a sibling type of the same name with the last generic parameter removed. So, `Option` has a sibling type of `Option`, `Seq` has `Seq`, etc. Those sibling types implement the traits, like `Monad`, `Functor`, etc. And, because `Option`, `Seq`, etc. all inherit from `K` - where `TRAIT` is `Option`, `Seq`, `Eff`; this allows generic functions that have constriants like `where F : Functor` to 'find' the bespoke implementation. > Types like `Either`, that have multiple generic arguments, again just lose the last argument for their sibling type: `Either`. Invoking the trait functions directly isn't that elegant - although perfectly usable - so there's extension methods that work with all of the abstract traits. Here's the above `AddOne` method rewritten to use the `Map` extension instead: ```c# public static K AddOne(K mx) where F : Functor => mx.Map(x => x + 1); ``` So, let's try it out by calling it with a number of functors: ```c# K mx = AddOne(Option.Some(10)); K my = AddOne(Seq(1, 2, 3, 4)); K mz = AddOne(Fin.Succ(123)); ``` Note, the return types have the 'trait' baked in. So you know it's still an option, seq, fin, etc. > Without full support for higher-kinds (from the C# language team) we can't do better than that. However, there are extensions to help get back to the original type. Just call: `As()`. ```c# Option mx = AddOne(Option.Some(10)).As(); Seq my = AddOne(Seq(1, 2, 3, 4)).As(); Fin mz = AddOne(Fin.Succ(123)).As(); ``` You only need to do that when you 'realise the concrete type'. Because the trait (`Option`, `Seq`, `Fin`, etc.) is the type that inherits `Monad`, `Applicative`, `Traversable`, etc. (not `Option`, `Seq`, `Fin`, ) - you can just call their capabilities directly off the `K` value: ```c# Option mx = AddOne(Option.Some(10)) .Bind(x => Option.Some(x + 10)) .Map(x => x + 20) .As(); Seq mx = AddOne(Seq(1, 2, 3, 4)) .Bind(x => Seq(x + 10)) .Map(x => x + 20) .As(); ``` Just this capability alone has alowed me [to delete nearly 200,000 lines of generated code](https://github.com/louthy/language-ext/commit/c4c9df3b3b2fd9f0eaf0850742ce309948eea0d7). That is incredible! ************ CONTINUE HERE *************** ### Leverage modern C# features The library has been held back by the need to support .NET Framework. As of now this library is .NET (formally known as .NET Core) only. Instantly jumping to .NET 8.0 (which has Long Term Support). This opens up: static interface members (which allows the trait/ad-hoc polymorphism support to get a power-up) and collection initialisers for all of the immutable collections - amongst others. ## New Features - IO monads - Two new IO effect monads that have parametric errors - Transducers - All computation based monads rewritten to use transducers - Infinite recursion in monads - Streaming effects - By calling `many(stream)` in any monad or monad-transformer expression you instantly turn the monad into a stream. - Supported streams: `IAsyncEnumerable`, `IEnumerable`, `IObservable` - Auto-resource managment (`use` / `release`) - `Pure` / `Fail` monads - Allow easy lifting of pure and failure monadic values (a bit like `None`) - Lifting - Allow easy lifting of synchronous functions and asynchronous into computation based monads. - Improved guards - Nullable annotations - Everywhere! - Collection initialisers - `Seq xs = [1, 2, 3]` FTW!. - Monad transformers - Yes, for real: stackable, aliasable, monad-transformers! ## Breaking changes ### `netstandard2.0` no longer supported Version 5 of language-ext jumps straight to `net8.0` support. **Motivation** I held off for as long as I could, but there are lots of new C# features that this library can make use of (primarily static interfaces, but others too like collection initialisers); so it's time to leave .NET Framework behind and focus on .NET [Core]. **Impact** High (if you're still on .NET Framework) **Mitigation** Migrate your application to .NET Core ### `Seq1` made `[Obsolete]` A [previous attempt](https://github.com/louthy/language-ext/releases/tag/v4.0.2) to remove `Seq1` was paused due to [potential migration issues](https://github.com/louthy/language-ext/discussions/931). **Motivation** The plan to remove the `Seq1` singleton `Seq` constructor was announced a few years ago. I've taken this opportunity to make it obsolete as we now have collection initialisers and the previous reasons for the delay in making `Seq1` obsolete have subsided (a 3 year window should be enough!). **Impact** Low **Mitigation** Use `[x]` or `Seq.singleton(x)` ### `Error` no longer implicitly convertable from `String` It seemed like a good idea at the time, but can easily cause problems with `Error` carrying types like `Fin` (`Fin` in particular). To avoid the confusion I have made it an `explicit` conversion operation. **Impact** Low **Mitigation** Manually cast to `Error` where needed - or call `Error.New(string)` ### 'Trait' types now use static interface methods Before static interface methods existed, the technique was to rely on the non-nullable nature of structs to get access to 'static' methods (via interface based constraints), by calling `default(TRAIT_TYPE).StaticMethod()`. Language-ext has many of these 'trait types', like `Eq`, `HasCancel`, etc. They have all been updated to use `static abstract` methods. **Motivation** So, where before you might call: `default(EqA).Equals(x, y)` (where `EqA` is `struct, Eq`) - you now need to call `EqA.Equals(x, y)` (where `EqA` is `Eq`) . This is obviously much more elegant and removes the need for the `struct` constraint. **Impact** Medium - your code will throw up lots of 'Cannot access static method' errors. It is a fairly mechanical processes to fix them up. **Mitigation** If you have implemented any of these traits, as instances, then you'll need to implement these changes: * Remove the `struct` from any constraints (`where X : struct`) * Add `static` to trait method implementations * Any default `Inst` usages should be removed * The types can still be implemented as structs, so that doesn't need to change, but they can now be implemented with any instance type. ### The 'higher-kind' trait types have all been refactored The following types have all bee rewritten: `Monad`, `Functor`, `Applicative`, `Alternative`, `Foldable`, etc. **Motivation** The new static interfaces have opened up a more effective approach to higher-kinds in C#. Instead of doing as much as possible to retain the original types in methods like `Bind`, `Map`, `Apply`, etc. we now expect all types that need to leverage `Monad`, `Functor`, etc. to inherit `K`. For example, `Option` inherits `K`, `Seq` inehrits `K`, `Either` inherits `K, R>`. The `M` in `K` is the trait implementation. So, `Option` (no generic argument) would inherit `Monad` and `Monoid` types have been refactored The `Append` in `Semigroup` (which `Monoid` inherits) is now an instance method. Meaning you must derive your semigroup and monoidal types from `Monoid` to leverage its capabilities. The functions in the `TypeClass` static class have been moved to: `Monoid` and `Semigroup` static _module_ classes. `TypeClass.mappend` is now `Semigroup.append`, `TypeClass.mconcat` is now `Monoid.concat`, etc. Semigroup also defines `operator+` now. **Motivation** Monoids, like the other trait types, were set up work ad-hoc polymorphically. That is, we could build a `Monoid` instance for a type that we don't own. And, although we have now lost that capability, we have gained a much easier experience for working with monoidal types. For example, `Validation` is now just `Validation`. Previously, you'd have to specify the `MonoidFail` trait all the time because there was no way for C# to infer it. I suspect most people use the `Validation` variant with its built-in `Seq` of `Fail` results. Now it's just as easy to use any monoid. This obviously means that, with types that you don't own, they can't be monoidal directly. However, you can always wrap existing types with monoidal container: ```c# public readonly record struct MEnumerable(IEnumerable Items) : Monoid> { public MEnumerable Append(MEnumerable rhs) => new(Items.Concat(rhs.Items)); public static MEnumerable Empty => new(Enumerable.Empty()); } ``` This lifts an existing type into a monoid that you can then use with generic functions that expect a monoid. I think that although this is a little bit awkward, it's the scenario that happens then least; most of the time we have control over the type we want to be monoidal and so we can just inherit `Monoid`. **Impact** Medium - I'm not expecting mass adoption of the previous traits system, so it probably will have a low impact for most. However, monoids were probably one of the easier traits to use. **Mitigation** Any implementations of `Monoid` that you have, take the implementation and move the members into `YOUR_TYPE` and `Append` into a non-static method that takes a single argument rather than two (`this` is your first argument now). ### The static `TypeClass` class has been renamed `Trait` `LanguageExt.TypeClass` is effectively a Prelude for the trait functions, this has been renamed to `LanguageExt.Trait`. **Motivation** The name type-class comes from Haskell, which has been a massive influence on this library, however, I think the word 'trait' is more descriptive than 'type class', which is potentially a bit confusing to the average C# developer. **Impact** Low **Mitigation** Search and replace `TypeClass` for `Trait`. ### `Apply` extensions that use raw `Func` removed **Motivation** The applicative-functor `Apply` function is supposed to work on lifted functions (i.e. `M>` not the raw `Func`). I orignally provided variants that work with the raw `Func` for convenience, but really they're just `Map` by another name. **Impact** Medium **Mitigation** The new `Functor` trait gives all functor types a new variant of `Map` which takes the `Func` as the first argument and the functor value as the second (this differs from the existing handwritten `Map` methods which take `this` as the first argument and a `Func` as the second argument). That, along with new extension methods for `Func` have been added that curry the `Func` and then applies the map function. So, instead of: ```c# Func surround = (str, before, after) => $"{before} {str} {after}"; var mx = Some("Paul"); var my = Some("Mr."); var mz = Some("Louth"); surround.Apply(mx).Apply(my).Apply(mz); ``` Change the first `Apply` to `Map`: ```c# surround.Map(mx).Apply(my).Apply(mz); ``` Those of you that have used Haskell will recognise that pattern, as it's commonly used: ```haskell surround <$> mx <*> my <*> mz ``` ### Manually written `Sequence` extension methods have been removed (#1) The `Sequence` methods that follow this pattern have been removed: ```c# public static Fin> Sequence(this Lst ta, Func> f) => ... ``` **Motivation** Before being able to formulate the higher-kinds I had to manually write six `Sequence` methods and `Traverse` for every pair of monadic types. There are 450 of them in total. As you can imagine that's a real pain to develop and maintain. The other issue is that `Sequence` and `Traverse` don't work for monadic types that you build. You have to write those extension methods yourself for every pair. This is hardly approachable. There is a new trait type called `Traversable` that generalises traversals. The completely generic functions are available via: ```c# Traversable.traverse(...) Traversable.sequence(...) Traversable.sequenceA(...) Traversable.mapM(...) ``` They will work with *any* pair of traversble and applicatives. I have also added a `Traverse` method to every traversable type (`Seq`, `Option`, etc.) and those will work with *any* applicative. Which means if you build your own monads/applicative-functors that have implementations that derive from `Monad` and/or `Applicative` then they will automatically work with the language-ext traversable types. I have named the new version `Traverse` because that's really what it should have been called in the first place. So, as this is a big breaking changes release, I decided to bite the bullet and rename it. **Impact** High: `Sequence` is likely to be used a lot because it's so useful. Renaming it to `Traverse` will instantly cause your build to fail. The new version also returns a lifted value that you may need to convert. **Mitigation** Where your build fails due to `Sequence` you should change `Sequence` to `Traverse` and follow the call with `.As()` to lower the generic type: Before: ```c# var results = Seq(1,2,3).Sequence(x => x % 2 == 0 : Some(x) : None); ``` After: ```c# var results = Seq(1,2,3).Traverse(x => x % 2 == 0 : Some(x) : None).As(); ``` ### Manually written `Sequence` extension methods have been removed (#2) The `Sequence` methods that follow this pattern have been removed: ```c# public static Option> Sequence(this Seq> ta) => ... ``` **Motivation** The motivations are the same as for the other removal of `Sequence`. By removing it we get to use the generalised version which allows others to build monadic types that be composed with the language-ext types and be traversed. The difference with this removal and the last one is that there is no equivalent `Sequence` method added to the monadic types (like `Option`, `Seq`, etc.); The reason for this is that it's not possible for C# to pick up the nested generics correctly, so it's a waste of time writing them. You can still call `Traversable.sequence` and `Traversable.sequenceA` to do this manually, however it's much easier to call `ma.Traverse(identity)` which is isomorphic. **Mitigation** Before: ```c# var results = Seq(Some(1), Some(2), None).Sequence(); ``` After: ```c# var results = Seq(Some(1), Some(2), None).Traverse(identity).As(); ``` It's not quite as concise obviously, but it is completely generic and extensible which the previous one wasn't. You can also build your own extension methods for commonly used pairs of monads: ```c# public static Option> Sequence(this Seq> mx) => mx.Traverse(identity).As(); ``` That will restore the previous functionality. ### Manually written `Traverse` extension methods have been removed (#3) The `Traverse` methods that follow this pattern have been removed: ```c# public static Seq> Traverse(this Fin> ma, Func f) => ... ``` **Motivation** The motivations are the same as for the other removals of `Sequence`. By removing it we get to use the generalised version which allows others to build monadic types that be composed with the language-ext types and be traversed. Again, there will be no replacement for this as you can just use a combination of `Sequence` and `Map` to achieve the same goals. **Mitigation** Before: ```c# var results = Seq(Some(1), Some(2), None).Traverse(x => x * 2); ``` After: ```c# var results = Seq(Some(1), Some(2), None).Sequence().Map(x => x * 2).As(); ``` Again, it's not quite as concise, but it is completely generic and extensible which the previous one wasn't. You can also build your own extension methods for commonly used pairs of monads: ```c# public static Option> Traverse(this Seq> mx, Func f) => mx.Sequence().Map(f).As(); ``` That will restore the previous functionality. ### `ToComparer` doesn't exist on the `Ord` trait any more **Motivation** Because the trait types now use `static` methods, we can't now have a `ToComparer()` extension for the `Ord` type. Instead there's a class called `OrdComparer` that contains a singleton `IComparer` property called `Default`. **Impact** Low **Mitigation** Use `OrdComparer.Default` instead of `.ToComparer()`. ### Renamed `LanguageExt.ClassInstances.Sum` Renamed to `LanguageExt.ClassInstances.Addition` **Mitigation** There's a new type called `Sum` for use with transducers. **Impact** Low **Mitigation** Rename uses of `Sum` to `Addition` ### `Guard` has become `Guard` The `A` is never used, this just allows guards to work in LINQ by enabling the implementation of `SelectMany`. The benefit is that a number of guards can be placed together in a LINQ statement, where only one could before. * Impact: Zero unless you've written your own code to work with `Guard`. If you only ever used the `guard` Prelude function this will have no impact. ### Existing uses of `HasCancel` should be replaced with `HasIO` The basic runtime traits needed by the effect monads (`IO`, `Eff`, and `Aff`) has been expanded to `HasCancel`, `HasFromError`, and `HasSyncContext`. These can all be applied manually or you can use the `HasIO` trait which wraps all three traits into a single trait. Simply search and replace: `HasCancel` for `HasIO`. This will work for all existing `Eff` and `Aff` code. * `HasSyncContext` allows all `IO` and `Eff` monads to use the `post(effect)` function. This allows the running of an effect on original `SynchronizationContext` (needed for WPF and Windows Form applications). * `HasFromErrror` allows conversion of `Error` to `E`. The new `IO` monads have an parametric error type - and so this allows for exceptional errors and expecteded `Error` values to be converted to the parametric error type: `E`. ### `UnitsOfMeasaure` namespace converted to a static class The various utilty fields used for units (`seconds`, `cm`, `kg`, etc.) have been moved out of the `LanguageExt.Prelude` and into `LanguageExt.UnitsOfMeasure` static class. The unit types (`Length`, `Mass`, etc.) are now all in the `LanguageExt` namespace. * Impact: Low * Mitigation: * All usages of `using LanguageExt.UnitsOfMeasure` should be converted to `using static LanguageExt.UnitsOfMeasure` * Any uses of the unit-fields will also need `using static LanguageExt.UnitsOfMeasure` either globally or in each `cs` file. ### `Either` doesn't support `IEnumerable` any more This is part of preparing the library for future serialisation improvements. * Impact: Low * Mitigation: * If you used the feature for serialisation, build your own handler for whatever serialisation library you use. * If you use it in LINQ expressions, write your own extension method to convert the `Either` to an `IEnumerable` that supports ## `Either` 'bi' functions have their arguments flipped There were lots of methods like `BiMap`, `BiBind`, `BiFold` etc. where `Left` and `Right` were in the wrong order in the method argument list. So, I have flipped them. * Impact: Low * Mitigation: change the argument order of any usages ## Nullable (struct) extensions removed These just keep causing problems with resolution of common functions like `Map`, `Bind`, etc. The nullable Prelude remains. ## Support for `Tuple` and `KeyValuePair` removed ValueTuple support only from now on. ## Types removed outright Originally, I was going to just mark these as `[Obsolete]`. And, for a while, they were. But after over 1000 files changed with over 100,000 lines of code added or modified, I realised that maintaining these types (updating them to support nullable references and other fixups) was potentially doubling the effort I'd done up to that point. So, I have just deleted them outright. It is brutal, but it saved my sanity. I am very, very, very sorry that this will mean you have to fixup everything before you can work with language-ext `v5`. But unfortunately, I have to spread the load of this, as it was burning me to the ground! This is my '.NET Framework to .NET Core' moment. I realise that. And I an truly sorry to those that have to do the migration. Please make sure you have adequate time set aside for the migration. ### `Some` * Mitigtation: use nullable references instead ### `OptionUnsafe` * Mitigtation: use `Option` instead ### `OptionAsync` * Mitigtation: use `OptionT` instead ### `OptionNone`: * Mitigtation: use `Fail` instead ### `EitherUnsafe`: * Mitigtation: use `Either` instead ### `EitherLeft` * Mitigtation: use `Fail` instead ### `EitherRight`: * Mitigtation: use `Pure` instead ### `EitherAsync`: * Mitigtation: use `EitherT` instead ### `Try` * Mitigtation: use `Eff` ### `TryOption` * Mitigtation: use `OptionT` ### `TryAsync` * Mitigtation: use `Eff` ### `TryOptionAsync` * Mitigtation: use `OptionT` ### `Result` * Mitigtation: use `Fin` ### `OptionalResult` * Mitigtation: use `Fin` ### Async extensions for `Option` and `Either` * Mitigtation: use `ToIO()` to convert to the transformer variants with embedded `IO` ### `ExceptionMatch`, `ExceptionMatchAsync`, `ExceptionMatchOptionalAsync` * Mitigtations: * use effect monads with `@catch` * use `switch` expressions ## Libraries removed outright ### `LanguageExt.SysX` this was only needed to partition newer .NET Core code from .NET Standard. This has now been merged into `LanguageExt.Sys` ### `LanguageExt.CodeGen` Deprecated from now. To be replaced later by `LanguageExt.SourceGen`. Note, this library has always been standalone and can therefore continue to work without new versions being released. ### `LanguageExt.Transformers` No need for them now we have proper higher-kind support. # TODO * Traverse / Sequence - generic system using Transducers * Make TraverseParallel for Eff * ~~Find a way of resolving default implementations for classes now that we're using static interface methods (IL.cs).~~ * Test that resources are freed correctly in ResourceT when the result of Run is lazy * `bracket` * `EitherT`, `TryT` (derives `EitherT)`, `Try` (derives `TryT`) * `yieldAll`, `many`, and `repeat` for Pipes needs tail recursion support * `yieldAll`, `many` have been temporarily removed * `yieldAll` in Pipes has a temporary solution - need proper recursion strategy * ~~Use alpha and beta versioning like like: `5.0.0-alpha.1`, `5.0.0-alpha.2`, etc. So we can release `5.0.0` when done~~ * Make ForkIO.Await respect two cancellation tokens, the original and the one from the runner of Await * ~~Make Eff a ReaderM, ResourceM, etc. -- so we don't have to do so much manual lifting~~ * Overrides of Foldable for the sequence types (`Count()`, `Head()`, `Last()` etc.) * Make sure Index is used properly in collections this[] implementations (namely, from end!) * Make sure correct trait is used between MonoidK and Alternative -- the collections in particular should be MonoidK as the Combine usually concats the collections (rather than provides an Alternative). * Review `Prelude_Collections` * Write unit tests (generally!) * Write unit tests for index operator on lists and foldables [^1] * Add IComparisonOperators / IAdditionOperators / etc. to types * Make NewTypes support appropriate traits * Add Applicatice.Actions to every applicative that has an error state * Exception catching in Producer.merge * Implement `TraverseM` for each data-type (like the `Traverse` implementations) ================================================ FILE: Performance.md ================================================ # Perfomance benchmarks ** Machine spec ** BenchmarkDotNet=v0.11.5, OS=Windows 10.0.17134.1184 (1803/April2018Update/Redstone4) Intel Core i5-3470 CPU 3.20GHz (Ivy Bridge), 1 CPU, 4 logical and 4 physical cores Frequency=3117923 Hz, Resolution=320.7263 ns, Timer=TSC .NET Core SDK=3.1.100 [Host] : .NET Core 3.0.1 (CoreCLR 4.700.19.51502, CoreFX 4.700.19.51609), 64bit RyuJIT DefaultJob : .NET Core 3.0.1 (CoreCLR 4.700.19.51502, CoreFX 4.700.19.51609), 64bit RyuJIT ## Note References to `Sys.Coll.Imm` are Microsoft's `ImmutableCollections` library. * `Sys.Coll.Imm.List` is `System.Collections.Immutable.ImmutableList` * `Sys.Coll.Imm.Dictionary` is `System.Collections.Immutable.ImmutableDictionary` * `Sys.Coll.Imm.SortedDictionary` is `System.Collections.Immutable.ImmutableSortedDictionary` * `Sys.Coll.Imm.SortedSet` is `System.Collections.Immutable.ImmutableSortedSet` ## Lists ### Lists with value types Type | N | Add | RandomAccess | Iteration ----- | ----- | ----- | ----- | ----- `Sys.Coll.Imm.List` | `100` | `20 us` | `6,016 ns` | `1,021 ns` `Sys.Coll.Imm.List` | `1000` | `342 us` | `62,300 ns` | `32,942 ns` `Sys.Coll.Imm.List` | `10000` | `5,942 us` | `605,456 ns` | `443,482 ns` `Sys.Coll.Imm.List` | `100000` | `87,089 us` | `8,154,876 ns` | `6,146,402 ns` `LanguageExt.Lst` | `100` | `17 us` | `1,386 ns` | `1,213 ns` `LanguageExt.Lst` | `1000` | `273 us` | `13,455 ns` | `35,304 ns` `LanguageExt.Lst` | `10000` | `5,341 us` | `137,278 ns` | `471,614 ns` `LanguageExt.Lst` | `100000` | `62,340 us` | `1,696,505 ns` | `6,195,590 ns` `LanguageExt.Seq` | `100` | `2 us` | `638 ns` | `359 ns` `LanguageExt.Seq` | `1000` | `17 us` | `6,194 ns` | `3,518 ns` `LanguageExt.Seq` | `10000` | `205 us` | `61,358 ns` | `35,117 ns` `LanguageExt.Seq` | `100000` | `1,879 us` | `613,746 ns` | `351,036 ns` ### Lists with reference types Type | N | Add | RandomAccess | Iteration ----- | ----- | ----- | ----- | ----- `Sys.Coll.Imm.List` | `100` | `22 us` | `10,652 ns` | `1,013 ns` `Sys.Coll.Imm.List` | `1000` | `357 us` | `105,946 ns` | `33,410 ns` `Sys.Coll.Imm.List` | `10000` | `6,237 us` | `1,069,881 ns` | `448,148 ns` `Sys.Coll.Imm.List` | `100000` | `83,450 us` | `13,386,415 ns` | `6,260,259 ns` `LanguageExt.Lst` | `100` | `24 us` | `1,547 ns` | `3,561 ns` `LanguageExt.Lst` | `1000` | `349 us` | `14,446 ns` | `56,720 ns` `LanguageExt.Lst` | `10000` | `6,240 us` | `148,034 ns` | `747,441 ns` `LanguageExt.Lst` | `100000` | `75,870 us` | `1,986,228 ns` | `9,194,727 ns` `LanguageExt.Seq` | `100` | `3 us` | `888 ns` | `856 ns` `LanguageExt.Seq` | `1000` | `28 us` | `8,489 ns` | `8,537 ns` `LanguageExt.Seq` | `10000` | `367 us` | `84,479 ns` | `85,092 ns` `LanguageExt.Seq` | `100000` | `4,463 us` | `856,149 ns` | `853,446 ns` ## Maps ### Unsorted maps with value types Type | N | Add | ContainsKey | Iterate | RandomRemoval ----- | ----- | ----- | ----- | ----- | ----- `Sys.Coll.Imm.Dictionary` | `100` | `69 us` | `2,348 ns` | `10,647 ns` | `49,871 ns` `Sys.Coll.Imm.Dictionary` | `1000` | `1,125 us` | `60,126 ns` | `118,251 ns` | `894,815 ns` `Sys.Coll.Imm.Dictionary` | `10000` | `17,785 us` | `958,715 ns` | `1,164,131 ns` | `13,768,680 ns` `Sys.Coll.Imm.Dictionary` | `100000` | `298,988 us` | `17,859,368 ns` | `13,715,622 ns` | `242,798,528 ns` `LanguageExt.HashMap` | `100` | `15 us` | `2,042 ns` | `3,579 ns` | `14,065 ns` `LanguageExt.HashMap` | `1000` | `245 us` | `27,338 ns` | `38,108 ns` | `231,652 ns` `LanguageExt.HashMap` | `10000` | `5,023 us` | `354,385 ns` | `459,233 ns` | `3,810,743 ns` `LanguageExt.HashMap` | `100000` | `92,372 us` | `7,196,852 ns` | `7,432,346 ns` | `77,218,736 ns` ### Unsorted maps with reference types Type | N | Add | ContainsKey | Iterate | RandomRemoval ----- | ----- | ----- | ----- | ----- | ----- `Sys.Coll.Imm.Dictionary` | `100` | `90 us` | `6 us` | `20,352 ns` | `67 us` `Sys.Coll.Imm.Dictionary` | `1000` | `1,481 us` | `104 us` | `202,502 ns` | `1,085 us` `Sys.Coll.Imm.Dictionary` | `10000` | `23,336 us` | `1,504 us` | `2,076,426 ns` | `17,480 us` `Sys.Coll.Imm.Dictionary` | `100000` | `390,738 us` | `28,854 us` | `26,252,578 ns` | `291,299 us` `LanguageExt.HashMap` | `100` | `27 us` | `5 us` | `5,394 ns` | `24 us` `LanguageExt.HashMap` | `1000` | `404 us` | `74 us` | `62,036 ns` | `364 us` `LanguageExt.HashMap` | `10000` | `7,080 us` | `859 us` | `755,971 ns` | `5,773 us` `LanguageExt.HashMap` | `100000` | `138,283 us` | `17,573 us` | `12,049,411 ns` | `114,837 us` ### Sorted maps with value types Type | N | Add | ContainsKey | Iterate | RandomRemoval ----- | ----- | ----- | ----- | ----- | ----- `Sys.Coll.Imm.SortedDictionary` | `100` | `31 us` | `3,814 ns` | `6,140 ns` | `24,785 ns` `Sys.Coll.Imm.SortedDictionary` | `1000` | `539 us` | `78,032 ns` | `61,408 ns` | `449,868 ns` `Sys.Coll.Imm.SortedDictionary` | `10000` | `9,631 us` | `1,165,587 ns` | `617,841 ns` | `7,246,961 ns` `Sys.Coll.Imm.SortedDictionary` | `100000` | `203,653 us` | `19,308,708 ns` | `8,107,066 ns` | `149,215,888 ns` `LanguageExt.Map` | `100` | `19 us` | `2,805 ns` | `1,593 ns` | `13,458 ns` `LanguageExt.Map` | `1000` | `335 us` | `82,493 ns` | `16,605 ns` | `283,067 ns` `LanguageExt.Map` | `10000` | `6,871 us` | `1,272,800 ns` | `190,544 ns` | `5,016,271 ns` `LanguageExt.Map` | `100000` | `155,131 us` | `22,845,338 ns` | `3,707,917 ns` | `118,625,112 ns` ### Sorted maps with reference types Type | N | Add | ContainsKey | Iterate | RandomRemoval ----- | ----- | ----- | ----- | ----- | ----- `Sys.Coll.Imm.SortedDictionary` | `100` | `79 us` | `42 us` | `10,449 ns` | `59 us` `Sys.Coll.Imm.SortedDictionary` | `1000` | `1,333 us` | `706 us` | `103,676 ns` | `1,069 us` `Sys.Coll.Imm.SortedDictionary` | `10000` | `21,790 us` | `10,429 us` | `1,073,453 ns` | `17,848 us` `Sys.Coll.Imm.SortedDictionary` | `100000` | `393,881 us` | `164,318 us` | `13,811,388 ns` | `319,229 us` `LanguageExt.Map` | `100` | `72 us` | `44 us` | `2,112 ns` | `51 us` `LanguageExt.Map` | `1000` | `1,137 us` | `737 us` | `22,033 ns` | `896 us` `LanguageExt.Map` | `10000` | `19,178 us` | `10,676 us` | `252,819 ns` | `15,598 us` `LanguageExt.Map` | `100000` | `350,295 us` | `171,571 us` | `4,256,098 ns` | `301,462 us` ## Sets ### Unsorted sets with value types Type | N | Add | Contains | Iteration | RandomRemoval ----- | ----- | ----- | ----- | ----- | ----- `Sys.Coll.Imm.HashSet` | `100` | `52 us` | `3 us` | `11,975 ns` | `39,490 ns` `Sys.Coll.Imm.HashSet` | `1000` | `878 us` | `65 us` | `118,770 ns` | `702,550 ns` `Sys.Coll.Imm.HashSet` | `10000` | `14,230 us` | `1,033 us` | `1,245,373 ns` | `11,267,592 ns` `Sys.Coll.Imm.HashSet` | `100000` | `249,189 us` | `17,250 us` | `15,266,368 ns` | `207,453,344 ns` `LanguageExt.HashSet` | `100` | `13 us` | `2 us` | `3,366 ns` | `13,782 ns` `LanguageExt.HashSet` | `1000` | `224 us` | `23 us` | `34,842 ns` | `221,730 ns` `LanguageExt.HashSet` | `10000` | `4,520 us` | `320 us` | `458,131 ns` | `3,744,864 ns` `LanguageExt.HashSet` | `100000` | `85,851 us` | `5,648 us` | `6,528,404 ns` | `74,686,456 ns` ### Unsorted sets with reference types Type | N | Add | Contains | Iteration | RandomRemoval ----- | ----- | ----- | ----- | ----- | ----- `Sys.Coll.Imm.HashSet` | `100` | `66 us` | `8 us` | `26,331 ns` | `51 us` `Sys.Coll.Imm.HashSet` | `1000` | `1,008 us` | `117 us` | `261,931 ns` | `833 us` `Sys.Coll.Imm.HashSet` | `10000` | `15,992 us` | `1,575 us` | `2,660,329 ns` | `12,879 us` `Sys.Coll.Imm.HashSet` | `100000` | `271,162 us` | `25,866 us` | `30,410,462 ns` | `224,692 us` `LanguageExt.HashSet` | `100` | `21 us` | `6 us` | `3,665 ns` | `21 us` `LanguageExt.HashSet` | `1000` | `322 us` | `69 us` | `40,037 ns` | `327 us` `LanguageExt.HashSet` | `10000` | `5,761 us` | `820 us` | `488,167 ns` | `4,944 us` `LanguageExt.HashSet` | `100000` | `113,138 us` | `13,739 us` | `7,971,738 ns` | `94,890 us` ### Sorted sets with value types Type | N | Add | Contains | Iteration | RandomRemoval ----- | ----- | ----- | ----- | ----- | ----- `Sys.Coll.Imm.SortedSet` | `100` | `29 us` | `3 us` | `6,058 ns` | `23,319 ns` `Sys.Coll.Imm.SortedSet` | `1000` | `506 us` | `67 us` | `60,265 ns` | `425,159 ns` `Sys.Coll.Imm.SortedSet` | `10000` | `8,748 us` | `1,009 us` | `618,772 ns` | `6,950,004 ns` `Sys.Coll.Imm.SortedSet` | `100000` | `195,510 us` | `17,397 us` | `7,341,612 ns` | `145,175,136 ns` `LanguageExt.Set` | `100` | `17 us` | `3 us` | `1,575 ns` | `18,006 ns` `LanguageExt.Set` | `1000` | `306 us` | `70 us` | `16,889 ns` | `324,040 ns` `LanguageExt.Set` | `10000` | `6,199 us` | `1,101 us` | `192,437 ns` | `5,918,020 ns` `LanguageExt.Set` | `100000` | `147,726 us` | `20,893 us` | `2,980,587 ns` | `138,141,008 ns` ### Sorted sets with reference types Type | N | Add | Contains | Iteration | RandomRemoval ----- | ----- | ----- | ----- | ----- | ----- `Sys.Coll.Imm.SortedSet` | `100` | `72 us` | `40 us` | `10,645 ns` | `60 us` `Sys.Coll.Imm.SortedSet` | `1000` | `1,187 us` | `683 us` | `104,584 ns` | `1,022 us` `Sys.Coll.Imm.SortedSet` | `10000` | `19,371 us` | `9,842 us` | `1,071,031 ns` | `16,301 us` `Sys.Coll.Imm.SortedSet` | `100000` | `329,705 us` | `161,498 us` | `13,185,054 ns` | `288,019 us` `LanguageExt.Set` | `100` | `63 us` | `37 us` | `1,644 ns` | `47 us` `LanguageExt.Set` | `1000` | `1,001 us` | `629 us` | `17,305 ns` | `854 us` `LanguageExt.Set` | `10000` | `16,150 us` | `9,435 us` | `200,705 ns` | `14,227 us` `LanguageExt.Set` | `100000` | `307,030 us` | `144,921 us` | `2,834,565 ns` | `272,077 us` ================================================ FILE: README.md ================================================ ![lang-ext](https://raw.githubusercontent.com/louthy/language-ext/main/Images/banner.png) C# Functional Programming Language Extensions ============================================= This library uses and abuses the features of C# to provide a pure functional-programming framework that, if you squint, can look like extensions to the language itself. The desire here is to make programming in C# much more robust by helping the engineer's inertia flow in the direction of declarative and pure functional code rather than imperative. Using these techniques for large code-bases can bring tangible benefits to long-term maintenance by removing hidden complexity and by easing the engineer's and team's cognitive load. [![GitHub Discussions](https://raw.githubusercontent.com/louthy/language-ext/main/Images/discussions.svg)](https://github.com/louthy/language-ext/discussions) __Author on...__ * __Blog__: [Notes from a Small Functional Island](https://paullouth.com/) * __Bluesky__: [@paullouth.bsky.social](https://bsky.app/profile/paullouth.bsky.social) * __Mastodon:__ [@louthy@4four.org](https://4four.org/@louthy) * __Github ReadME project__: ['Functional programming is finally going mainstream'](https://github.com/readme/featured/functional-programming) ## Contents * [Reference](#reference) * [Nu-get package](#nu-get) * [Getting started](#getting-started) * [Prologue](#prologue) * [**Features**](#features) * [Functional effects and IO](#functional-effects-and-io) * [Atomic concurrency, shared state, and collections](#atomic-concurrency-and-collections) * [Immutable collections](#immutable-collections) * [Functional streams](#functional-streams) * [Optional and Alternative value monads](#optional-and-alternative-value-monads) * [State managing monads](#state-managing-monads) * [Parser combinators](#parser-combinators) * [Pretty: Produce nicely formatted text with smart layouts](#pretty) * [Differencing](#differencing) * [Traits](#traits) * [Value traits](#value-traits) * [Contributing & Code of Conduct](#contributing--code-of-conduct) ## Reference * [API Reference](https://louthy.github.io/language-ext/) * [Issues that contain documentation and examples](https://github.com/louthy/language-ext/issues?utf8=%E2%9C%93&q=is%3Aissue%20label%3A%22examples%20%2F%20documentation%22%20) ## Nu-get Nu-get package | Description ---------------|------------- [LanguageExt.Parsec](https://www.nuget.org/packages/LanguageExt.Parsec) | Port of the [Haskell parsec library](https://hackage.haskell.org/package/parsec) [LanguageExt.Streaming](https://www.nuget.org/packages/LanguageExt.Streaming) | A set of compositional streaming types [LanguageExt.FSharp](https://www.nuget.org/packages/LanguageExt.FSharp) | F# to C# interop package. Provides interop between the LanguageExt.Core types (like `Option`, `List` and `Map`) to the F# equivalents, as well as interop between core BCL types and F# [LanguageExt.Parsec](https://www.nuget.org/packages/LanguageExt.Parsec) | Port of the [Haskell parsec library](https://hackage.haskell.org/package/parsec) [LanguageExt.Rx](https://www.nuget.org/packages/LanguageExt.Rx) | Reactive Extensions support for various types within the Core [LanguageExt.Sys](https://www.nuget.org/packages/LanguageExt.Sys) | Provides an effects wrapper around the .NET System namespace making common IO operations pure and unit-testable ![lang-ext](https://raw.githubusercontent.com/louthy/language-ext/main/Images/lang-ext-warning.jpg) ## Getting started To use this library, simply include `LanguageExt.Core.dll` in your project or grab it from NuGet. It is also worth setting up some `global using` for your project. This is the full list that will cover all functionality and bring it into scope: ```C# global using LanguageExt; global using LanguageExt.Common; global using LanguageExt.Traits; global using LanguageExt.Effects; global using LanguageExt.Streaming; global using LanguageExt.Pretty; global using LanguageExt.Traits.Domain; global using static LanguageExt.Prelude; ``` A minimum, might be: ```c# global using LanguageExt; global using static LanguageExt.Prelude; ``` The namespace `LanguageExt` contains most of the core types; `LanguageExt.Prelude` contains the functions that bring into scope the prelude functions that behave like standalone functions in ML style functional programming languages; `LanguageExt.Traits` brings in the higher-kinded trait-types and many extensions; `LanguageExt.Common` brings in the `Error` type and predefined `Errors`. ## Prologue From C# 6 onwards we got the ability to treat static classes like namespaces. This means that we can use static methods without qualifying them first. That instantly gives us access to single term method names that look exactly like functions in ML-style functional languages. i.e. ```C# using static System.Console; WriteLine("Hello, World"); ``` This library tries to bring some of the functional world into C#. It won't always sit well with the seasoned C# OO programmer, especially the choice of `camelCase` names for a lot of functions and the seeming 'globalness' of a lot of the library. I can understand that much of this library is non-idiomatic, but when you think of the journey C# has been on, is "idiomatic" necessarily right? A lot of C#'s idioms are inherited from Java and C# 1.0. Since then we've had generics, closures, Func, LINQ, async... C# as a language is becoming more and more like a functional language on every release. In fact, the bulk of the new features are either inspired by or directly taken from features in functional languages. So perhaps it's time to move the C# idioms closer to the functional world's idioms? My goal with this library is very much to create a whole new community within the larger C# community. This community is not constrained by the dogma of the past or by the norms of C#. It understands that the OOP approach to programming has some problems and tries to address them head-on. And for those that say "just use F#" or "just use Haskell", sure, go do that. But it's important to remember that C# has a lot going for it: * Incredible investment into a state-of-the art compiler * Incredible tooling (Visual Studio and Rider) * A large ecosystem of open-source libraries * A large community of developers already using it * This is also very important for companies that hire engineers * It _is_ a functional programming language! It has first-class functions, lambdas, etc. * And with this library it has a functional-first _Base Class Library_ ### A note about naming One of the areas that's likely to get seasoned C# heads worked up is my choice of naming style. The intent is to try and make something that _feels_ like a functional language rather than following rules of naming conventions (mostly set out by the BCL). There is, however, a naming guide that will keep you in good stead while reading through this documentation: * Type names are `PascalCase` in the normal way * The types all have constructor functions rather than public constructors that you instantiate with `new`. They will always be `PascalCase`: ```C# Option x = Some(123); Option y = None; Seq items = Seq(1,2,3,4,5); List items = List(1,2,3,4,5); HashMap dict = HashMap((1, "Hello"), (2, "World")); Map dict = Map((1, "Hello"), (2, "World")); ``` * Any (non-type constructor) static function that can be used on its own by `using static LanguageExt.Prelude` are `camelCase`. ```C# var x = map(opt, v => v * 2); ``` * Any extension methods, or anything "fluent" are `PascalCase` in the normal way ```C# var x = opt.Map(v => v * 2); ``` Even if you disagree with this non-idiomatic approach, all of the `camelCase` static functions have fluent variants, so you never actually have to see the non-standard stuff. ## Features ### [Functional effects and IO](https://louthy.github.io/language-ext/LanguageExt.Core/Effects/index.html) | Location | Feature | Description | |----------|--------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `Core` | `IO` | [A synchronous and asynchronous side-effect: an IO monad](https://louthy.github.io/language-ext/LanguageExt.Core/Effects/IO/index.html) | | `Core` | `Eff` | [A synchronous and asynchronous side-effect with error handling](https://louthy.github.io/language-ext/LanguageExt.Core/Effects/Eff/Eff%20no%20runtime/index.html) | | `Core` | `Eff` | [Same as `Eff` but with an injectable runtime for dependency-injection: a unit testable IO monad](https://louthy.github.io/language-ext/LanguageExt.Core/Effects/Eff/Eff%20with%20runtime/index.html) | ### [Atomic concurrency and collections](https://louthy.github.io/language-ext/LanguageExt.Core/Concurrency/index.html) | Location | Feature | Description | |----------|------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------| | `Core` | `Atom` | [A lock-free atomically mutable reference for working with shared state](https://louthy.github.io/language-ext/LanguageExt.Core/Concurrency/Atom) | | `Core` | `Ref` | [An atomic reference to be used in the transactional memory system](https://louthy.github.io/language-ext/LanguageExt.Core/Concurrency/STM) | | `Core` | `AtomHashMap` | [An immutable `HashMap` with a lock-free atomically mutable reference](https://louthy.github.io/language-ext/LanguageExt.Core/Concurrency/AtomHashMap) | | `Core` | `AtomSeq` | [An immutable `Seq` with a lock-free atomically mutable reference](https://louthy.github.io/language-ext/LanguageExt.Core/Concurrency/AtomSeq) | | `Core` | `VectorClock` | [Understand distributed causality](https://louthy.github.io/language-ext/LanguageExt.Core/Concurrency/VectorClock) | | `Core` | `VersionVector` | [A vector clock with some versioned data](https://louthy.github.io/language-ext/LanguageExt.Core/Concurrency/VersionVector) | | `Core` | `VersionHashMap ` | [Distrubuted atomic versioning of keys in a hash-map](https://louthy.github.io/language-ext/LanguageExt.Core/Concurrency/VersionHashMap) | ### [Immutable collections](https://louthy.github.io/language-ext/LanguageExt.Core/Immutable%20Collections/index.html) | Location | Feature | Description | |----------|----------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `Core` | `Arr` | [Immutable array](https://louthy.github.io/language-ext/LanguageExt.Core/Immutable%20Collections/Arr/index.html) | | `Core` | `Seq` | [Lazy immutable list, evaluate at-most-once](https://louthy.github.io/language-ext/LanguageExt.Core/Immutable%20Collections/Seq/index.html) - very, very fast! | | `Core` | `Iterable` | [Wrapper around `IEnumerable` with support for traits](https://louthy.github.io/language-ext/LanguageExt.Core/Immutable%20Collections/Iterable/index.html) - enables the higher-kinded traits to work with enumerables. | | `Core` | `Lst` | [Immutable list](https://louthy.github.io/language-ext/LanguageExt.Core/Immutable%20Collections/List/index.html) - use `Seq` over `Lst` unless you need `InsertAt` | | `Core` | `Map` | [Immutable map](https://louthy.github.io/language-ext/LanguageExt.Core/Immutable%20Collections/Map/index.html) | | `Core` | `Map` | [Immutable map with Ord constraint on `K`](https://louthy.github.io/language-ext/LanguageExt.Core/Immutable%20Collections/Map/index.html) | | `Core` | `HashMap` | [Immutable hash-map](https://louthy.github.io/language-ext/LanguageExt.Core/Immutable%20Collections/HashMap/index.html) | | `Core` | `HashMap` | [Immutable hash-map with Eq constraint on `K`](https://louthy.github.io/language-ext/LanguageExt.Core/Immutable%20Collections/HashMap/index.html) | | `Core` | `Set` | [Immutable set](https://louthy.github.io/language-ext/LanguageExt.Core/Immutable%20Collections/Set/index.html) | | `Core` | `Set` | [Immutable set with Ord constraint on `A`](https://louthy.github.io/language-ext/LanguageExt.Core/Immutable%20Collections/Set/index.html) | | `Core` | `HashSet` | [Immutable hash-set](https://louthy.github.io/language-ext/LanguageExt.Core/Immutable%20Collections/HashSet/index.html) | | `Core` | `HashSet` | [Immutable hash-set with Eq constraint on `A`](https://louthy.github.io/language-ext/LanguageExt.Core/Immutable%20Collections/HashSet/index.html) | | `Core` | `Que` | [Immutable queue](https://louthy.github.io/language-ext/LanguageExt.Core/Immutable%20Collections/Queue/index.html) | | `Core` | `Stck` | [Immutable stack](https://louthy.github.io/language-ext/LanguageExt.Core/Immutable%20Collections/Stack/index.html) | ### [Functional streams](https://louthy.github.io/language-ext/LanguageExt.Streaming/index.html) | Location | Feature | Description | |----------|--------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `Streaming` | Pipes | [Connect reusable streaming components into a closed effect](https://louthy.github.io/language-ext/LanguageExt.Streaming/Pipes/) | | `Streaming` | `Sink` | [Entry point to a channel. Sinks receive values and propagate them through a channel](https://louthy.github.io/language-ext/LanguageExt.Streaming/Sink/) | | `Streaming` | `SinkT` | [As above but with effects](https://louthy.github.io/language-ext/LanguageExt.Streaming/SinkT/) | | `Streaming` | `Source` | [Stream of synchronous or asynchronous values depending on the construction. Values flow downstream and are aggregated with a reducer.](https://louthy.github.io/language-ext/LanguageExt.Streaming/Source/) | | `Streaming` | `SourceT` | [As above but with effects](https://louthy.github.io/language-ext/LanguageExt.Streaming/SourceT/) | | `Streaming` | `Conduit` | [Represents a channel with an internal queue. The conduit has a `Sink` and a `Source` allowing items to be posted into the conduit, co-mapped, mapped, and consumed.](https://louthy.github.io/language-ext/LanguageExt.Streaming/Conduit) | | `Streaming` | `ConduitT` | [As above but with effects: the conduit has a `SinkT` and a `SourceT` allowing items to be posted into the conduit, co-mapped, mapped, and consumed.](https://louthy.github.io/language-ext/LanguageExt.Streaming/ConduitT) | ### [Optional and alternative value monads](https://louthy.github.io/language-ext/LanguageExt.Core/Monads/Alternative%20Monads/index.html) | Location | Feature | Description | |----------|---------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `Core` | `Option` | [Option monad](https://louthy.github.io/language-ext/LanguageExt.Core/Monads/Alternative%20Monads/Option/index.html) | | `Core` | `OptionT` | [Option monad-transformer](https://louthy.github.io/language-ext/LanguageExt.Core/Monads/Alternative%20Monads/OptionT/index.html) | | `Core` | `Either` | [Right/Left choice monad](https://louthy.github.io/language-ext/LanguageExt.Core/Monads/Alternative%20Monads/Either/index.html) | | `Core` | `EitherT` | [Right/Left choice monad-transformer](https://louthy.github.io/language-ext/LanguageExt.Core/Monads/Alternative%20Monads/EitherT/index.html) | | `Core` | `Fin` | [`Error` handling monad, like `Either`](https://louthy.github.io/language-ext/LanguageExt.Core/Monads/Alternative%20Monads/Fin/index.html) | | `Core` | `FinT` | [`Error` handling monad-transformer](https://louthy.github.io/language-ext/LanguageExt.Core/Monads/Alternative%20Monads/FinT/index.html) | | `Core` | `Try` | [Exception handling monad](https://louthy.github.io/language-ext/LanguageExt.Core/Monads/Alternative%20Monads/Try/index.html) | | `Core` | `TryT` | [Exception handling monad-transformer](https://louthy.github.io/language-ext/LanguageExt.Core/Monads/Alternative%20Monads/TryT/index.html) | | `Core` | `Validation` | [Validation applicative and monad](https://louthy.github.io/language-ext/LanguageExt.Core/Monads/Alternative%20Monads/Validation/index.html) for collecting multiple errors before aborting an operation | | `Core` | `ValidationT` | [Validation applicative and monad-transformer](https://louthy.github.io/language-ext/LanguageExt.Core/Monads/Alternative%20Monads/ValidationT/index.html) | ### [State managing monads](https://louthy.github.io/language-ext/LanguageExt.Core/Monads/State%20and%20Environment%20Monads/index.html) | Location | Feature | Description | |----------|--------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `Core` | `Reader` | [Reader monad](https://louthy.github.io/language-ext/LanguageExt.Core/Monads/State%20and%20Environment%20Monads/Reader/Reader/index.html) | | `Core` | `ReaderT` | [Reader monad-transformer](https://louthy.github.io/language-ext/LanguageExt.Core/Monads/State%20and%20Environment%20Monads/Reader/ReaderT/index.html) | | `Core` | `Writer` | [Writer monad that logs to a `W` constrained to be a Monoid](https://louthy.github.io/language-ext/LanguageExt.Core/Monads/State%20and%20Environment%20Monads/Writer/Writer/index.html) | | `Core` | `WriterT` | [Writer monad-transformer](https://louthy.github.io/language-ext/LanguageExt.Core/Monads/State%20and%20Environment%20Monads/Writer/WriterT/index.html) | | `Core` | `State` | [State monad](https://louthy.github.io/language-ext/LanguageExt.Core/Monads/State%20and%20Environment%20Monads/State/State/index.html) | | `Core` | `StateT` | [State monad-transformer](https://louthy.github.io/language-ext/LanguageExt.Core/Monads/State%20and%20Environment%20Monads/State/StateT/index.html) | ### [Parser combinators](https://louthy.github.io/language-ext/LanguageExt.Parsec/index.html) | Location | Feature | Description | |----------|----------------|--------------------------------------------------------------------------------------------------------------------------------| | `Parsec` | `Parser` | [String parser monad and full parser combinators library](https://louthy.github.io/language-ext/LanguageExt.Parsec/index.html) | | `Parsec` | `Parser` | [Parser monad that can work with any input stream type](https://louthy.github.io/language-ext/LanguageExt.Parsec/index.html) | ### [Pretty](https://louthy.github.io/language-ext/LanguageExt.Core/Pretty/index.html) | Location | Feature | Description | |----------|----------|--------------------------------------------------| | `Core` | `Doc` | Produce nicely formatted text with smart layouts | ### [Differencing](https://louthy.github.io/language-ext/LanguageExt.Core/DataTypes/Patch/index.html) | Location | Feature | Description | |----------|-----------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `Core` | `Patch` | Uses patch-theory to efficiently calculate the difference (`Patch.diff(list1, list2)`) between two collections of `A` and build a patch which can be applied (`Patch.apply(patch, list)`) to one to make the other (think git diff). | ### [Traits](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/index.html) The traits are major feature of `v5`+ language-ext that makes generic programming with higher-kinds a reality. Check out Paul's [series on Higher Kinds](https://paullouth.com/higher-kinds-in-c-with-language-ext/) to get a deeper insight. | Location | Feature | Description | |----------|----------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `Core` | `Applicative` | [Applicative functor](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Applicative/index.html) | | `Core` | `Eq` | [Ad-hoc equality trait](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Eq/index.html) | | `Core` | `Fallible` | [Trait that describes types that can fail](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Fallible/index.html) | | `Core` | `Foldable` | [Aggregation over a structure](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Foldable/index.html) | | `Core` | `Functor` | [Functor `Map`](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Functor/index.html) | | `Core` | `Has` | [Used in runtimes to enable DI-like capabilities](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Has/index.html) | | `Core` | `Hashable` | [Ad-hoc has-a-hash-code trait](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Hashable/index.html) | | `Core` | `Local` | [Creates a local environment to run a computation ](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Local/index.html) | | `Core` | `Monad` | [Monad trait](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Monads/Monad/index.html) | | `Core` | `MonadT` | [Monad transformer trait](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Monads/MonadT/index.html) | | `Core` | `Monoid` | [A monoid is a type with an identity `Empty` and an associative binary operation `+`](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Monoid/index.html) | | `Core` | `MonoidK` | [Equivalent of monoids for working on higher-kinded types](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/MonoidK/index.html) | | `Core` | `Mutates` | [Used in runtimes to enable stateful operations](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Mutates/index.html) | | `Core` | `Ord` | [Ad-hoc ordering / comparisons](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Ord/index.html) | | `Core` | `Range` | [Abstraction of a range of values](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Range/index.html) | | `Core` | `Readable` | [Generalised Reader monad abstraction](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Readable/index.html) | | `Core` | `Semigroup` | [Provides an associative binary operation `+`](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Semigroup/index.html) | | `Core` | `SemigroupK` | [Equivalent of semigroups for working with higher-kinded types](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/SemigroupK/index.html) | | `Core` | `Stateful` | [Generalised State monad abstraction](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Stateful/index.html) | | `Core` | `Traversable` | [Traversable structures support element-wise sequencing of Applicative effects](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Traversable/index.html) | | `Core` | `Writable` | [Generalised Writer monad abstraction](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Writable/index.html) | ### [Value traits](https://louthy.github.io/language-ext/LanguageExt.Core/Traits/Domain/index.html) These work a little like type-aliasing but they impart semantic meaning and some common operators for the underlying value. | Location | Feature | Description | |----------|--------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `Core` | `DomainType` | Provides a mapping from `SELF` to an underlying representation: `REPR` | | `Core` | `Identifier ` | Identifiers (like IDs in databases: `PersonId` for example), they are equivalent to `DomaintType` with equality. | | `Core` | `VectorSpace` | Scalable values; can add and subtract self, but can only multiply and divide by a scalar. Can also negate. | | `Core` | `Amount ` | Quantities, such as the amount of money in USD on a bank account or a file size in bytes. Derives `VectorSpace`, `IdentifierLike`, `DomainType`, and is orderable (comparable). | | `Core` | `Locus ` | Works with space-like structures. Spaces have absolute and relative distances. Has an origin/zero point and derives `DomainType`, `IdentifierLike`, `AmountLike` and `VectorSpace`. `DISTANCE` must also be an `AmountLike`. | _These features are still a little in-flux as of 17th Oct 2024 - they may evolve, be renamed, or removed - but I like the idea!_ ## Further For some non-reference like documentation: * Paul's blog: [Notes from a Small Functional Island](https://paullouth.com/) does deep dives into the philosophy of FP and the inner-workings of language-ext. * [The wiki](https://github.com/louthy/language-ext/wiki) has some additional documentation, some might be a little out of date since the big `v5` refactor, but should give some good insights. ## Contributing & Code of Conduct If you would like to get involved with this project, please first read the [Contribution Guidelines](https://github.com/louthy/language-ext/blob/main/CONTRIBUTING.md) and the [Code of Conduct](https://github.com/louthy/language-ext/blob/main/CODE_OF_CONDUCT.md). ================================================ FILE: Samples/BlazorApp/BlazorApp.csproj ================================================ net10.0 enable enable ================================================ FILE: Samples/BlazorApp/Components/App.razor ================================================  ================================================ FILE: Samples/BlazorApp/Components/Layout/MainLayout.razor ================================================ @inherits LayoutComponentBase
An unhandled error has occurred. Reload 🗙
================================================ FILE: Samples/BlazorApp/Components/Layout/MainLayout.razor.css ================================================ .page { position: relative; display: flex; flex-direction: column; } main { flex: 1; } .sidebar { background-image: linear-gradient(180deg, rgb(5, 39, 103) 0%, #3a0647 70%); } .top-row { background-color: #f7f7f7; border-bottom: 1px solid #d6d5d5; justify-content: flex-end; height: 3.5rem; display: flex; align-items: center; } .top-row ::deep a, .top-row ::deep .btn-link { white-space: nowrap; margin-left: 1.5rem; text-decoration: none; } .top-row ::deep a:hover, .top-row ::deep .btn-link:hover { text-decoration: underline; } .top-row ::deep a:first-child { overflow: hidden; text-overflow: ellipsis; } @media (max-width: 640.98px) { .top-row { justify-content: space-between; } .top-row ::deep a, .top-row ::deep .btn-link { margin-left: 0; } } @media (min-width: 641px) { .page { flex-direction: row; } .sidebar { width: 250px; height: 100vh; position: sticky; top: 0; } .top-row { position: sticky; top: 0; z-index: 1; } .top-row.auth ::deep a:first-child { flex: 1; text-align: right; width: 0; } .top-row, article { padding-left: 2rem !important; padding-right: 1.5rem !important; } } #blazor-error-ui { background: lightyellow; bottom: 0; box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2); display: none; left: 0; padding: 0.6rem 1.25rem 0.7rem 1.25rem; position: fixed; width: 100%; z-index: 1000; } #blazor-error-ui .dismiss { cursor: pointer; position: absolute; right: 0.75rem; top: 0.5rem; } ================================================ FILE: Samples/BlazorApp/Components/Layout/NavMenu.razor ================================================  ================================================ FILE: Samples/BlazorApp/Components/Layout/NavMenu.razor.css ================================================ .navbar-toggler { appearance: none; cursor: pointer; width: 3.5rem; height: 2.5rem; color: white; position: absolute; top: 0.5rem; right: 1rem; border: 1px solid rgba(255, 255, 255, 0.1); background: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e") no-repeat center/1.75rem rgba(255, 255, 255, 0.1); } .navbar-toggler:checked { background-color: rgba(255, 255, 255, 0.5); } .top-row { height: 3.5rem; background-color: rgba(0,0,0,0.4); } .navbar-brand { font-size: 1.1rem; } .bi { display: inline-block; position: relative; width: 1.25rem; height: 1.25rem; margin-right: 0.75rem; top: -1px; background-size: cover; } .bi-house-door-fill-nav-menu { background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-house-door-fill' viewBox='0 0 16 16'%3E%3Cpath d='M6.5 14.5v-3.505c0-.245.25-.495.5-.495h2c.25 0 .5.25.5.5v3.5a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5v-7a.5.5 0 0 0-.146-.354L13 5.793V2.5a.5.5 0 0 0-.5-.5h-1a.5.5 0 0 0-.5.5v1.293L8.354 1.146a.5.5 0 0 0-.708 0l-6 6A.5.5 0 0 0 1.5 7.5v7a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5Z'/%3E%3C/svg%3E"); } .bi-plus-square-fill-nav-menu { background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-plus-square-fill' viewBox='0 0 16 16'%3E%3Cpath d='M2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2zm6.5 4.5v3h3a.5.5 0 0 1 0 1h-3v3a.5.5 0 0 1-1 0v-3h-3a.5.5 0 0 1 0-1h3v-3a.5.5 0 0 1 1 0z'/%3E%3C/svg%3E"); } .bi-list-nested-nav-menu { background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-list-nested' viewBox='0 0 16 16'%3E%3Cpath fill-rule='evenodd' d='M4.5 11.5A.5.5 0 0 1 5 11h10a.5.5 0 0 1 0 1H5a.5.5 0 0 1-.5-.5zm-2-4A.5.5 0 0 1 3 7h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5zm-2-4A.5.5 0 0 1 1 3h10a.5.5 0 0 1 0 1H1a.5.5 0 0 1-.5-.5z'/%3E%3C/svg%3E"); } .nav-item { font-size: 0.9rem; padding-bottom: 0.5rem; } .nav-item:first-of-type { padding-top: 1rem; } .nav-item:last-of-type { padding-bottom: 1rem; } .nav-item ::deep .nav-link { color: #d7d7d7; background: none; border: none; border-radius: 4px; height: 3rem; display: flex; align-items: center; line-height: 3rem; width: 100%; } .nav-item ::deep a.active { background-color: rgba(255,255,255,0.37); color: white; } .nav-item ::deep .nav-link:hover { background-color: rgba(255,255,255,0.1); color: white; } .nav-scrollable { display: none; } .navbar-toggler:checked ~ .nav-scrollable { display: block; } @media (min-width: 641px) { .navbar-toggler { display: none; } .nav-scrollable { /* Never collapse the sidebar for wide screens */ display: block; /* Allow sidebar to scroll for tall menus */ height: calc(100vh - 3.5rem); overflow-y: auto; } } ================================================ FILE: Samples/BlazorApp/Components/Pages/Counter.razor ================================================ @using LanguageExt @using static LanguageExt.Prelude; @page "/counter" @rendermode InteractiveServer Counter

Counter

Current count: @currentCount

@code { Atom currentCount = Atom(0); void IncrementCount() => incrementCount.Run(); IO incrementCount => currentCount.SwapIO(x => x + 1); } ================================================ FILE: Samples/BlazorApp/Components/Pages/Error.razor ================================================ @page "/Error" @using System.Diagnostics Error

Error.

An error occurred while processing your request.

@if (ShowRequestId) {

Request ID: @RequestId

}

Development Mode

Swapping to Development environment will display more detailed information about the error that occurred.

The Development environment shouldn't be enabled for deployed applications. It can result in displaying sensitive information from exceptions to end users. For local debugging, enable the Development environment by setting the ASPNETCORE_ENVIRONMENT environment variable to Development and restarting the app.

@code{ [CascadingParameter] private HttpContext? HttpContext { get; set; } private string? RequestId { get; set; } private bool ShowRequestId => !string.IsNullOrEmpty(RequestId); protected override void OnInitialized() => RequestId = Activity.Current?.Id ?? HttpContext?.TraceIdentifier; } ================================================ FILE: Samples/BlazorApp/Components/Pages/Home.razor ================================================ @page "/" Home

Hello, world!

Welcome to your new app. ================================================ FILE: Samples/BlazorApp/Components/Pages/Page.cs ================================================ using BlazorApp.Effects; using LanguageExt; using Microsoft.AspNetCore.Components; using static LanguageExt.Prelude; namespace BlazorApp.Components.Pages; public class Page : ComponentBase { protected override async Task OnInitializedAsync() => (await OnInitialized().RunAsync(AppRuntime.Current!)).SafeError(); protected override async Task OnAfterRenderAsync(bool firstRender) => (await OnAfterRender(firstRender).RunAsync(AppRuntime.Current!)).SafeError(); protected override async Task OnParametersSetAsync() => (await OnParametersSet().RunAsync(AppRuntime.Current!)).SafeError(); public override async Task SetParametersAsync(ParameterView parameters) => (await SetParameters(parameters).RunAsync(AppRuntime.Current!)).SafeError(); protected new virtual Eff OnInitialized() => liftEff(async _ => { await base.OnInitializedAsync(); return unit; }); protected new virtual Eff OnAfterRender(bool firstRender) => liftEff(async _ => { await base.OnAfterRenderAsync(firstRender); return unit; }); protected new virtual Eff OnParametersSet() => liftEff(async _ => { await base.OnParametersSetAsync(); return unit; }); protected new virtual Eff SetParameters(ParameterView parameters) => liftEff(async _ => { await base.SetParametersAsync(parameters); return unit; }); } ================================================ FILE: Samples/BlazorApp/Components/Pages/Weather.razor ================================================ @page "/weather" @using BlazorApp.Data @using BlazorApp.Effects @using LanguageExt @using static LanguageExt.Prelude @attribute [StreamRendering] @inherits Page Weather

Weather

This component demonstrates showing data.

@if (forecasts.IsEmpty) {

Loading...

} else { @foreach (var forecast in forecasts) { }
Date Temp. (C) Temp. (F) Summary
@forecast.Date.ToShortDateString() @forecast.Temperature.Celsius @forecast.Temperature.Fahrenheit @forecast.Summary
} @code { AtomSeq forecasts = AtomSeq(); protected override Eff OnInitialized() => from fs in Control.Weather.forecastNextFiveDays from _ in forecasts.SwapIO(_ => fs) select unit; } ================================================ FILE: Samples/BlazorApp/Components/Routes.razor ================================================  ================================================ FILE: Samples/BlazorApp/Components/_Imports.razor ================================================ @using System.Net.Http @using System.Net.Http.Json @using Microsoft.AspNetCore.Components.Forms @using Microsoft.AspNetCore.Components.Routing @using Microsoft.AspNetCore.Components.Web @using static Microsoft.AspNetCore.Components.Web.RenderMode @using Microsoft.AspNetCore.Components.Web.Virtualization @using Microsoft.JSInterop @using BlazorApp @using BlazorApp.Components ================================================ FILE: Samples/BlazorApp/Control/Weather.cs ================================================ using BlazorApp.Data; using BlazorApp.Effects; using BlazorApp.Effects.Interfaces; using LanguageExt; using LanguageExt.Sys; using LanguageExt.Sys.Traits; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace BlazorApp.Control; public static class Weather where RT : Has, TimeIO>, Has, RndIO> { /// /// Some example summaries /// static readonly Seq summaries = ["Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"]; /// /// Get the next five-days weather forecast /// public static Eff> forecastNextFiveDays => from d in dateNow from f in Range(1, 5).AsIterable() .Map(d.AddDays) .Traverse(forecast) select toSeq(f); /// /// Gets a weather forecast for a given date /// public static Eff forecast(DateOnly date) => from t in randomTemperature from s in randomSummary select new WeatherForecast(date, t, s); /// /// Get the date now /// static Eff dateNow => Time.now.Map(DateOnly.FromDateTime).As(); /// /// Generate a random temperature between -20 and 55 degrees celsius /// static Eff randomTemperature => Rnd.next(-20, 55).Map(c => c.Celsius()).As(); /// /// Generate a random summary /// static Eff randomSummary => Rnd.next(summaries.Length).Map(ix => summaries[ix]).As(); } ================================================ FILE: Samples/BlazorApp/Data/WeatherForecast.cs ================================================ using LanguageExt; namespace BlazorApp.Data; /// /// Weather forecast data /// /// Date /// Temperature for the day /// Summary text public record WeatherForecast( DateOnly Date, Temperature Temperature, string Summary); ================================================ FILE: Samples/BlazorApp/Effects/AppRuntime.cs ================================================ namespace BlazorApp.Effects; public static class AppRuntime { /// /// This will always point to the same runtime in this example, but it should be relatively /// obvious that switching this to a test-runtime, or any alternative with different effects /// implementations is how you would do mocking or DI. /// /// There may well be a more elegant way to make the runtime available to the Razor pages, but /// I have zero experience with Razer/Blazor, so I haven't figured it out yet. If you know, /// please let me know in the repo Discussions. Thanks! /// public static Runtime? Current; } ================================================ FILE: Samples/BlazorApp/Effects/Impl/RndImpl.cs ================================================ using BlazorApp.Effects.Interfaces; namespace BlazorApp.Effects.Impl; public class RndImpl : RndIO { public static readonly RndIO Default = new RndImpl(); public int Next(int min, int max) => Random.Shared.Next(min, max); public int Next(int max) => Random.Shared.Next(max); } ================================================ FILE: Samples/BlazorApp/Effects/Interfaces/RndIO.cs ================================================ namespace BlazorApp.Effects.Interfaces; public interface RndIO { /// /// Get a random integer between min and max, inclusive. /// int Next(int min, int max); /// /// Get a random integer up to max, inclusive. /// int Next(int max); } ================================================ FILE: Samples/BlazorApp/Effects/Rnd.cs ================================================ using BlazorApp.Effects.Interfaces; using LanguageExt; using LanguageExt.Traits; namespace BlazorApp.Effects; public static class Rnd where M : MonadIO where RT : Has { static readonly K trait = Has.ask; /// /// Get a random integer between min and max, inclusive. /// public static K next(int min, int max) => trait.Map(t => t.Next(min, max)); /// /// Get a random integer up to max, inclusive. /// public static K next(int max) => trait.Map(t => t.Next(max)); } public static class Rnd where RT : Has, RndIO> { /// /// Get a random integer between min and max, inclusive. /// public static Eff next(int min, int max) => +Rnd, RT>.next(min, max); /// /// Get a random integer up to max, inclusive. /// public static Eff next(int max) => +Rnd, RT>.next(max); } ================================================ FILE: Samples/BlazorApp/Effects/Runtime.cs ================================================ using BlazorApp.Effects.Interfaces; using LanguageExt; using LanguageExt.Sys.Traits; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace BlazorApp.Effects; /// /// Concrete runtime for this project /// public record Runtime(RuntimeInterfaces Interfaces) : Has, TimeIO>, Has, RndIO> { public Runtime(TimeIO timeIO, RndIO rndIO) : this(new RuntimeInterfaces(timeIO, rndIO)){} static K, TimeIO> Has, TimeIO>.Ask { get; } = liftEff(rt => rt.Interfaces.TimeIO); static K, RndIO> Has, RndIO>.Ask { get; } = liftEff(rt => rt.Interfaces.RndIO); } public record RuntimeInterfaces(TimeIO TimeIO, RndIO RndIO); ================================================ FILE: Samples/BlazorApp/Effects/SafeErrorExtensions.cs ================================================ using LanguageExt; using LanguageExt.Common; using LanguageExt.Traits; namespace BlazorApp.Effects; public static class SafeErrorExtensions { /// /// Catch exceptional errors and make them safe errors by wrapping up the exception /// in an `Inner` property and returning with 'There was an error' as the message. /// /// /// /// /// public static K SafeError(this K ma) where M : Fallible => ma.Catch(e => e.IsExceptional, e => Error.New("There was an error", e)); } ================================================ FILE: Samples/BlazorApp/Program.cs ================================================ using BlazorApp.Components; using BlazorApp.Effects; using BlazorApp.Effects.Impl; // Set a default runtime for the app AppRuntime.Current = new Runtime( LanguageExt.Sys.Live.Implementations.TimeIO.Default, RndImpl.Default ); var builder = WebApplication.CreateBuilder(args); // Add services to the container. builder.Services .AddRazorComponents() .AddInteractiveServerComponents(); var app = builder.Build(); // Configure the HTTP request pipeline. if (!app.Environment.IsDevelopment()) { app.UseExceptionHandler("/Error", createScopeForErrors: true); app.UseHsts(); } app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseAntiforgery(); app.MapRazorComponents() .AddInteractiveServerRenderMode(); app.Run(); ================================================ FILE: Samples/BlazorApp/Properties/launchSettings.json ================================================ { "$schema": "http://json.schemastore.org/launchsettings.json", "iisSettings": { "windowsAuthentication": false, "anonymousAuthentication": true, "iisExpress": { "applicationUrl": "http://localhost:58079", "sslPort": 44315 } }, "profiles": { "http": { "commandName": "Project", "dotnetRunMessages": true, "launchBrowser": true, "applicationUrl": "http://localhost:5124", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } }, "https": { "commandName": "Project", "dotnetRunMessages": true, "launchBrowser": true, "applicationUrl": "https://localhost:7126;http://localhost:5124", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } }, "IIS Express": { "commandName": "IISExpress", "launchBrowser": true, "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } } } } ================================================ FILE: Samples/BlazorApp/appsettings.Development.json ================================================ { "Logging": { "LogLevel": { "Default": "Information", "Microsoft.AspNetCore": "Warning" } } } ================================================ FILE: Samples/BlazorApp/appsettings.json ================================================ { "Logging": { "LogLevel": { "Default": "Information", "Microsoft.AspNetCore": "Warning" } }, "AllowedHosts": "*" } ================================================ FILE: Samples/BlazorApp/wwwroot/app.css ================================================ html, body { font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; } a, .btn-link { color: #006bb7; } .btn-primary { color: #fff; background-color: #1b6ec2; border-color: #1861ac; } .btn:focus, .btn:active:focus, .btn-link.nav-link:focus, .form-control:focus, .form-check-input:focus { box-shadow: 0 0 0 0.1rem white, 0 0 0 0.25rem #258cfb; } .content { padding-top: 1.1rem; } h1:focus { outline: none; } .valid.modified:not([type=checkbox]) { outline: 1px solid #26b050; } .invalid { outline: 1px solid #e50000; } .validation-message { color: #e50000; } .blazor-error-boundary { background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121; padding: 1rem 1rem 1rem 3.7rem; color: white; } .blazor-error-boundary::after { content: "An error has occurred." } .darker-border-checkbox.form-check-input { border-color: #929292; } ================================================ FILE: Samples/CardGame/Card.cs ================================================ using LanguageExt; namespace CardGame; /// /// Simple card type. Contains an index that can be converted to a textual /// representation of the card. /// public record Card(int Index) { public string Name => (Index % 13) switch { 0 => "Ace", 10 => "Jack", 11 => "Queen", 12 => "King", var ix => $"{ix + 1}" }; public string Suit => Index switch { < 13 => "Hearts", < 26 => "Clubs", < 39 => "Spades", < 52 => "Diamonds", _ => throw new NotSupportedException() }; public override string ToString() => $"{Name} of {Suit}"; public Seq FaceValues => (Index % 13) switch { 0 => [1, 11], // Ace 10 => [10], // Jack 11 => [10], // Queen 12 => [10], // King var x => [x + 1] }; } ================================================ FILE: Samples/CardGame/CardGame.csproj ================================================ Exe net10.0 enable enable ================================================ FILE: Samples/CardGame/Console.cs ================================================ using LanguageExt; using static LanguageExt.Prelude; namespace CardGame; /// /// Simple IO wrappers of Console /// public static class Console { public static IO emptyLine => lift(System.Console.WriteLine); public static IO writeLine(string line) => lift(() => System.Console.WriteLine(line)); public static IO readLine => lift(System.Console.ReadLine).Map(ln => ln ?? ""); public static IO readKey => IO.lift(System.Console.ReadKey) >> emptyLine; } ================================================ FILE: Samples/CardGame/Deck.cs ================================================ using LanguageExt; using LanguageExt.UnsafeValueAccess; using static LanguageExt.Prelude; namespace CardGame; /// /// Deck of cards /// public record Deck(Seq Cards) { public static Deck Empty = new ([]); /// /// Generate a randomly shuffled deck of cards /// public static Game shuffle => from deck in generate from _ in put(deck) select unit; /// /// Get the deck from the game-state /// public static Game deck => Game.gets(g => g.Deck); /// /// Return the number of cards remaining in the deck /// public static Game cardsRemaining => deck.Map(d => d.Cards.Count); /// /// Deal a card from the deck /// /// When the cards are exhausted the game will cancel public static Game deal => from d in deck from x in when(d.Cards.IsEmpty, Display.deckFinished) from c in Game.lift(d.Cards.Head) from _ in put(new Deck(d.Cards.Tail)) select c; /// /// Update the deck /// public static Game put(Deck deck) => Game.modify(g => g with { Deck = deck }); /// /// Generate a randomly shuffled deck of cards /// static IO generate => IO.lift(() => { var random = new Random((int)DateTime.Now.Ticks); var array = LanguageExt.List.generate(52, ix => new Card(ix)).ToArray(); random.Shuffle(array); return new Deck(array.ToSeqUnsafe()); }); public override string ToString() => Cards.ToFullArrayString(); } ================================================ FILE: Samples/CardGame/Display.cs ================================================ using LanguageExt; using static LanguageExt.Prelude; namespace CardGame; /// /// UI messages /// public static class Display { public static readonly Game introduction = Console.writeLine("Let's play..."); public static readonly Game askPlayerNames = Console.writeLine("Enter a player name, or just press enter complete"); public static readonly Game askPlayAgain = Console.writeLine("Play again (Y/N)"); public static readonly Game deckFinished = Console.writeLine("The deck is out of cards"); public static readonly Game cardsRemaining = Deck.cardsRemaining.Bind(remain => Console.writeLine($"{remain} cards remaining in the deck")); public static readonly Game bust = Console.writeLine("\tBust!"); public static Game playerExists(string name) => Console.writeLine($"Player '{name}' already exists") >> Console.writeLine("Please pick a unique name"); public static Game playerAdded(string name) => Console.writeLine($"'{name}' added to the game"); public static Game playerStates(Seq<(Player Player, PlayerState State)> players) => players.Traverse(p => playerState(p.Player, p.State)).Map(_ => unit).As(); public static Game playerState(Player player, PlayerState state) => state.StickState ? Console.writeLine($"{player.Name} {state.Cards}, possible scores {state.Scores} [STICK]") : Console.writeLine($"{player.Name} {state.Cards}, possible scores {state.Scores}"); public static Game winners(Seq<(Player Player, int Score)> winners) => winners switch { [] => everyoneIsBust, [var p] => Console.writeLine($"{p.Player.Name} is the winner with {p.Score}!"), var ps => Console.writeLine($"{ps.Map(p => p.Player.Name).ToFullString()} have won with {ps[0].Score}!") }; public static Game everyoneIsBust => Console.writeLine("Everyone's bust!"); public static readonly Game askStickOrTwist = Player.current.Bind(player => Console.writeLine($"{player.Name}, stick or twist? (S/T)")); public static readonly Game stickOrTwistBerate = Console.writeLine("'S' key for stick, 'T' for twist!"); public static Game showCard(Card card) => Console.writeLine($"\t{card}"); public static Game showCardsAndScores(Seq cards, Seq scores, int highScore) => Console.writeLine($"\t{cards}, possible scores {scores}, high-score: {highScore}"); } ================================================ FILE: Samples/CardGame/Game.Monad/Game.Extensions.cs ================================================ using LanguageExt; using LanguageExt.Traits; namespace CardGame; public static class GameExtensions { extension(K ma) { public Game As() => (Game)ma; /// /// Run the Game transformer down to the IO monad /// public IO> Run(GameState state) => ma.As().runGame.Run(state).As().Run().As(); public Game SelectMany(Func> bind, Func project) => ma.As().SelectMany(bind, project); public static Game operator + (K lhs) => (Game)lhs; public static Game operator >> (K lhs, Lower _) => (Game)lhs; } public static Game SelectMany( this K ma, Func> bind, Func project) => MonadIO.liftIO(ma.As()).SelectMany(bind, project); public static Game SelectMany( this IO ma, Func> bind, Func project) => MonadIO.liftIO(ma).SelectMany(bind, project); public static Game SelectMany( this Pure ma, Func> bind, Func project) => Game.Pure(ma.Value).SelectMany(bind, project); } ================================================ FILE: Samples/CardGame/Game.Monad/Game.Module.cs ================================================ using LanguageExt; using static LanguageExt.Prelude; namespace CardGame; public partial class Game { public static Game Pure(A value) => Game.Pure(value); /// /// Cached unit returning Game monad /// public static readonly Game unitM = Game.Pure(unit); /// /// Use this to cancel the game /// /// Represents a None state in the embedded OptionT transformer. public static readonly Game cancel = lift(Option.None); /// /// Get the game-state /// public static Game state => new (StateT.get, GameState>()); /// /// Return true if the game is still active /// public static Game isGameActive => from non in nonActivePlayersState from res in non.Exists(p => p.State.Has21 || p.State.StickState) ? activePlayersState.Map(ps => ps.Count > 0) : activePlayersState.Map(ps => ps.Count > 1) select res; /// /// The current high-score /// public static Game currentHighScore => playersState.Map(ps => ps.Filter(p => !p.State.IsBust) .Choose(p => p.State.MaximumNonBustScore) .Max(0)); /// /// Get the player's state from the StateT monad-transformer /// public static Game> playersState => state.Map(s => s.State).Map(gs => gs.AsIterable().ToSeq()); /// /// Get the player's that are still playing /// public static Game> activePlayersState => playersState.Map(ps => ps.Filter(p => p.State.StillInTheGame())); /// /// Get the player's that are still playing /// public static Game> nonActivePlayersState => playersState.Map(ps => ps.Filter(p => !p.State.StillInTheGame())); /// /// Get the list of players from the StateT monad-transformer /// public static Game> players => playersState.Map(ps => ps.Map(p => p.Player)); /// /// Get the player's that are still playing /// public static Game> activePlayers => activePlayersState.Map(ps => ps.Map(p => p.Player).Strict()); /// /// Initialise the player's state /// public static Game initPlayers => modifyPlayers(kv => kv.Map(_ => PlayerState.Zero)); /// /// Return the winners of the game (there may be multiple winners!) /// public static Game> winners => from ps in playersState select ps.Choose(p => p.State.MaximumNonBustScore.Map(score => (p.Player, Score: score))) .OrderByDescending(p => p.Score) .Select(Seq) .Reduce((ss, sp) => (from s in ss from p in sp select s.Score == p.Score).ForAll(x => x) ? ss + sp : ss); /// /// Discover if a player exists /// public static Game playerExists(string name) => playerExists(new Player(name)); /// /// Discover if a player exists /// public static Game playerExists(Player player) => state.Map(s => s.State) .Map(s => s.Find(player).IsSome); /// /// Add a player to the game /// public static Game addPlayer(string name) => iff(playerExists(name), Then: Display.playerExists(name), Else: from _1 in modifyPlayers(s => s.Add(new Player(name), PlayerState.Zero)) from _2 in Display.playerAdded(name) select unit) .As(); /// /// Lazy wrapper /// public static Game lazy(Func> f) => unitM.Bind(_ => f()); /// /// Lift an option into the Game - None will cancel the game /// public static Game lift(Option ma) => Game.Lift(ma); /// /// Lift an IO operation into the Game /// public static Game liftIO(IO ma) => Game.LiftIO(ma); /// /// Map the game state to a new value /// public static Game gets(Func f) => new (StateT.gets, GameState, A>(f)); /// /// Map the game state to a new value and update it in the monad /// public static Game modify(Func f) => new (StateT.modify, GameState>(f)); /// /// Modify the player map /// public static Game modifyPlayers(Func, HashMap> f) => modify(s => s with { State = f(s.State) }); } ================================================ FILE: Samples/CardGame/Game.Monad/Game.Monad.cs ================================================ using LanguageExt; using LanguageExt.Traits; namespace CardGame; public partial class Game : Deriving.Monad>>, Deriving.Stateful>, GameState>, MonadIO { public static K>, A> Transform(K fa) => fa.As().runGame; public static K CoTransform(K>, A> fa) => new Game(fa.As()); public static K LiftIO(IO ma) => new Game(StateT.liftIO, A>(ma)); } ================================================ FILE: Samples/CardGame/Game.Monad/Game.cs ================================================ using LanguageExt; using LanguageExt.Traits; namespace CardGame; public record Game(StateT, A> runGame) : K { public static Game Pure(A x) => new(StateT, A>.Pure(x)); public static Game None => new(StateT, A>.Lift(OptionT.None())); public static Game Lift(Option mx) => new(StateT, A>.Lift(OptionT.lift(mx))); public static Game LiftIO(IO mx) => new(StateT, A>.LiftIO(mx)); public Game Map(Func f) => this.Kind().Map(f).As(); public Game Select(Func f) => this.Kind().Map(f).As(); public Game Bind(Func> f) => this.Kind().Bind(f).As(); public Game Bind(Func> f) => this.Kind().Bind(f).As(); public Game SelectMany(Func> bind, Func project) => Bind(a => bind(a).Map(b => project(a, b))); public Game SelectMany(Func> bind, Func project) => SelectMany(a => MonadIO.liftIO(bind(a)), project); public Game SelectMany(Func> bind, Func project) => SelectMany(a => MonadIO.liftIO(bind(a).As()), project); public Game SelectMany(Func> bind, Func project) => Map(a => project(a, bind(a).Value)); public static implicit operator Game(Pure ma) => Pure(ma.Value); public static implicit operator Game(IO ma) => Game.liftIO(ma); public static implicit operator Game(Option ma) => Game.lift(ma); /* public static Game operator >>(Game ma, K mb) => ma.Bind(_ => mb); public static Game operator >>(Game ma, K mb) => ma.Bind(x => mb.Map(_ => x)); public static Game operator >>(Game ma, K mb) => ma.Bind(_ => mb.As()); public static Game operator >>(Game ma, K mb) => ma.Bind(x => mb.As().Map(_ => x));*/ } ================================================ FILE: Samples/CardGame/Game.cs ================================================ using LanguageExt; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace CardGame; /// /// Pontoon / Vingt-Un / 21 /// public partial class Game { /// /// Play the game! /// public static Game play => Display.askPlayerNames >> enterPlayerNames >> Display.introduction >> Deck.shuffle >> playHands >> lower; /// /// Ask the users to enter their names until `enterPlayerName` returns `false` /// static Game enterPlayerNames => +when(enterPlayerName, lazy(() => enterPlayerNames)); /// /// Wait for the user to enter the name of a player, then add them to the game /// static Game enterPlayerName => from name in Console.readLine from _ in when(notEmpty(name), addPlayer(name)) select notEmpty(name); /// /// Play many hands until the players decide to quit /// static Game playHands => from _ in initPlayers >> playHand >> Display.askPlayAgain from key in Console.readKey from __ in when(key.Key == ConsoleKey.Y, playHands) select unit; /// /// Play a single hand /// static Game playHand => dealHands >> playRound >> gameOver >> Display.cardsRemaining >> lower; /// /// Deal the initial cards to the players /// static Game dealHands => Players.with(players, dealHand); /// /// Deal the two initial cards to a player /// static Game dealHand => from cs in dealCard >> dealCard from player in Player.current from state in Player.state from _ in Display.playerState(player, state) select unit; /// /// Deal a single card /// static Game dealCard => Deck.deal >> Player.addCard >> lower; /// /// For each active player, check if they want to stick or twist /// Keep looping until the game ends /// static Game playRound => when(isGameActive, from _ in Players.with(activePlayers, stickOrTwist) from r in playRound select r) .As(); /// /// Ask the player if they want to stick or twist, then follow their instruction /// static Game stickOrTwist => when(isGameActive, from _ in Display.askStickOrTwist >> Player.showCards from key in Console.readKey from __ in key.Key switch { ConsoleKey.S => Player.stick, ConsoleKey.T => twist, _ => stickOrTwistBerate } select unit) .As(); /// /// Player wants to twist /// static Game twist => from card in Deck.deal from _ in Player.addCard(card) >> Display.showCard(card) >> when(Player.isBust, Display.bust) select unit; /// /// Berate the user for not following instructions! /// static K stickOrTwistBerate => Display.stickOrTwistBerate >> stickOrTwist; /// /// Show the game over summary /// static Game gameOver => from ws in winners from ps in playersState from _ in Display.winners(ws) >> Display.playerStates(ps) select unit; } ================================================ FILE: Samples/CardGame/GameState.cs ================================================ using LanguageExt; using static LanguageExt.Prelude; namespace CardGame; /// /// The game's internal state /// public record GameState(HashMap State, Deck Deck, Option CurrentPlayer) { public static readonly GameState Zero = new ([], Deck.Empty, None); } ================================================ FILE: Samples/CardGame/Player.cs ================================================ using LanguageExt; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace CardGame; public record Player(string Name) { public override string ToString() => Name; /// /// Run a computation in a `Player` local context /// /// /// /// /// public static Game with(Player player, Game ma) => from cp in Game.gets(s => s.CurrentPlayer) from _1 in setCurrent(player) from rs in ma >> setCurrent(cp) select rs; /// /// Get the current player. If there isn't one, then `Game.cancel` is raised /// public static Game current => from p in Game.gets(s => s.CurrentPlayer) from r in Game.lift(p) select r; /// /// Set the current player /// public static Game setCurrent(Player player) => Game.modify(s => s with { CurrentPlayer = player }).As(); /// /// Set the current player /// public static Game setCurrent(Option player) => Game.modify(s => s with { CurrentPlayer = player }).As(); /// /// Get a player's state from the StateT monad-transformer /// public static Game state => from pl in current from s1 in Game.state.Map(s => s.State) from s2 in Game.lift(s1.Find(pl)) select s2; /// /// Show the player's cards /// public static Game showCards => from state in state from score in Game.currentHighScore from cards in Display.showCardsAndScores(state.Cards, state.Scores, score) select unit; /// /// Modify a player's game-state stored in the StateT monad-transformer /// static Game modify( Func f) => from p in current from s in state from _ in Game.modifyPlayers(s1 => s1.SetItem(p, f(s))) select unit; /// /// Add a card to a player's state /// public static Game addCard(Card card) => modify(p => p.AddCard(card)); /// /// Set a player's state to stick /// public static Game stick => modify(p => p.Stick()); /// /// Gets whether the player is bust or not /// public static Game isBust => state.Map(p => p.IsBust); } ================================================ FILE: Samples/CardGame/PlayerState.cs ================================================ using LanguageExt; using static LanguageExt.Prelude; namespace CardGame; /// /// Holds a running sequence of dealt cards and the stick-status /// public record PlayerState(Seq Cards, bool StickState) { public static readonly PlayerState Zero = new ([], false); public PlayerState AddCard(Card card) => this with { Cards = Cards.Add(card) }; public PlayerState Stick() => this with { StickState = true }; public Seq Scores => Cards.Map(c => c.FaceValues) .Fold(Seq(Seq()), (s, vs) => from x in s from v in vs select x.Add(v)) .Map(s => s.Sum()) .Distinct() .OrderBy(s => s) .AsIterable() .ToSeq(); public bool StillInTheGame() => !Scores.Exists(s => s == 21) && !StickState && !IsBust; public Option MaximumNonBustScore => Scores.Filter(s => s <= 21).Last; public bool Has21 => Scores.Exists(s => s == 21); public bool IsBust => Scores.ForAll(s => s > 21); } ================================================ FILE: Samples/CardGame/Players.cs ================================================ using LanguageExt; using static LanguageExt.Prelude; namespace CardGame; /// /// Bulk management of players /// public static class Players { /// /// For each player in a collection of players: /// /// * Make into the current player /// * Rub the `ma` computation in that context /// * Reset the current player /// /// /// Ignores the results /// public static Game with(Game> playersM, Game ma) => playersM.Bind(ps => with(ps, ma)) .IgnoreF() .As(); /// /// For each player in a collection of players: /// /// * Make into the current player /// * Rub the `ma` computation in that context /// * Reset the current player /// /// Return a sequence of results, one for each player /// public static Game with(Seq players, Game ma) => players.TraverseM(p => Player.with(p, ma)) .Map(_ => unit) .As(); /// /// For each player in a collection of players: /// /// * Make into the current player /// * Rub the `ma` computation in that context /// * Reset the current player /// /// /// /// Return a sequence of results, one for each player /// public static Game> map(Game> playersM, Game ma) => playersM.Bind(ps => map(ps, ma)); /// /// For each player in a collection of players: /// /// * Make into the current player /// * Rub the `ma` computation in that context /// * Reset the current player /// /// /// /// Return a sequence of results, one for each player /// public static Game> map(Seq players, Game ma) => players.TraverseM(p => Player.with(p, ma)) .As(); } ================================================ FILE: Samples/CardGame/Program.cs ================================================ using CardGame; using LanguageExt; using Console = System.Console; // Play the game! Game.play .Run(GameState.Zero) // Runs the Game .Run() // Runs the IO .Ignore(); // Discard the result Console.WriteLine("GAME OVER"); ================================================ FILE: Samples/CreditCardValidation/Control/CreditCard.cs ================================================ // This is a toy sample that demonstrates credit-card validation. It shouldn't be considered complete, // it simply demonstrates usage of the Validation applicative/monad for capturing multiple errors when // doing complex validation. // // For more information, see Paul Louth's blog article on Validation: // https://paullouth.com/higher-kinds-in-c-with-language-ext-part-5-validation/ using System.Numerics; using CreditCardValidation.Data; using LanguageExt; using LanguageExt.Common; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace CreditCardValidation.Control; public static class CreditCard { public static Validation Validate(string cardNo, string expiryDate, string cvv) => fun(CreditCardDetails.Make) .Map(ValidateCardNumber(cardNo)) .Apply(ValidateExpiryDate(expiryDate)) .Apply(ValidateCVV(cvv)) .As(); static Validation ValidateCardNumber(string cardNo) => (ValidateAllDigits(cardNo), ValidateLength(cardNo, 16)) .Apply((digits, _) => digits.ToSeq()) .Bind(ValidateLuhn) .Map(CardNumber.FromUnsafe) .As() .MapFail(e => Error.New("card number not valid", e)); static Validation ValidateExpiryDate(string expiryDate) => expiryDate.Split('\\', '/', '-', ' ') switch { [var month, var year] => from my in ValidateInt(month) & ValidateInt(year) from exp in Expiry.From(my[0], my[1]).ToValidation() from _ in ValidateInRange(exp, Expiry.NextTenYears) select exp, _ => Fail(Error.New($"expected expiry-date in the format: MM/YYYY, but got: {expiryDate}")) }; static Validation ValidateInRange(A value, Range range) where A : IComparisonOperators => range.InRange(value) ? Pure(value) : Fail(Error.New($"expected value in range of {range.From} to {range.To}, but got: {value}")); static Validation ValidateCVV(string cvv) => fun((code, _) => new CVV(code)) .Map(ValidateInt(cvv).MapFail(_ => Error.New("CVV code should be a number"))) .Apply(ValidateLength(cvv, 3).MapFail(_ => Error.New("CVV code should be 3 digits in length"))) .As(); static Validation> ValidateAllDigits(string value) => value.AsIterable() .Traverse(CharToDigit) .As(); static Validation ValidateInt(string value) => ValidateAllDigits(value).Map(_ => int.Parse(value)); static Validation ValidateLength(string value, int length) => ValidateLength(value.AsIterable(), length) .Map(_ => value); static Validation> ValidateLength(K fa, int length) where F : Foldable => fa.Count == length ? Pure(fa) : Fail(Error.New($"expected length to be {length}, but got: {fa.Count}")); static Validation CharToDigit(char ch) => ch is >= '0' and <= '9' ? Pure(ch - '0') : Fail(Error.New($"expected a digit, but got: {ch}")); static Validation> ValidateLuhn(Seq digits) { var checkDigit = 0; for (var i = digits.Length - 2; i >= 0; --i) { checkDigit += ((i & 1) is 0) switch { true => digits[i] > 4 ? digits[i] * 2 - 9 : digits[i] * 2, false => digits[i] }; } return (10 - checkDigit % 10) % 10 == digits.Last ? Pure(digits) : Fail(Error.New("invalid card number")); } } ================================================ FILE: Samples/CreditCardValidation/CreditCardValidation.csproj ================================================  Exe net10.0 enable enable ================================================ FILE: Samples/CreditCardValidation/Data/Base12.cs ================================================ // This is a toy sample that demonstrates credit-card validation. It shouldn't be considered complete, // it simply demonstrates usage of the Validation applicative/monad for capturing multiple errors when // doing complex validation. // // For more information, see Paul Louth's blog article on Validation: // https://paullouth.com/higher-kinds-in-c-with-language-ext-part-5-validation/ using LanguageExt; using LanguageExt.Common; using LanguageExt.Traits.Domain; namespace CreditCardValidation.Data; /// /// Base 12 number /// /// /// Handy for representing years and months /// /// Most significant digits /// Least significant digits public readonly struct Base12(int MostSig, uint LeastSig) : DomainType, Amount { public static readonly Base12 Zero = new (0, 0); public static Base12 From(int value) => new (value / 12, (uint)(Math.Abs(value) % 12)); public static Fin From((int MostSig, uint LeastSig) repr) => repr.LeastSig > 11 ? Error.New("invalid base-12 number") : new Base12(repr.MostSig, repr.LeastSig); public (int MostSig, uint LeastSig) To() => (MostSig, LeastSig); public int ToBase10() => MostSig * 12 + (int)LeastSig; public static bool operator ==(Base12 left, Base12 right) => left.Equals(right); public static bool operator !=(Base12 left, Base12 right) => !(left == right); public static Base12 operator -(Base12 value) => value.To() switch { var (ms, ls) => new Base12(-ms, ls) }; public static Base12 operator +(Base12 left, Base12 right) { var (lm, ll) = left.To(); var (rm, rl) = right.To(); var most = lm + rm; var least = ll + rl; (var carry, least) = least >= 12 ? (1, least - 12) : (0, least); return new (most + carry, least); } public static Base12 operator -(Base12 left, Base12 right) { var (lm, ll) = left.To(); var (rm, rl) = right.To(); var most = lm - rm; var least = (int)ll - (int)rl; (var carry, least) = least < 0 ? (-1, least + 12) : (0, least); return new (most + carry, (uint)least); } public static Base12 operator *(Base12 left, Base12 right) { var (lm, ll) = left.To(); var (rm, rl) = right.To(); var leastQuot = (ll * rl) % 12; var leastRem = (ll * rl) / 12; var most = lm * rm + leastRem; return new((int)most, leastQuot); } public static Base12 operator /(Base12 left, Base12 right) { var (lm, ll) = left.To(); var (rm, rl) = right.To(); var mostQuot = lm / rm; var mostRem = lm % rm; var least = (ll / rl + mostRem) % 12; return new(mostQuot, (uint)least); } public bool Equals(Base12 other) => To() == other.To(); public override bool Equals(object? obj) => obj is Base12 other && Equals(other); public override int GetHashCode() => HashCode.Combine(MostSig, LeastSig); public int CompareTo(Base12 other) => (To(), other.To()) switch { var ((lm, ll), (rm, rl)) => lm.CompareTo(rm) switch { < 0 => -1, > 0 => 1, _ => ll.CompareTo(rl) } }; public static bool operator >(Base12 left, Base12 right) => left.CompareTo(right) > 0; public static bool operator >=(Base12 left, Base12 right) => left.CompareTo(right) >= 0; public static bool operator <(Base12 left, Base12 right) => left.CompareTo(right) < 0; public static bool operator <=(Base12 left, Base12 right) => left.CompareTo(right) <= 0; } ================================================ FILE: Samples/CreditCardValidation/Data/CVV.cs ================================================ // This is a toy sample that demonstrates credit-card validation. It shouldn't be considered complete, // it simply demonstrates usage of the Validation applicative/monad for capturing multiple errors when // doing complex validation. // // For more information, see Paul Louth's blog article on Validation: // https://paullouth.com/higher-kinds-in-c-with-language-ext-part-5-validation/ using LanguageExt; using LanguageExt.Common; using LanguageExt.Traits.Domain; namespace CreditCardValidation.Data; /// /// CVV code /// /// Integer representation of the code public class CVV(int Number) : DomainType, Identifier { public override string ToString() => $"{Number:000}"; public static Fin From(int repr) => repr is >= 0 and <= 999 ? new CVV(repr) : Error.New("invalid CVV number"); public int To() => Number; public override int GetHashCode() => Number.GetHashCode(); public bool Equals(CVV? other) => To().Equals(other?.To()); public override bool Equals(object? obj) => obj is CVV cvv && Equals(cvv); public static bool operator ==(CVV? left, CVV? right) => left?.To() == right?.To(); public static bool operator !=(CVV? left, CVV? right) => !(left == right); } ================================================ FILE: Samples/CreditCardValidation/Data/CardNumber.cs ================================================ // This is a toy sample that demonstrates credit-card validation. It shouldn't be considered complete, // it simply demonstrates usage of the Validation applicative/monad for capturing multiple errors when // doing complex validation. // // For more information, see Paul Louth's blog article on Validation: // https://paullouth.com/higher-kinds-in-c-with-language-ext-part-5-validation/ using LanguageExt; using LanguageExt.Common; using LanguageExt.Traits.Domain; namespace CreditCardValidation.Data; /// /// Credit card number /// /// Credit card number public class CardNumber(Seq Number) : DomainType>, Identifier { public override string ToString() => $"{Number}"; public static Fin From(Seq repr) => repr.Count == 16 && repr.ForAll(n => n is >= 0 and <= 9) ? new CardNumber(repr) : Error.New("card number not valid"); public static CardNumber FromUnsafe(Seq repr) => new (repr); public Seq To() => Number; public bool Equals(CardNumber? other) => To() == other?.To(); public override bool Equals(object? obj) => obj is CardNumber other && Equals(other); public override int GetHashCode() => To().GetHashCode(); public static bool operator ==(CardNumber? left, CardNumber? right) => left?.To() == right?.To(); public static bool operator !=(CardNumber? left, CardNumber? right) => !(left == right); } ================================================ FILE: Samples/CreditCardValidation/Data/CreditCardDetails.cs ================================================ // This is a toy sample that demonstrates credit-card validation. It shouldn't be considered complete, // it simply demonstrates usage of the Validation applicative/monad for capturing multiple errors when // doing complex validation. // // For more information, see Paul Louth's blog article on Validation: // https://paullouth.com/higher-kinds-in-c-with-language-ext-part-5-validation/ namespace CreditCardValidation.Data; /// /// Complete credit card details /// /// Credit card number /// Expiry date /// Card security code public record CreditCardDetails(CardNumber CardNumber, Expiry Expiry, CVV CVV) { public static CreditCardDetails Make(CardNumber cardNo, Expiry expiry, CVV cvv) => new (cardNo, expiry, cvv); public override string ToString() => $"CreditCard({CardNumber}, {Expiry}, {CVV})"; } ================================================ FILE: Samples/CreditCardValidation/Data/Expiry.cs ================================================ // This is a toy sample that demonstrates credit-card validation. It shouldn't be considered complete, // it simply demonstrates usage of the Validation applicative/monad for capturing multiple errors when // doing complex validation. // // For more information, see Paul Louth's blog article on Validation: // https://paullouth.com/higher-kinds-in-c-with-language-ext-part-5-validation/ using LanguageExt; using LanguageExt.Common; using LanguageExt.Traits.Domain; namespace CreditCardValidation.Data; /// /// Expiry date MM/YYYY /// /// Base12 number representing year and month public readonly struct Expiry(Base12 Value) : DomainType, Locus { public static Expiry AdditiveIdentity { get; } = new (Base12.Zero); public static Expiry Now { get { var now = DateTime.Now; return new Expiry(new Base12(now.Year, (uint)(now.Month - 1))); } } public static Range NextTenYears => new (Now, Now + MonthSpan.TenYears, default, NextTenYears1); static Fin DomainType.From(Base12 repr) => new Expiry(repr); public static Fin From(int month, int year) => month is >= 1 and <= 12 ? new Expiry(new Base12(year, (uint)(month - 1))) : Error.New("invalid date: month out of range, should be 1 - 12"); public Base12 To() => Value; public bool Equals(Expiry other) => To() == other.To(); public override bool Equals(object? obj) => obj is Expiry other && Equals(other); public override int GetHashCode() => To().GetHashCode(); public int CompareTo(Expiry other) => To().CompareTo(other.To()); public static bool operator ==(Expiry left, Expiry right) => left.Equals(right); public static bool operator !=(Expiry left, Expiry right) => !(left == right); public static Expiry operator +(Expiry left, MonthSpan right) => new(left.To() + Base12.From(right.To())); public static MonthSpan operator -(Expiry left, Expiry right) => new(left.To().ToBase10() - right.To().ToBase10()); public static Expiry operator -(Expiry value) => new(-value.To()); public static bool operator >(Expiry left, Expiry right) => left.To() > right.To(); public static bool operator >=(Expiry left, Expiry right) => left.To() >= right.To(); public static bool operator <(Expiry left, Expiry right) => left.To() < right.To(); public static bool operator <=(Expiry left, Expiry right) => left.To() <= right.To(); public override string ToString() => $"{To().To().LeastSig + 1}/{To().To().MostSig}"; static IEnumerable NextTenYears1 { get { var current = Now; for (var i = 0; i < 10; i++) { yield return current; current += MonthSpan.OneYear; } } } } ================================================ FILE: Samples/CreditCardValidation/Data/MonthSpan.cs ================================================ // This is a toy sample that demonstrates credit-card validation. It shouldn't be considered complete, // it simply demonstrates usage of the Validation applicative/monad for capturing multiple errors when // doing complex validation. // // For more information, see Paul Louth's blog article on Validation: // https://paullouth.com/higher-kinds-in-c-with-language-ext-part-5-validation/ using LanguageExt; using LanguageExt.Traits.Domain; namespace CreditCardValidation.Data; /// /// Time span in months /// /// Number of months to span public readonly struct MonthSpan(int Value) : DomainType, Amount { public static readonly MonthSpan OneYear = new(12); public static readonly MonthSpan TenYears = new(12 * 10); static Fin DomainType.From(int repr) => new MonthSpan(repr); public static MonthSpan From(int repr) => new (repr); public int To() => Value; public bool Equals(MonthSpan other) => To() == other.To(); public override bool Equals(object? obj) => obj is MonthSpan other && Equals(other); public override int GetHashCode() => To().GetHashCode(); public int CompareTo(MonthSpan other) => To().CompareTo(other.To()); public static bool operator ==(MonthSpan left, MonthSpan right) => left.To() == right.To(); public static bool operator !=(MonthSpan left, MonthSpan right) => !(left == right); public static MonthSpan operator -(MonthSpan value) => From(-value.To()); public static MonthSpan operator +(MonthSpan left, MonthSpan right) => From(left.To() + right.To()); public static MonthSpan operator -(MonthSpan left, MonthSpan right) => From(left.To() - right.To()); public static MonthSpan operator *(MonthSpan left, int right) => From(left.To() * right); public static MonthSpan operator /(MonthSpan left, int right) => From(left.To() * right); public static bool operator >(MonthSpan left, MonthSpan right) => left.To() > right.To(); public static bool operator >=(MonthSpan left, MonthSpan right) => left.To() >= right.To(); public static bool operator <(MonthSpan left, MonthSpan right) => left.To() < right.To(); public static bool operator <=(MonthSpan left, MonthSpan right) => left.To() <= right.To(); } ================================================ FILE: Samples/CreditCardValidation/Program.cs ================================================ using CreditCardValidation.Control; // This is a toy sample that demonstrates credit-card validation. It shouldn't be considered complete, // it simply demonstrates usage of the Validation applicative/monad for capturing multiple errors when // doing complex validation. // // For more information, see Paul Louth's blog article on Validation: // https://paullouth.com/higher-kinds-in-c-with-language-ext-part-5-validation/ Console.WriteLine(CreditCard.Validate("4560005094752584", "12-2024", "123")); Console.WriteLine(CreditCard.Validate("00000", "00-2345", "WXYZ")); ================================================ FILE: Samples/DomainTypesExamples/Dimension.cs ================================================ namespace DomainTypesExamples; public interface Dimension { public static abstract int Size { get; } } public class D3 : Dimension { public static int Size => 3; } public class D128 : Dimension { public static int Size => 128; } ================================================ FILE: Samples/DomainTypesExamples/DomainTypesExamples.csproj ================================================  Exe net10.0 enable enable ================================================ FILE: Samples/DomainTypesExamples/Program.cs ================================================ // See https://aka.ms/new-console-template for more information using DomainTypesExamples; using LanguageExt; using static LanguageExt.Prelude; var v1 = Vector.From([100, 200, 300]).ThrowIfFail(); var v2 = Vector.From([100, 200, 300]).ThrowIfFail(); var v3 = v1 + v2; Console.WriteLine($"{v3}"); var w1 = Vector.From(Range(0, 128).ToArr()).ThrowIfFail(); var w2 = Vector.From(Range(0, 128).ToArr()).ThrowIfFail(); var w3 = w1 + w2; Console.WriteLine($"{w3}"); var x1 = Vector.From(Range(0, 128).ToArr()).ThrowIfFail(); var x2 = Vector.From(Range(0, 128).ToArr()).ThrowIfFail(); var x3 = x1 * x2; Console.WriteLine($"{x3}"); ================================================ FILE: Samples/DomainTypesExamples/Time.cs ================================================ using LanguageExt; using LanguageExt.Traits.Domain; namespace DomainTypesExamples; public readonly record struct Time(long Timestamp) : DomainType, Locus { static Fin>, VectorSpace, A> where A : IAdditiveIdentity, IAdditionOperators, ISubtractionOperators, IMultiplyOperators, IDivisionOperators, IUnaryNegationOperators where D : Dimension { readonly Arr Values; private Vector(Arr values) { if(values.Count != D.Size) throw new ArgumentException(nameof(values)); Values = values; } public static Fin> From(Arr repr) => repr.Count == D.Size ? new Vector(repr) : Error.New($"Array isn't the correct size. Expected: {D.Size}, got: {repr.Count}"); public Arr To() => Values; public override bool Equals(object? obj) => obj is Vector rhs && Equals(rhs); public virtual bool Equals(Vector? other) { var ia = Values.GetEnumerator(); var ib = (other?.To() ?? Arr.empty()).GetEnumerator(); while (ia.MoveNext() && ib.MoveNext()) { if (!ia.Current.Equals(ib.Current)) return false; } return ia.MoveNext() == ib.MoveNext(); } public override int GetHashCode() => hash(Values); public static bool operator ==(Vector? left, Vector? right) => left?.Equals(right) ?? right is null; public static bool operator !=(Vector? left, Vector? right) => !(left == right); public static Vector operator -(Vector value) { var vector = new A[D.Size]; var ix = 0; foreach (var x in value.To()) { vector[ix++] = -x; } return new(Arr.create(vector)); } public static Vector operator +(Vector left, Vector right) { var vector = new A[D.Size]; var rem = D.Size % Vector.Count; var total = D.Size - rem; var larray = left.Values; var rarray = right.Values; // Perform the operation using SIMD intrinsics for (var i = 0; i < total; i += Vector.Count) { var v1 = new Vector(larray.AsSpan(i, Vector.Count)); var v2 = new Vector(rarray.AsSpan(i, Vector.Count)); (v1 + v2).CopyTo(vector, i); } // Perform the remainder of the operation that couldn't fit into a SIMD intrinsic for (var i = D.Size - rem; i < D.Size; i++) { vector[i] = left.Values[i] + right.Values[i]; } return new(Arr.create(vector)); } public static Vector operator -(Vector left, Vector right) { var vector = new A[D.Size]; var rem = D.Size % Vector.Count; var total = D.Size - rem; var larray = left.Values; var rarray = right.Values; // Perform the operation using SIMD intrinsics for (var i = 0; i < total; i += Vector.Count) { var v1 = new Vector(larray.AsSpan(i, Vector.Count)); var v2 = new Vector(rarray.AsSpan(i, Vector.Count)); (v1 - v2).CopyTo(vector, i); } // Perform the remainder of the operation that couldn't fit into a SIMD intrinsic for (var i = D.Size - rem; i < D.Size; i++) { vector[i] = left.Values[i] - right.Values[i]; } return new(Arr.create(vector)); } /// /// Returns a new vector whose values are the product of each pair of elements in two specified vectors. /// public static Vector operator *(Vector left, Vector right) { var vector = new A[D.Size]; var rem = D.Size % Vector.Count; var total = D.Size - rem; var larray = left.Values; var rarray = right.Values; // Perform the operation using SIMD intrinsics for (var i = 0; i < total; i += Vector.Count) { var v1 = new Vector(larray.AsSpan(i, Vector.Count)); var v2 = new Vector(rarray.AsSpan(i, Vector.Count)); (v1 * v2).CopyTo(vector, i); } // Perform the remainder of the operation that couldn't fit into a SIMD intrinsic for (var i = D.Size - rem; i < D.Size; i++) { vector[i] = left.Values[i] * right.Values[i]; } return new(Arr.create(vector)); } public static Vector operator *(Vector left, A right) { var vector = new A[D.Size]; var rem = D.Size % Vector.Count; var total = D.Size - rem; var larray = left.Values; // Perform the operation using SIMD intrinsics for (var i = 0; i < total; i += Vector.Count) { var v = new Vector(larray.AsSpan(i, Vector.Count)); (v * right).CopyTo(vector, i); } // Perform the remainder of the operation that couldn't fit into a SIMD intrinsic for (var i = D.Size - rem; i < D.Size; i++) { vector[i] = left.Values[i] * right; } return new(Arr.create(vector)); } public static Vector operator /(Vector left, A right) { var vector = new A[D.Size]; var rem = D.Size % Vector.Count; var total = D.Size - rem; var larray = left.Values; // Perform the operation using SIMD intrinsics for (var i = 0; i < total; i += Vector.Count) { var v = new Vector(larray.AsSpan(i, Vector.Count)); (v / right).CopyTo(vector, i); } // Perform the remainder of the operation that couldn't fit into a SIMD intrinsic for (var i = D.Size - rem; i < D.Size; i++) { vector[i] = left.Values[i] / right; } return new(Arr.create(vector)); } /// /// Calculate the dot product between two vectors /// public A Dot(Vector rhs) => (this * rhs).Sum(); /// /// Calculate of all values in the vector /// public A Sum() { var rem = D.Size % 16; var total = D.Size - rem; var array = Values; var sum = A.AdditiveIdentity; // Perform the operation using SIMD intrinsics for (var i = 0; i < total; i += Vector.Count) { var span = array.AsSpan(i, 16); sum += span[0] + span[1] + span[2] + span[3] + span[4] + span[5] + span[6] + span[7] + span[8] + span[9] + span[10] + span[11] + span[12] + span[13] + span[14] + span[15]; } // Perform the remainder of the operation that couldn't fit into a SIMD intrinsic for (var i = D.Size - rem; i < D.Size; i++) { sum += array[i]; } return sum; } public override string ToString() => Values.ToFullArrayString(); } ================================================ FILE: Samples/EffectsExamples/EffectsExamples.csproj ================================================ Exe net10.0 default 0.6.1 enable MSBuild:GenerateCodeFromAttributes MSBuild:GenerateCodeFromAttributes MSBuild:GenerateCodeFromAttributes MSBuild:GenerateCodeFromAttributes MSBuild:GenerateCodeFromAttributes ================================================ FILE: Samples/EffectsExamples/Examples/CancelExample.cs ================================================ using System; using LanguageExt; using LanguageExt.Sys; using LanguageExt.Sys.Traits; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace EffectsExamples; /// /// Cancel example /// /// /// Accepts key presses and echos them to the console until Enter is pressed. /// When Enter is pressed it calls `cancel` to trigger the cancellation token /// public class CancelExample where RT: Has, ConsoleIO> { public static Eff main => +repeat(from k in Console.readKey from _ in k.Key == ConsoleKey.Enter ? cancel : unitIO from w in Console.write(k.KeyChar) select unit); } ================================================ FILE: Samples/EffectsExamples/Examples/ErrorAndGuardExample.cs ================================================ using System; using LanguageExt; using LanguageExt.Sys; using LanguageExt.Common; using LanguageExt.Sys.Traits; using LanguageExt.Traits; using static LanguageExt.Prelude; using static LanguageExt.UnitsOfMeasure; namespace EffectsExamples; /// /// Error handling and guards example /// /// /// Repeats the text you type in until you press Enter on an empty line, which will write a UserExited error - this /// will be caught for a safe exit /// Or, 'sys' that will throw a SystemException - this will be caught and 'sys error' will be printed /// Or, 'err' that will throw an Exception - this will be caught to become 'there was a problem' /// public static class ErrorAndGuardExample where RT : Has, ConsoleIO> { static readonly Error UserExited = Error.New(100, "user exited"); static readonly Error SafeError = Error.New(200, "there was a problem"); public static Eff main => from _1 in askUser | @catch(ex => ex.HasException(), Console.writeLine("system error")) | SafeError from _2 in Console.writeLine("goodbye") select unit; static Eff askUser => repeat(Schedule.spaced(1 * second) | Schedule.recurs(3), from ln in Console.readLine from _1 in guard(notEmpty(ln), UserExited) from _2 in guard(ln != "sys", () => throw new SystemException()) from _3 in guard(ln != "err", () => throw new Exception()) from _4 in Console.writeLine(ln) select unit) | @catch(UserExited, pure, Unit>(unit)); } ================================================ FILE: Samples/EffectsExamples/Examples/FoldTest.cs ================================================ using System; using LanguageExt; using LanguageExt.Sys; using LanguageExt.Pipes; using LanguageExt.Common; using LanguageExt.Sys.Traits; using LanguageExt.Traits; using static LanguageExt.Prelude; using static LanguageExt.Pipes.Effect; using static LanguageExt.Pipes.Producer; using static LanguageExt.Pipes.Pipe; using static LanguageExt.Pipes.Consumer; namespace EffectsExamples { /// /// Fold test /// /// /// Processes keys from the keyboard into words, when a whitespace is encountered the folded word is yielded /// down the pipe /// public static class FoldTest where RT : Has, ConsoleIO> { public static Eff main => mainEffect.Run().As(); static Effect mainEffect => Console.readKeys | exitIfEscape | keyChar | words | filterEmpty | writeLine | Schedule.Forever; static Pipe exitIfEscape => from k in awaiting() from x in guard(k.Key != ConsoleKey.Escape, Errors.Cancelled) from _ in yield(k) select unit; static Pipe keyChar => map(k => k.KeyChar); static Pipe words => foldUntil((word, ch) => word + ch, x => char.IsWhiteSpace(x.Value), ""); static Pipe filterEmpty => filter(notEmpty); static Consumer writeLine => from l in awaiting() from _ in Console.writeLine(l) select unit; } } ================================================ FILE: Samples/EffectsExamples/Examples/ForkCancelExample.cs ================================================ using LanguageExt; using LanguageExt.Sys; using LanguageExt.Sys.Traits; using LanguageExt.Traits; using static LanguageExt.Prelude; using static LanguageExt.UnitsOfMeasure; namespace EffectsExamples; /// /// Process forking and cancelling example /// /// /// Forks a process that runs 10 times, summing a value each time. /// If you press enter before the 10 iterations then the forked process will be cancelled /// public static class ForkCancelExample where RT : Has, ConsoleIO>, Has, TimeIO> { public static Eff main => +from frk in fork(inner) from key in Console.readKey from _1 in frk.Cancel from _2 in Console.writeLine("done") select unit; static Eff inner => from x in sum from _ in Console.writeLine($"total: {x}") select unit; static Eff sum => +digit.FoldIO(Schedule.recurs(9) | Schedule.spaced(1 * second), 0, (s, x) => s + x); static Eff digit => from one in SuccessEff(1) from _ in Console.writeLine("*") select one; } ================================================ FILE: Samples/EffectsExamples/Examples/QueueExample.cs ================================================ using LanguageExt; using LanguageExt.Sys; using LanguageExt.Pipes; using LanguageExt.Common; using LanguageExt.Sys.Traits; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace EffectsExamples; /// /// Queue example /// /// /// Creates two queues. Queues are Producers that have an Enqueue function. /// The two producers are merged into a single producer and piped to a writeLine consumer to create an Effect /// The effect is forked to run without awaiting the result /// Another effect is created that listens to input from the user and pipes it to queue 1 or 2 depending on if the /// text starts with a '1' or a '2'. /// If the text starts with anything else, the effect is cancelled. /// Then the fork is cancelled. /// /// public static class QueueExample where RT : Has, ConsoleIO> { public static Eff main() { // Create two queues. Queues are Producers that have an Enqueue function var queue1 = Conduit.make(); var queue2 = Conduit.make(); // Compose the queues with a pipe that prepends some text to what they produce var queues = Seq(queue1.ToProducer() | prepend("Queue 1: "), queue2.ToProducer() | prepend("Queue 2: ")); // Run the queues in a forked task // Repeatedly read from the console and write to one of the two queues depending on // whether the first char is 1 or 2 var effect = from f in fork(Producer.merge(queues) | writeLine).As() from x in Console.readLines | writeToQueue(queue1, queue2) | Schedule.Forever from _ in f.Cancel // cancels the forked task select unit; return effect.As().Run(); } // Consumer of the console. It enqueues the item to queue1 or queue2 depending // on the first char of the string it awaits static Consumer writeToQueue( Conduit queue1, Conduit queue2) => from x in Consumer.awaiting() from u in guard(x.Length > 0, Error.New("exiting")) from _ in x[0] switch { '1' => queue1.Post(x.Substring(1)).As(), '2' => queue2.Post(x.Substring(1)).As(), _ => Fail(Errors.Cancelled) } select unit; /// /// Pipe that prepends the text provided to the awaited value and then yields it /// static Pipe prepend(string x) => from l in Pipe.awaiting() from _ in Pipe.yield($"{x}{l}") select unit; /// /// Consumer that simply writes to the console /// static Consumer writeLine => from l in Consumer.awaiting() from _ in Console.writeLine(l) select unit; } ================================================ FILE: Samples/EffectsExamples/Examples/RetryExample.cs ================================================ using LanguageExt; using LanguageExt.Common; using LanguageExt.Sys; using LanguageExt.Sys.Traits; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace EffectsExamples; /// /// Retry example /// /// /// Asks you to say hello. /// If you don't type 'hello' then an error will be raised and it will retry. /// /// public static class RetryExample where RT : Has, ConsoleIO> { static readonly Error Failed = (Error)"I asked you to say hello, and you can't even do that?!"; public static Eff main => +retry(Schedule.recurs(5), from _ in Console.writeLine("Say hello") from t in Console.readLine from e in guard(t == "hello", Failed) from m in Console.writeLine("Hi") select unit); } ================================================ FILE: Samples/EffectsExamples/Examples/TextFileChunkStreamExample.cs ================================================ using System.Text; using LanguageExt; using LanguageExt.Sys; using LanguageExt.Pipes; using LanguageExt.Sys.IO; using LanguageExt.Sys.Traits; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace EffectsExamples; /// /// Text file chunk streaming example /// /// /// Streams the contents of a text file in chunks of 40 characters /// public static class TextFileChunkStreamExample where RT : Has, ConsoleIO>, Has, FileIO>, Has, TextReadIO>, Has, EncodingIO> { public static Eff main => from _ in Console.writeLine("Please type in a path to a text file and press enter") from p in Console.readLine from e in mainEffect(p).Run() select unit; static Effect mainEffect(string path) => File.openRead(path) | Stream>.read(80).ToEff() | decodeUtf8 | writeLine; static Pipe, string, Unit> decodeUtf8 => from c in Pipe.awaiting, string>() from _ in Pipe.yield, string>(Encoding.UTF8.GetString(c.ToReadOnlySpan())) select unit; static Consumer writeLine => from l in Consumer.awaiting() from _ in Console.writeLine(l) select unit; } ================================================ FILE: Samples/EffectsExamples/Examples/TextFileLineStreamExample.cs ================================================ using LanguageExt; using LanguageExt.Sys; using LanguageExt.Pipes; using LanguageExt.Sys.IO; using LanguageExt.Sys.Traits; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace EffectsExamples; /// /// Text file line streaming example /// /// /// Streams the contents of a text file, one line at a time /// public class TextFileLineStreamExample where RT : Has, ConsoleIO>, Has, FileIO>, Has, TextReadIO>, Has, EncodingIO> { public static Eff main => from _ in Console.writeLine("Please type in a path to a text file and press enter") from p in Console.readLine from e in mainEffect(p).Run() select unit; static Effect mainEffect(string path) => File.openText(path) | TextRead.readLine | writeLine; static Consumer writeLine => from l in Consumer.awaiting() from _ in Console.writeLine(l) select unit; } ================================================ FILE: Samples/EffectsExamples/Examples/TimeExample.cs ================================================ using LanguageExt; using LanguageExt.Sys; using LanguageExt.Sys.Traits; using LanguageExt.Traits; using static LanguageExt.Prelude; using static LanguageExt.UnitsOfMeasure; namespace EffectsExamples; /// /// Clock example /// /// /// Prints the time for 10 repetitions, the space between the prints follows the Fibonacci sequence up to 10 seconds /// and then it's clamped /// public static class TimeExample where RT : Has, TimeIO>, Has, ConsoleIO> { public static Eff main => +repeat(Schedule.spaced(10 * second) | Schedule.fibonacci(1 * second) | Schedule.recurs(9), from tm in Time.now from _1 in Console.writeLine(tm.ToLongTimeString()) select unit); } ================================================ FILE: Samples/EffectsExamples/Examples/TimeoutExample.cs ================================================ using LanguageExt; using LanguageExt.Sys; using LanguageExt.Common; using LanguageExt.Sys.Traits; using LanguageExt.Traits; using static LanguageExt.Prelude; using static LanguageExt.UnitsOfMeasure; namespace EffectsExamples; /// /// Process timeout example /// /// /// Repeats a backing off process for 1 minutes /// The back-off follows the fibonacci sequence in terms of the delay /// /// public class TimeoutExample where RT : Has, TimeIO>, Has, ConsoleIO> { public static Eff main => from _1 in timeout(60 * seconds, longRunning) | @catch(Errors.TimedOut, pure, Unit>(unit)) from _2 in Console.writeLine("done") select unit; static Eff longRunning => (from tm in Time.now from _1 in Console.writeLine(tm.ToLongTimeString()) select unit) .RepeatIO(Schedule.fibonacci(1 * second)).As(); } ================================================ FILE: Samples/EffectsExamples/Menu.cs ================================================ using System; using LanguageExt; using LanguageExt.Common; using LanguageExt.Sys; using LanguageExt.Sys.Traits; using static LanguageExt.Prelude; using static LanguageExt.UnitsOfMeasure; using LanguageExt.Traits; namespace EffectsExamples; public static class Menu where RT : Has, FileIO>, Has, TextReadIO>, Has, TimeIO>, Has, ConsoleIO>, Has, EncodingIO> { public static Eff menu => repeat(from __0 in clearConsole(ConsoleColor.Green) from __1 in showOptions from key in Console.readKey from __2 in clearConsole(ConsoleColor.White) from __3 in runExample(key.KeyChar) select unit).As(); static Eff> showOptions => +menuItems.Traverse(p => Console.writeLine($"{p.Item}. {p.Text}")); static Eff clearConsole(ConsoleColor color) => from _0 in Console.clear.As() from _1 in Console.setColour(color) select unit; static Eff runExample(char ix) => from exa in findExample(ix) from __0 in Console.setColour(ConsoleColor.Yellow) from __1 in Console.writeLine(exa.Desc) from __2 in Console.resetColour from res in localCancel(exa.Example) | @catch(logError) from __3 in showComplete(5) select res; static Eff<(Eff Example, string Desc)> findExample(char ix) => menuItems.Find(item => item.Item == ix) .Map(item => (Example: item.Example, Desc: item.Desc)) .ToEff() | Pure((Eff.unit(), "invalid menu option")); static Eff logError(Error e) => from _0 in Console.setColour(ConsoleColor.Red).As() from _1 in Console.writeLine($"{e}") from _2 in Console.setColour(ConsoleColor.Yellow) select unit; static Eff showComplete(int x) => x == 0 ? unitEff : from _0 in Console.setColour(ConsoleColor.Cyan).As() from _1 in Console.writeLine($"Returning to menu in {x}") from _2 in Time.sleepFor(1 * second) from _3 in showComplete(x - 1) select unit; static Seq<(char Item, Eff Example, string Text, string Desc)> menuItems => [('1', ErrorAndGuardExample.main, "Error handling and guards example", "Repeats the text you type in until you press Enter on an empty line, which will write a UserExited error - this will be caught for a safe exit\nOr, 'sys' that will throw a SystemException - this will be caught and 'sys error' will be printed\nOr, 'err' that will throw an Exception - this will be caught to become 'there was a problem'"), ('2', ForkCancelExample.main, "Process forking and cancelling example", "Forks a process that runs 10 times, summing a value each time.\nIf you press enter before the 10 iterations then the forked process will be cancelled"), ('3', TimeoutExample.main, "Process timeout example", "Repeats a backing off process for 1 minutes\nThe back-off delay follows the fibonacci sequence"), ('4', TimeExample.main, "Clock example", "Prints the time for 10 repetitions, the space between the prints follows the Fibonacci sequence up to 10 seconds\nand then it's clamped"), ('5', CancelExample.main, "Cancel example", "Accepts key presses and echos them to the console until Enter is pressed.\nWhen Enter is pressed it calls `cancel()` to trigger the cancellation token"), ('6', RetryExample.main, "Retry example", "Asks you to say hello.\nIf you don't type 'hello' then an error will be raised and it will retry."), ('7', QueueExample.main(), "Queue effect example", "Creates two queues. Queues are Producers that have an Enqueue function.\nThe two producers are merged into a single producer and piped to a writeLine consumer to create an Effect\nThe effect is forked to run without awaiting the result\nAnother effect is created that listens to input from the user and pipes it to queue 1 or 2 depending on if the text starts with a '1' or a '2'.\nIf the text starts with anything else, the effect is cancelled.\nThen the fork is cancelled."), ('8', FoldTest.main, "Pipe word folding example", "Folds keys from the keyboard into words, when a whitespace is encountered the folded word is yielded\ndown the pipe"), ('9', TextFileLineStreamExample.main, "Text file line streaming example", "Streams the contents of a text file, one line at a time"), ('a', TextFileChunkStreamExample.main, "Text file chunk streaming example", "Streams the contents of a text file in chunks of 80 characters") //('8', ClientServer.main, "Client / server effect example", "Simple request/response example. The client sends 3 values to the server and it increments them"), ]; } ================================================ FILE: Samples/EffectsExamples/Program.cs ================================================ using LanguageExt; using LanguageExt.Sys.Live; namespace EffectsExamples; class Program { static void Main(string[] args) => Menu .menu .Run(Runtime.New(), EnvIO.New()) .ThrowIfFail(); } ================================================ FILE: Samples/IOExmples/IOExmples.csproj ================================================ Exe net10.0 default 0.6.1 enable ================================================ FILE: Samples/IOExmples/Program.cs ================================================ using System; using LanguageExt; using static LanguageExt.Prelude; namespace IOExamples; class Program { static void Main(string[] args) => infiniteIterator(); //infiniteLoop(0).Run(); static void infiniteIterator() { // NOTE: This should be run in Release mode, otherwise you might get a space leak for(var iter = Naturals.GetIterator(); !iter.IsEmpty; iter = iter.Tail) { if (iter.Head % 10000 == 0) { Console.WriteLine(iter.Head); } } } static IO infiniteLoop(int value) => from _ in value % 10000 == 0 ? writeLine($"{value}") : Pure(unit) from r in tail(infiniteLoop(value + 1)) select unit; static IO infiniteLoop1(int value) => (value % 10000 == 0 ? writeLine($"{value}") : Pure(unit)) .Bind(_ => infiniteLoop1(value + 1)); static IO recursiveAskForNumber => from n in askForNumber(1) from _ in writeLine($"Your number is: {n}") select n; static IO askForNumber(int attempts) => from _ in writeLine($"Enter a number (attempt number: {attempts})") from l in readLine from n in int.TryParse(l, out var v) ? Pure(v) : from _ in writeLine("That's not a number!") from r in askForNumber(attempts + 1) select r select n; // static readonly Transducer folder = // foldUntil(Schedule.spaced(TimeSpan.FromMilliseconds(100)), // 0, // (int s, int x) => s + x, // valueIs: x => x % 10 == 0); // // static IO folding => // from n in many(Range(1, 25)) // from _ in writeLine($"item flowing in: {n}") // from s in n | folder // from r in writeLine($"Total {s}") // select unit; // static IO retrying => // from ix in many(Range(1, 10)) // from _1 in retry(from _2 in writeLine($"Enter a number to add to {ix}") // from nm in readLine.Map(parseInt) // from _3 in guard(nm.IsSome, Error.New("Please enter a valid integer")) // from _4 in writeLine($"Number {ix} + {(int)nm} = {ix + (int)nm}") // select unit) // from _4 in waitOneSecond // select unit; static IO repeating => repeat(Schedule.recurs(5), from x in readNumber("Enter the first number to add") from y in readNumber("Enter the second number to add") from _ in writeLine($"{x} + {y} = {x + y}") from t in waitOneSecond select unit).As() | writeLine("Obviously you don't know what a number is so I'm off."); static IO readNumber(string question) => retry(Schedule.recurs(3), from _1 in writeLine(question) from nx in readLine.Map(int.Parse) select nx).As(); static readonly IO readLine = lift(() => Console.ReadLine() ?? ""); static IO writeLine(string line) => lift(() => { Console.WriteLine(line); return unit; }); static IO waitOneSecond => wait(1000); static IO wait(int milliseconds) => yieldFor(milliseconds); static IO now => lift(() => DateTime.Now); } ================================================ FILE: Samples/Newsletter/Newsletter/Command/Email.cs ================================================ using Newsletter.UI; using Newsletter.Data; using Newsletter.Effects; using Newsletter.Effects.Traits; using SendGrid.Helpers.Mail; namespace Newsletter.Command; public static class Email where RT : Has, Has, Has where M : MonadIO, Fallible { public static K sendToAll(Seq members, Letter letter) => members.Traverse(m => send(m.Name, m.Email, letter)) .IgnoreF(); public static K send(string name, string email, Letter letter) => Effects.Email .send(new EmailAddress("noreply@paullouth.com", "Notes from a Small Functional Island"), new EmailAddress(email, name), letter.Title, letter.PlainText, letter.Html) | @catch(Display.error); } ================================================ FILE: Samples/Newsletter/Newsletter/Command/Members.cs ================================================ using CsvHelper; using CsvHelper.Configuration; using System.Globalization; using Newsletter.Effects; using Newsletter.Data; namespace Newsletter.Command; /// /// Member access /// public static class Members where RT : Has, Has, Has, Has where M : MonadIO, Fallible { public static K> readAll => from folder in Config.membersFolder from path in readFirstFile(folder) select readMembers(path).Filter(m => m.SubscribedToEmails); static K readFirstFile(string folder) => Directory.enumerateFiles(folder, "*.csv") .Map(fs => fs.OrderDescending() .AsIterable() .Head) .Bind(path => path.Match( Some: pure, None: error(Error.New($"no member files found in {folder}")))); static Seq readMembers(string path) { var config = new CsvConfiguration(CultureInfo.InvariantCulture) { HasHeaderRecord = true }; using var reader = new StreamReader(path); using var csv = new CsvReader(reader, config); return csv.GetRecords() .AsIterable() .Map(r => new Member(r.id, r.email, r.name, r.subscribed_to_emails == "true", r.tiers == "Supporter")) .ToSeq() .Strict(); } record Row( string id, string email, string name, string note, string subscribed_to_emails, string complimentary_plan, string stripe_customer_id, string created_at, string deleted_at, string labels, string tiers); } ================================================ FILE: Samples/Newsletter/Newsletter/Command/Newsletter.cs ================================================ using System.Web; using System.Text; using Newsletter.Data; using Newsletter.Effects; namespace Newsletter.Command; public static class Newsletter where RT : Has, Has, Has, Has where M : MonadIO, Fallible { /// /// Builds the newsletter /// public static K make(Seq posts, Templates templates) => M.Pure(Newsletter.make(posts, templates)); /// /// Saves the newsletter as HTML and plain-text to the letters folder /// public static K save(Letter letter) => from f in Config.lettersFolder from _ in File.writeAllText(Path.Combine(f, $"letter-{letter.PublishedAt:yyyy-MM-dd}.html"), letter.Html) >> File.writeAllText(Path.Combine(f, $"letter-{letter.PublishedAt:yyyy-MM-dd}.txt"), letter.PlainText) select unit; } public static class Newsletter { /// /// Builds the newsletter /// public static Letter make(Seq posts, Templates templates) { var post = posts[0]; var recentHtml = new StringBuilder(); var recentText = new StringBuilder(); foreach(var recent in posts.Tail) { var rhtml = templates.RecentItem .Html .Replace("[TITLE]", HttpUtility.HtmlEncode(recent.Title)) .Replace("[EXCERPT]", HttpUtility.HtmlEncode(recent.Excerpt)) .Replace("[URL]", recent.Url.ToString()); if (recent.FeatureImageUrl) { rhtml = rhtml.Replace("[IMAGE_URL]", ((Uri)recent.FeatureImageUrl).ToString()); } recentHtml.Append(rhtml); recentText.Append(templates.RecentItem .PlainText .Replace("[TITLE]", recent.Title) .Replace("[EXCERPT]", recent.Excerpt) .Replace("[URL]", recent.Url.ToString())); } var yearRange = DateTime.Now.Year switch { 2024 => "2024", var y => $"2024 - {y}" }; var html = templates.Email .Html .Replace("[POST_CONTENT]", post.Html) .Replace("[POST_DATE]", $"{post.PublishedAt.Day} {monthToString(post.PublishedAt.Month)} {post.PublishedAt.Year}") .Replace("[POST_URL]", post.Url.ToString()) .Replace("[POST_TITLE]", HttpUtility.HtmlEncode(post.Title)) .Replace("[YEAR_RANGE]", yearRange) .Replace("[RECENT_ARTICLES]", recentHtml.ToString()); if (post.FeatureImageUrl) { html = html.Replace("[FEATURE_IMAGE_URL]", ((Uri)post.FeatureImageUrl).ToString()); } var text = templates.Email .PlainText .Replace("[POST_CONTENT]", post.PlainText) .Replace("[POST_DATE]", $"{post.PublishedAt.Day} {monthToString(post.PublishedAt.Month)} {post.PublishedAt.Year}") .Replace("[POST_URL]", post.Url.ToString()) .Replace("[POST_TITLE]", post.Title) .Replace("[YEAR_RANGE]", yearRange) .Replace("[RECENT_ARTICLES]", recentText.ToString()); return new Letter(post.Title, html, text, post.PublishedAt); } /// /// Convert month integer to a string /// static string monthToString(int month) => month switch { 1 => "Jan", 2 => "Feb", 3 => "Mar", 4 => "Apr", 5 => "May", 6 => "Jun", 7 => "Jul", 8 => "Aug", 9 => "Sep", 10 => "Oct", 11 => "Nov", 12 => "Dec", _ => "?" }; } ================================================ FILE: Samples/Newsletter/Newsletter/Command/Posts.cs ================================================ using System.Text.Json; using Newsletter.Data; using Newsletter.Effects; using Newsletter.Effects.Traits; namespace Newsletter.Command; public static class Posts where RT : Has, Has, Has, Has, Has, Has, Has where M : MonadIO, Fallible { /// /// Read the latest post from the Ghost API /// public static K readFromApi => readAllFromApi.Bind( p => p.Head .Match(Some: M.Pure, None: M.Fail(Error.New("no posts found")))); /// /// Read the last n posts from the Ghost API /// public static K> readLastFromApi(int n) => readAllFromApi.Map(ps => ps.Take(n).ToSeq()); /// /// Read all posts from the Ghost API /// public static K> readAllFromApi => from root in Config.siteUrl from apiKey in Config.contentApiKey from text in Web.downloadText(makePostsUrl(root, apiKey)) from element in getPostsElementForApiResult(text) from posts in readPostsFromText(element) select posts; static K readFirstFile(string folder) => from fs in Directory.enumerateFiles(folder, "*.json") .Bind(fs => fs.OrderDescending() .AsIterable() .Take(1) .Traverse(File.readAllText)) from rs in fs.ToSeq() switch { [var text, ..] => M.Pure(text), _ => M.Fail(Error.New($"no JSON posts file found in {folder}")) } select rs; static K getPostsElementForApiResult(string text) => Json.readJson(text) .Map(doc => doc.RootElement .Get("posts")); static K getPostsElementForFile(string text) => Json.readJson(text) .Map(doc => doc.RootElement .Get("db") .EnumerateArray() .FirstOrDefault() .Get("data") .Get("posts")); static K> readPostsFromText(JsonElement postsElement) => postsElement.Enumerate() .Traverse(readPost) .Map(posts => posts.OrderDescending() .AsIterable() .Filter(p => p.IsPublic)); static K readPost(JsonElement element) => Config.siteUrl.Map(siteUrl => { // Acquire var id = element.Get("id").GetString() ?? throw new Exception("failed to read post 'id'"); var title = element.Get("title").GetString() ?? throw new Exception("failed to read post 'title'"); var slug = element.Get("slug").GetString() ?? throw new Exception("failed to read post 'slug'"); var html = element.Get("html").GetString() ?? throw new Exception("failed to read post 'html'"); var plaintext = element.Get("plaintext").GetString() ?? throw new Exception("failed to read post 'plaintext'"); var featureImage = element.Get("feature_image") .GetString() ?.Replace("__GHOST_URL__", siteUrl) ?? null; var excerpt = element.Get("custom_excerpt").GetString() ?? ""; var publishedAt = element.Get("published_at").GetDateTime(); var emailRecFilter = element.TryGetProperty("email_recipient_filter", out var value1) switch { true => value1.GetString() == "all", _ => true }; var status = element.TryGetProperty("status", out var value2) switch { true => value2.GetString() == "published", _ => true }; var isPublic = emailRecFilter && status; // Fix up var url = new Uri($"{siteUrl}/{slug}"); var featureImageUrl = featureImage == null ? null : new Uri(featureImage); html = fixupText(html, siteUrl); plaintext = fixupText(plaintext, siteUrl); return new Post(id, title, url, html, plaintext, excerpt, Optional(featureImageUrl), publishedAt, isPublic); }); static string fixupText(string text, string siteUrl) => text.Replace("\\n", "\n") .Replace("\\\"", "\"") .Replace("\\\\", "\\") .Replace("", "") .Replace("__GHOST_URL__", siteUrl); /// /// Generate the Uri for the /posts API for the Ghost CMS platform /// static Uri makePostsUrl(string root, string apiKey) => new ($"{root}/ghost/api/content/posts/?key={apiKey}&formats=html,plaintext"); } ================================================ FILE: Samples/Newsletter/Newsletter/Command/Send.cs ================================================ using Newsletter.Data; using Newsletter.Effects; using Newsletter.Effects.Traits; using Newsletter.UI; namespace Newsletter.Command; public static class Send where RT : Has, Has, Has, Has, Has, Has, Has, Has, Has where M : MonadIO, Fallible { public static K newsletter => from posts in Posts.readLastFromApi(4) from members in Members.readAll from templates in Templates.loadDefault from letter in Newsletter.make(posts, templates) from _ in Newsletter.save(letter) >> Display.showWhatsAboutToHappen(members) >> askUserToConfirmSend >> Email.sendToAll(members, letter) >> Display.confirmSent select unit; static K askUserToConfirmSend => from k in Console.readKey from x in Display.emptyLine from _ in k.Key == ConsoleKey.Y ? pure(unit) : error(Error.New("user cancelled")) select unit; } ================================================ FILE: Samples/Newsletter/Newsletter/Command/Templates.cs ================================================ using Newsletter.Data; using Newsletter.Effects; namespace Newsletter.Command; /// /// Loads the templates for the emails /// public static class Templates where RT : Has, Has, Has, Has where M : MonadIO, Fallible { public static K loadDefault => from folder in Config.templateFolder from email in loadTemplate("email") from recnt in loadTemplate("recent-item") select new Templates(email, recnt); public static K loadTemplate(string name) => from fldr in Config.templateFolder from html in File.readAllText(Path.Combine(fldr, $"{name}.html")) from text in File.readAllText(Path.Combine(fldr, $"{name}.txt")) select new Template(html, text); } ================================================ FILE: Samples/Newsletter/Newsletter/Data/Letter.cs ================================================ namespace Newsletter.Data; public record Letter( string Title, string Html, string PlainText, DateTime PublishedAt); ================================================ FILE: Samples/Newsletter/Newsletter/Data/Members.cs ================================================ namespace Newsletter.Data; /// /// Member record /// public record Member(string Id, string Email, string Name, bool SubscribedToEmails, bool Supporter); ================================================ FILE: Samples/Newsletter/Newsletter/Data/Posts.cs ================================================ namespace Newsletter.Data; public record Post( string Id, string Title, Uri Url, string Html, string PlainText, string Excerpt, Option FeatureImageUrl, DateTime PublishedAt, bool IsPublic) : IComparable { public int CompareTo(Post? other) { if (ReferenceEquals(this, other)) return 0; if (ReferenceEquals(null, other)) return 1; return PublishedAt.CompareTo(other.PublishedAt); } } ================================================ FILE: Samples/Newsletter/Newsletter/Data/Templates.cs ================================================ namespace Newsletter.Data; /// /// Collection of all the templates /// public record Templates(Template Email, Template RecentItem); /// /// Single template /// public record Template(string Html, string PlainText); ================================================ FILE: Samples/Newsletter/Newsletter/Effects/Config.cs ================================================ namespace Newsletter.Effects; /// /// Config record /// public record Config( string MembersFolder, // Location of the exported member list string TemplateFolder, // Location of the HTML and plain-text templates string LettersFolder, // Location to save the newsletters to string SiteUrl, // https://paullouth.com site-url int FeatureImageWidth, // Maximum size of the feature image string ContentApiKey, // GhostCMS API key (you need to generate this in the Admin console of GhostCMS) Option SendGridApiKey // Api key for SendGrid! ); /// /// Application configuration /// public static class Config where RT : Has where M : Monad, Fallible { static readonly K configIO = RT.Ask; /// /// Location of the exported member list /// public static K membersFolder => configIO.Map(t => t.MembersFolder); /// /// Location of the HTML and plain-text templates /// public static K templateFolder => configIO.Map(t => t.TemplateFolder); /// /// Location to save the newsletters to /// public static K lettersFolder => configIO.Map(t => t.LettersFolder); /// /// https://paullouth.com site-url /// public static K siteUrl => configIO.Map(t => t.SiteUrl); /// /// Maximum size of the feature image /// public static K featureImageWidth => configIO.Map(t => t.FeatureImageWidth); /// /// GhostCMS API key (you need to generate this in the Admin console of GhostCMS) /// public static K contentApiKey => configIO.Map(t => t.ContentApiKey); /// /// Api key for SendGrid! /// public static K sendGridApiKey => from k in configIO.Map(t => t.SendGridApiKey) from _ in when(k.IsNone, error((Error)"SendGrid key not set. No emails will be sent")) select (string)k; } ================================================ FILE: Samples/Newsletter/Newsletter/Effects/Email.cs ================================================ using Newsletter.Effects.Traits; using SendGrid.Helpers.Mail; namespace Newsletter.Effects; /// /// Json parser /// public static class Email where RT : Has, Has where M : MonadIO, Fallible { static K trait => Has.ask; /// /// Send an email /// public static K send( EmailAddress from, EmailAddress to, string subject, string plainTextContent, string htmlContent) => from key in Config.sendGridApiKey from eml in trait from res in liftIO(io => eml.Send(key, @from, to, subject, plainTextContent, htmlContent, io.Token)) select unit; } ================================================ FILE: Samples/Newsletter/Newsletter/Effects/Image.cs ================================================ using Newsletter.Effects.Traits; namespace Newsletter.Effects; /// /// Json parser /// public static class Image where RT : Has where M : Monad { public static readonly K trait = Has.ask; public static K scaleToMaximumWidthJpeg(byte[] input, int maxWidthInPixels) => trait.Map(t => t.ScaleToMaximumWidthJpeg(input, maxWidthInPixels)); } ================================================ FILE: Samples/Newsletter/Newsletter/Effects/Impl/Email.cs ================================================ using SendGrid; using SendGrid.Helpers.Mail; using Newsletter.Effects.Traits; namespace Newsletter.Effects.Impl; public class Email : EmailIO { public static readonly EmailIO Default = new Email(); public async Task Send( string apiKey, EmailAddress from, EmailAddress to, string subject, string plainTextContent, string htmlContent, CancellationToken token) { var client = new SendGridClient(apiKey); var msg = MailHelper.CreateSingleEmail(from, to, subject, plainTextContent, htmlContent); var response = await client.SendEmailAsync(msg, token); return response; } } ================================================ FILE: Samples/Newsletter/Newsletter/Effects/Impl/Image.cs ================================================ using SixLabors.ImageSharp; using SixLabors.ImageSharp.Processing; using Img = SixLabors.ImageSharp.Image; using Newsletter.Effects.Traits; namespace Newsletter.Effects.Impl; public class Image : ImageIO { public static readonly ImageIO Default = new Image(); public byte[] ScaleToMaximumWidthJpeg(ReadOnlySpan inputBytes, int maxWidthInPixels) { using var image = Img.Load(inputBytes); if (image.Width < maxWidthInPixels) return inputBytes.ToArray(); var height = (int)((double)image.Height * maxWidthInPixels / image.Width); image.Mutate(x => x.Resize(maxWidthInPixels, height)); using var stream = new MemoryStream(); image.SaveAsJpeg(stream); return stream.ToArray(); } } ================================================ FILE: Samples/Newsletter/Newsletter/Effects/Impl/Json.cs ================================================ using System.Text.Json; using Newsletter.Effects.Traits; namespace Newsletter.Effects.Impl; public record Json : JsonIO { public static readonly JsonIO Default = new Json(); public JsonDocument Parse(string text) => JsonDocument.Parse(text); } ================================================ FILE: Samples/Newsletter/Newsletter/Effects/Impl/Web.cs ================================================ using Newsletter.Effects.Traits; namespace Newsletter.Effects.Impl; public record Web : WebIO { public static readonly WebIO Default = new Web(); public async Task Download(Uri uri, HttpClient client) => await client.GetByteArrayAsync(uri); } ================================================ FILE: Samples/Newsletter/Newsletter/Effects/Json.cs ================================================ using System.Text.Json; using Newsletter.Effects.Traits; namespace Newsletter.Effects; /// /// Json parser /// public static class Json where RT : Has where M : Monad { static readonly K trait = Has.ask; public static K readJson(string text) => trait.Map(t => t.Parse(text)); } ================================================ FILE: Samples/Newsletter/Newsletter/Effects/Runtime.cs ================================================ using Newsletter.Effects.Traits; namespace Newsletter.Effects; public record Runtime(RuntimeEnv Env) : Has, WebIO>, Has, FileIO>, Has, JsonIO>, Has, EmailIO>, Has, ImageIO>, Has, ConsoleIO>, Has, EncodingIO>, Has, DirectoryIO>, Has, Config>, Has, HttpClient>, IDisposable { static Config Common(string repoRootFolder, Option sendGridKey) => new ("", Path.Combine(repoRootFolder, "templates"), "", "https://paullouth.com", 700, // Public Ghost-API key. It's ok this only accesses public data, // just don't abuse it, or I'll revoke it! "6d15eacab40271bcdd552306d0", sendGridKey); public static Runtime Test(string repoRootFolder, Option sendGridKey) => New(new RuntimeEnv( new HttpClient(), Common(repoRootFolder, sendGridKey) with { MembersFolder = Path.Combine(repoRootFolder, "members-test"), LettersFolder = Path.Combine(repoRootFolder, "letters-test") })); public static Runtime Live(string repoRootFolder, Option sendGridKey) => New(new RuntimeEnv( new HttpClient(), Common(repoRootFolder, sendGridKey) with { MembersFolder = Path.Combine(repoRootFolder, "members-live"), LettersFolder = Path.Combine(repoRootFolder, "letters-live") })); public static Runtime New(RuntimeEnv env) => new (env); static K, FileIO> Has, FileIO>.Ask => pure, FileIO>(LanguageExt.Sys.Live.Implementations.FileIO.Default); static K, EncodingIO> Has, EncodingIO>.Ask => pure, EncodingIO>(LanguageExt.Sys.Live.Implementations.EncodingIO.Default); static K, DirectoryIO> Has, DirectoryIO>.Ask => pure, DirectoryIO>(LanguageExt.Sys.Live.Implementations.DirectoryIO.Default); static K, ConsoleIO> Has, ConsoleIO>.Ask => pure, ConsoleIO>(LanguageExt.Sys.Live.Implementations.ConsoleIO.Default); static K, JsonIO> Has, JsonIO>.Ask => pure, JsonIO>(Impl.Json.Default); static K, EmailIO> Has, EmailIO>.Ask => pure, EmailIO>(Impl.Email.Default); static K, WebIO> Has, WebIO>.Ask => pure, WebIO>(Impl.Web.Default); static K, ImageIO> Has, ImageIO>.Ask => pure, ImageIO>(Impl.Image.Default); static K, Config> Has, Config>.Ask { get; } = liftEff(rt => rt.Env.Config); static K, HttpClient> Has, HttpClient>.Ask { get; } = liftEff(rt => rt.Env.HttpClient); public void Dispose() => Env.Dispose(); } ================================================ FILE: Samples/Newsletter/Newsletter/Effects/RuntimeEnv.cs ================================================ namespace Newsletter.Effects; public record RuntimeEnv(HttpClient HttpClient, Config Config) : IDisposable { public void Dispose() => HttpClient.Dispose(); } ================================================ FILE: Samples/Newsletter/Newsletter/Effects/Traits/EmailIO.cs ================================================ using SendGrid; using SendGrid.Helpers.Mail; namespace Newsletter.Effects.Traits; public interface EmailIO { Task Send( string apiKey, EmailAddress from, EmailAddress to, string subject, string plainTextContent, string htmlContent, CancellationToken token); } ================================================ FILE: Samples/Newsletter/Newsletter/Effects/Traits/ImageIO.cs ================================================ namespace Newsletter.Effects.Traits; public interface ImageIO { byte[] ScaleToMaximumWidthJpeg(ReadOnlySpan inputBytes, int maxWidthInPixels); } ================================================ FILE: Samples/Newsletter/Newsletter/Effects/Traits/JsonIO.cs ================================================ using System.Text.Json; namespace Newsletter.Effects.Traits; public interface JsonIO { public JsonDocument Parse(string text); } ================================================ FILE: Samples/Newsletter/Newsletter/Effects/Traits/WebIO.cs ================================================ namespace Newsletter.Effects.Traits; public interface WebIO { public Task Download(Uri uri, HttpClient client); } ================================================ FILE: Samples/Newsletter/Newsletter/Effects/Web.cs ================================================ using Newsletter.Effects.Traits; namespace Newsletter.Effects; /// /// Web downloader /// public static class Web where RT : Has, Has, Has where M : MonadIO { static K trait => Has.ask; static K client => Has.ask; public static K downloadText(Uri uri) => from en in Enc.encoding from bs in download(uri) select en.GetString(bs); public static K downloadBase64(Uri uri) => download(uri).Map(Convert.ToBase64String); public static K download(Uri uri) => from c in client from t in trait from r in liftIO(() => t.Download(uri, c)) select r; } ================================================ FILE: Samples/Newsletter/Newsletter/GlobalUsings.cs ================================================ global using LanguageExt; global using LanguageExt.Sys; global using LanguageExt.Sys.IO; global using LanguageExt.Traits; global using LanguageExt.Common; global using LanguageExt.Sys.Traits; global using static LanguageExt.Prelude; ================================================ FILE: Samples/Newsletter/Newsletter/JsonExtensions.cs ================================================ using System.Text.Json; namespace Newsletter; public static class JsonExtensions { public static JsonElement Get(this JsonElement element, string key) { try { return element.GetProperty(key); } catch (KeyNotFoundException) { throw new KeyNotFoundException($"'{key}' not found"); } } public static Iterable Enumerate(this JsonElement element) => element .EnumerateArray() .AsIterable(); } ================================================ FILE: Samples/Newsletter/Newsletter/Newsletter.csproj ================================================  Exe net10.0 enable enable fbefbe46-9ced-4f15-92b1-db45597ea1e3 ================================================ FILE: Samples/Newsletter/Newsletter/Newsletter.sln ================================================  Microsoft Visual Studio Solution File, Format Version 12.00 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Newsletter", "Newsletter.csproj", "{C6A964F8-57F7-4AA6-8A4F-A5847CDB4F9B}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {C6A964F8-57F7-4AA6-8A4F-A5847CDB4F9B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {C6A964F8-57F7-4AA6-8A4F-A5847CDB4F9B}.Debug|Any CPU.Build.0 = Debug|Any CPU {C6A964F8-57F7-4AA6-8A4F-A5847CDB4F9B}.Release|Any CPU.ActiveCfg = Release|Any CPU {C6A964F8-57F7-4AA6-8A4F-A5847CDB4F9B}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal ================================================ FILE: Samples/Newsletter/Newsletter/Program.cs ================================================ using System.Text.Json; using Microsoft.Extensions.Configuration.UserSecrets; using Newsletter.Command; using Newsletter.Effects; // Setup var path = args is [_, var p] ? p : Environment.CurrentDirectory; var secrets = loadUserSecrets(); var sendGridKey = secrets.Find("SendGridKey"); var runtime = args is ["live", ..] ? Runtime.Live(path, sendGridKey) : Runtime.Test(path, sendGridKey); // Run var result = Send, Runtime> .newsletter .Run(runtime); // Show results switch (result) { case Fin.Succ: Console.WriteLine("Complete"); break; case Fin.Fail (var error): Console.WriteLine(error); break; } // Loads the user-secrets controlled by "dotnet user-secrets" HashMap loadUserSecrets() { var userSecretsId = "fbefbe46-9ced-4f15-92b1-db45597ea1e3"; var path = PathHelper.GetSecretsPathFromSecretsId(userSecretsId); var text = File.ReadAllText(path); var json = JsonDocument.Parse(text); return json.RootElement .EnumerateObject() .AsIterable() .Map(e => (e.Name, e.Value.GetString() ?? "")) .ToHashMap(); } ================================================ FILE: Samples/Newsletter/Newsletter/UI/Display.cs ================================================ using Newsletter.Data; namespace Newsletter.UI; public static class Display where RT : Has where M : MonadIO, Fallible { public static K emptyLine => Console.writeEmptyLine; public static K writeLine(string line) => Console.writeLine(line); public static K confirmSent => Console.writeLine("Newsletter successfully sent to members"); public static K error(Error err) => Console.writeLine(err.ToString()); public static K showWhatsAboutToHappen(Seq members) => from _1 in writeLine($"You're about to send the newsletter to {members.Count} members") from _2 in emptyLine from _3 in writeLine("Below are some email addresses from the batch:") from _4 in members.Take(10) .Map(m => $"\t{m.Email}") .Traverse(writeLine) from _5 in emptyLine from _6 in writeLine("Are you sure you want to send the emails? (y/n)") select unit; } ================================================ FILE: Samples/Newsletter/README.md ================================================ # newsletter Newsletter generator for my paullouth.com site This shows off the effect system of language-ext and how to create and use runtimes. ================================================ FILE: Samples/Newsletter/members-test/members.2024-07-29.csv ================================================ id,email,name,note,subscribed_to_emails,complimentary_plan,stripe_customer_id,created_at,deleted_at,labels,tiers 65f31f852ac0342e8bc9373e,paul@example.com,Paul Louth,,true,,,2024-03-14T16:02:13.000Z,,, 66a73e0798be6a03e27d0e12,alan@example.com,Alan Turing,,true,,,2024-07-29T07:00:23.000Z,,, 65ddcee799e5fbe500fe8da7,isaac@example.com,Isaac Newton,,true,,,2024-02-27T12:00:39.000Z,,, 65de0dd899e5fbe500fe8e7f,sophie@example.com,Sophie Wilson,,true,,,2024-02-27T16:29:12.000Z,,, 65de17aa99e5fbe500fe8e87,delia@example.com,"Delia Derbyshire",,true,,,2024-02-27T17:11:06.000Z,,, ================================================ FILE: Samples/Newsletter/templates/email.html ================================================ Notes from a Small Functional Island ================================================ FILE: Samples/Newsletter/templates/email.txt ================================================ # Notes from a Small Functional Island ## [POST_TITLE] By Paul Louth - [POST_DATE] Read the original article on paullouth.com at: [POST_URL] [POST_CONTENT] ## Keep reading [RECENT_ARTICLES] Paul Louth © [YEAR_RANGE] You're receiving this email because you signed up to the newsletter at https://paullouth.com. You can unsubscribe by going to the site, logging in, and toggling the 'Email Newsletter' option. Any problems or if you need to reach out for any reason, drop me an email to: paul@paullouth.com ================================================ FILE: Samples/Newsletter/templates/recent-item.html ================================================
[EXCERPT]
================================================ FILE: Samples/Newsletter/templates/recent-item.txt ================================================ * [TITLE] [EXCERPT] [URL] ================================================ FILE: Samples/PipesExamples/PipesExamples.csproj ================================================ Exe net10.0 enable enable ================================================ FILE: Samples/PipesExamples/Program.cs ================================================ using System.Diagnostics; using System.Net.Sockets; using LanguageExt; using LanguageExt.Common; using LanguageExt.Pipes; using LanguageExt.Traits; using static LanguageExt.Pipes.ProducerT; using static LanguageExt.Pipes.PipeT; using static LanguageExt.Pipes.ConsumerT; using static LanguageExt.Prelude; var prod = yieldAll(Range(1, 100)); var pipe = from x in awaiting() from _ in x == 10 ? endOfStream() : yield(x) select unit; var cons = from x in awaiting() from _ in writeLine(x) select unit; var effect = prod | pipe | cons; var result = effect.RunToEnd().Run(); static PipeT endOfStream() => error(Errors.EndOfStream); static IO writeLine(object? value) => IO.lift(() => Console.Write($"{value} ")); public record Slice(int Length, int Offset, byte[] Buffer); public static class EffectExtensions { public static K RunToEnd(this EffectT effect) where M : MonadIO, Fallible => effect.Run() | @catch(Errors.EndOfStream, pure(unit)); } /* var p = yieldAll(Range(1, 10000000)); var o = foldUntil(Time: Schedule.spaced(10) | Schedule.recurs(3), Fold: (s, v) => s + v, Pred: v => v.Value % 10000 == 0, Init: 0, Item: awaiting()); var c = from x in awaiting() from _ in writeLine($"{x}") select unit; var e = p | o | c; var r = e.Run().Run(); Console.WriteLine("Done"); static IO writeLine(object? value) => IO.lift(() => Console.WriteLine(value)); */ ================================================ FILE: Samples/Streams/Console.cs ================================================ using LanguageExt; using static LanguageExt.Prelude; namespace Streams; // Simple IO wrappers of Console public static class Console { public static readonly IO emptyLine = lift(System.Console.WriteLine); public static IO setForegroundColour(ConsoleColor colour) => lift(() => System.Console.ForegroundColor = colour).Map(_ => unit); public static IO colour => lift(() => System.Console.ForegroundColor); public static readonly IO red = setForegroundColour(ConsoleColor.Red); public static readonly IO green = setForegroundColour(ConsoleColor.Green); public static readonly IO yellow = setForegroundColour(ConsoleColor.Yellow); public static readonly IO cyan = setForegroundColour(ConsoleColor.Cyan); public static readonly IO magenta = setForegroundColour(ConsoleColor.Magenta); public static IO writeLine(A? line) => lift(() => System.Console.WriteLine(line?.ToString() ?? "(null)")); public static IO write(A? text) => lift(() => System.Console.Write(text?.ToString() ?? "(null)")); public static IO readLine => lift(() => System.Console.ReadLine() ?? ""); public static IO readKey => IO.lift(System.Console.ReadKey) >> writeLine(""); } ================================================ FILE: Samples/Streams/CountForever.cs ================================================ using LanguageExt; using LanguageExt.Pipes; using static LanguageExt.Prelude; using static Streams.Console; namespace Streams; public static class CountForever { public static IO run => from f in fork(example.Iter()).As() from k in readKey from r in f.Cancel select unit; static SourceT naturals => SourceT.lift(Range(0, long.MaxValue)); static SourceT example => from v in naturals where v % 10000 == 0 from _ in writeLine($"{v:N0}") where false select unit; } ================================================ FILE: Samples/Streams/CountForeverAsync.cs ================================================ using LanguageExt; using static LanguageExt.Prelude; using static Streams.Console; namespace Streams; public static class CountForeverAsync { public static IO run => from f in fork(example.Iter()).As() from k in readKey from r in f.Cancel select unit; static SourceT naturals => SourceT.lift(naturalsEnum()); static SourceT example => from v in naturals from _ in writeLine($"{v:N0}") where false select unit; static async IAsyncEnumerable naturalsEnum() { for (var i = 0L; i < long.MaxValue; i++) { yield return i; await Task.Delay(1000); } } } ================================================ FILE: Samples/Streams/Folding.cs ================================================ using LanguageExt; using static Streams.Console; using static LanguageExt.Prelude; namespace Streams; public static class Folding { public static IO run => example(1000).Iter().As(); static SourceT naturals(int n) => Range(0, n).AsSourceT(); static SourceT example(int n) => from v in naturals(n).FoldUntil((s, x) => s + x, s => s.State % 4 == 0, 0) where v % 10 == 0 from _ in writeLine(v) select unit; } ================================================ FILE: Samples/Streams/Grouping.cs ================================================ using LanguageExt; using static Streams.Console; using static LanguageExt.Prelude; namespace Streams; /// /// Based on 'Grouping effects' from 'ListT done right' /// /// https://wiki.haskell.org/ListT_done_right /// public static class Grouping { public static IO run => runTestIO("test 1", test1) >> runTestIO("test 2", test2); static IO runTestIO(string name, SourceT test) => runTest(name, test1).Iter().As() >> emptyLine; static SourceT runTest(string name, SourceT test) => from t in writeLine($"{name}") from r in test from _ in write($"{r} ") where false select unit; static SourceT test1 = from r in atomIO(0) from n in next(r) + next(r) >> (next(r) + next(r) >> next(r) + next(r)) select n; static SourceT test2 = from r in atomIO(0) from n in (next(r) + next(r) >> next(r) + next(r)) >> next(r) + next(r) select n; static SourceT next(Atom atom) => from x in valueIO(atom) from _ in writeIO(atom, x + 1) select x; } ================================================ FILE: Samples/Streams/HeadsAndTails.cs ================================================ /* using LanguageExt; using LanguageExt.Streaming; using static Streams.Console; using static LanguageExt.Prelude; namespace Streams; public static class HeadsAndTails { static readonly StreamT collection = from item in Iterable(1, 2, 3, 4, 5, 6, 7, 8, 9, 10).AsStream() from col in colour >> red from _ in writeLine($"\tEvaluated item: {item}") >> setForegroundColour(col) select item; public static IO run => headExample >> tailExample.Run(); static IO headExample => from _ in writeLine("Head example") from h in collection.Head() from x in writeLine($"\tHead is: {h}") select unit; static StreamT tailExample => from _ in writeLine("Tail example") from x in collection.Tail() from y in writeLine($"\tYielded: {x}") where false select unit; } */ ================================================ FILE: Samples/Streams/Menu.cs ================================================ using LanguageExt; using static LanguageExt.Prelude; using static Streams.Console; namespace Streams; public static class Menu { public static IO run => from _ in introduction from ky in readKey >> green from ex in ky.Key switch { ConsoleKey.D1 => CountForever.run, ConsoleKey.D2 => CountForeverAsync.run, ConsoleKey.D3 => SumOfSquares.run, ConsoleKey.D4 => Grouping.run, // ConsoleKey.D5 => HeadsAndTails.run, ConsoleKey.D6 => Folding.run, ConsoleKey.D7 => Merging.run, ConsoleKey.D8 => Zipping.run, ConsoleKey.D9 => OptionalItems.run, ConsoleKey.D0 => SourceStream.run, ConsoleKey.X => RecursionIO.run, _ => unitIO } from _1 in run select ex; static IO introduction => cyan >> writeLine("Examples") >> writeLine("1. Count forever") >> writeLine("2. Count forever (async, with per-item 1s delay)") >> writeLine("3. Sum of squares") >> writeLine("4. Grouping test") >> writeLine("5. Heads and tails [DEPRECATED]") >> writeLine("6. Folding") >> writeLine("7. Merging") >> writeLine("8. Zipping") >> writeLine("9. Optional items") >> writeLine("0. Source stream") >> writeLine("Enter a number for the example you wish to run"); } ================================================ FILE: Samples/Streams/Merging.cs ================================================ using LanguageExt; using static Streams.Console; using static LanguageExt.Prelude; namespace Streams; public static class Merging { public static IO run => example(20).Iter().As() >> emptyLine; static SourceT example(int n) => from v in evens(n) | odds(n) where false select unit; static SourceT evens(int n) => from x in Range(0, n).AsSourceT() where isEven(x) from _ in magenta >> write($"{x} ") select x; static SourceT odds(int n) => from x in Range(0, n).AsSourceT() where isOdd(x) from _ in yellow >> write($"{x} ") select x; static bool isOdd(int n) => (n & 1) == 1; static bool isEven(int n) => !isOdd(n); } ================================================ FILE: Samples/Streams/OptionalItems.cs ================================================ using LanguageExt; using static LanguageExt.Prelude; namespace Streams; public static class OptionalItems { public static IO run => from _0 in Console.writeLine("starting") from _1 in example(100).Iter() from _2 in Console.writeLine("done") select unit; static SourceT, Unit> example1(int n) => from x in SourceT.liftM(asyncStream(n)) from _ in Console.write($"{x} ") where false select unit; static SourceT example(int n) => from x in asyncStream(n).SomeSource() from _ in Console.write($"{x} ") where false select unit; static bool isAllowed(int x) => (x & 1) == 1; static async IAsyncEnumerable> asyncStream(int n) { foreach (var x in Range(1, n)) { var option = isAllowed(x) ? OptionT.lift(IO.pure(x)) : OptionT.None; var r = await Task.FromResult(option); yield return r; } } } ================================================ FILE: Samples/Streams/Program.cs ================================================ using Streams; Menu.run.Run(); ================================================ FILE: Samples/Streams/RecursionIO.cs ================================================ using LanguageExt; using LanguageExt.Common; using static Streams.Console; using static LanguageExt.Prelude; namespace Streams; public static class RecursionIO { public static IO run => from _ in writeLine("Enter a number to count from") from s in readLine from n in parseInt(s) | IO.fail("expected a number!") from r in recurse(n) >> emptyLine select r; public static IO recurse(int n) => from _ in write($"{n} ") from r in when(n > 0, recurse(n - 1)) select r; } ================================================ FILE: Samples/Streams/SourceStream.cs ================================================ using LanguageExt; using static Streams.Console; using static LanguageExt.Prelude; namespace Streams; public class SourceStream { public static IO run => from c in ConduitT.makeM() from f in fork(subscribe(c.Source).Iter()) from _ in writeLine("Type something and press enter (empty-line ends the demo)") >> interaction(c.Sink) select unit; static IO interaction(SinkT sink) => repeat(from l in readLine from _ in deliver(sink, l) select unit) | @catch(unitIO); static IO deliver(SinkT sink, string line) => guardIO(line != "") >> sink.Post(line); static SourceT subscribe(SourceT source) => from v in source from _ in writeLine(v) where false select unit; } ================================================ FILE: Samples/Streams/Streams.csproj ================================================  Exe net10.0 enable enable Streams ================================================ FILE: Samples/Streams/SumOfSquares.cs ================================================ using LanguageExt; using LanguageExt.Traits; using static Streams.Console; using static LanguageExt.Prelude; namespace Streams; /// /// Based on 'Sum of squares' from 'ListT done right' /// /// https://wiki.haskell.org/ListT_done_right /// public static class SumOfSquares { public static IO run => from _ in writeLine("Enter a number to find the sum of squares") from s in readLine from n in parseLong(s) | IO.fail("expected a number!") from x in example(n).Iter() select unit; static SourceT squares(long n) where M : MonadIO, Alternative => SourceT.lift(Range(0, n).Select(v => v * v).Where(v => v <= n)); static SourceT example(long n) => from x in squares(n) from y in squares(n) where x + y == n from _ in writeLine($"Sum of squares! {(x, y)}") where false select (x, y); } ================================================ FILE: Samples/Streams/Zipping.cs ================================================ using LanguageExt; using static Streams.Console; using static LanguageExt.Prelude; namespace Streams; public static class Zipping { public static IO run => example(10).Iter().As(); static SourceT example(int n) => from v in evens(n).Zip(odds(n)) from _ in writeLine(v) select unit; static SourceT evens(int n) => from x in Range(0, n).AsSourceT() where isEven(x) select x; static SourceT odds(int n) => from x in Range(0, n).AsSourceT() where isOdd(x) select x; static bool isOdd(int n) => (n & 1) == 1; static bool isEven(int n) => !isOdd(n); } ================================================ FILE: Samples/TestBed/ApplicativeTest.cs ================================================ using LanguageExt.Common; namespace TestBed; using System; using System.Diagnostics; using LanguageExt; using System.Threading.Tasks; using static LanguageExt.Prelude; public static class ApplicativeTest { static Eff delay(int milliseconds) => liftIO(async () => { await Task.Delay(milliseconds); return unit; }); static Eff parse(string str) => from x in parseInt(str).ToEff(Error.New($"parse error: expected int, got: '{str}'")) from _ in delay(10000) select x; static Eff add(string sa, string sb, string sc, string sd, string se, string sf) => SuccessEff(curry(addPure)) .Apply(parse(sa)) .Apply(parse(sb)) .Apply(parse(sc)) .Apply(parse(sd)) .Apply(parse(se)) .Apply(parse(sf)); static int addPure(int a, int b, int c, int d, int e, int f) => a + b + c + d + e + f; public static void Test() { report(add("1", "2", "3", "4", "5", "6")); report(add("a", "b", "c", "d", "e", "f")); } static void report(Eff ma) { var sw = Stopwatch.StartNew(); var r = ma.Run(); sw.Stop(); Console.WriteLine($"Result: {r} in {sw.ElapsedMilliseconds}ms"); } } ================================================ FILE: Samples/TestBed/AtomHashMapTests.cs ================================================ using System; using System.Reactive.Linq; using LanguageExt; using static LanguageExt.Prelude; namespace TestBed; public class AtomHashMapTests { public static void Test() { var thm = TrackingHashMap(); Console.WriteLine(thm); thm = thm.Add(100, "World"); thm = thm.Snapshot(); thm = thm.SetItem(100, "Hello"); Console.WriteLine(thm.Changes); var xs = AtomHashMap(); xs.OnEntryChange().Subscribe(pair => Console.WriteLine(pair)); xs.Add("Hello", 456); xs.SetItem("Hello", 123); xs.Remove("Hello"); xs.Remove("Hello"); var rx = Ref("Hello"); var ry = Ref("World"); Observable.Merge(rx.OnChange(), ry.OnChange()) .Subscribe(Console.WriteLine); atomic(() => { swap(rx, x => $"1. {x}"); swap(ry, y => $"2. {y}"); }); } } ================================================ FILE: Samples/TestBed/AwaitAnyTest.cs ================================================ using System; using LanguageExt; using System.Threading.Tasks; using static LanguageExt.Prelude; namespace TestBed; public class AwaitAnyTest { public static void Run() { Console.WriteLine("Started"); var eff = awaitAny(uninterruptible(delayed("A", 9)), uninterruptible(delayed("B", 7)), uninterruptible(delayed("C", 5))); using var env = EnvIO.New(); ignore(eff.Run(env)); Console.ReadKey(); env.Source.Cancel(); Console.WriteLine("Cancellation trigger, press any key to exit"); Console.ReadKey(); } static Eff delayed(string info, int time) => Eff.lift(async e => { await Task.Delay(time * 1000, e.Token); Console.WriteLine(info); return unit; }); } ================================================ FILE: Samples/TestBed/BracketTest.cs ================================================ using System; using LanguageExt; using static LanguageExt.Prelude; namespace TestBed; public class BracketTest { class Test : IDisposable { public void Dispose() { Console.WriteLine("Disposing Test"); } } public static void Run() { var workflow = bracketIO( from d in use(() => new Test()) //from _ in fail() select d ); ignore(workflow.Run()); } static IO fail() => IO.lift(() => throw new Exception("boom")); } ================================================ FILE: Samples/TestBed/FreeTests.cs ================================================ using System; using System.IO; using LanguageExt; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace TestBed; public abstract record Op : K; public record ReadLinesOp(string Path, Func, A> Next): Op; public record WriteLinesOp(string Path, Seq Lines, Func Next) : Op; public partial class Op : Functor { public static K Map(Func f, K ma) => ma switch { ReadLinesOp (var path, var next) => new ReadLinesOp(path, x => f(next(x))), WriteLinesOp (var path, var lines, var next) => new WriteLinesOp(path, lines, x => f(next(x))) }; } public partial class Op { public static Free> readLines(string path) => Free.lift(new ReadLinesOp>(path, identity)); public static Free writeLines(string path, Seq lines) => Free.lift(new WriteLinesOp(path, lines, identity)); } public static class FreeTests { public static void Test() { var repr = from lines in Op.readLines("c:\\temp\\test.txt") from _ in Op.writeLines("c:\\temp\\test2.txt", lines) select unit; var comp = Interpret(repr); comp.Run(); } static IO Interpret(K, A> ma) => ma switch { Pure(var value) => IO.pure(value), Bind(var bind) => bind switch { ReadLinesOp> (var path, var next) => ReadLines(path).Map(next).Bind(Interpret), WriteLinesOp> (var path, var lines, var next) => WriteLines(path, lines).Map(next).Bind(Interpret) } }; static IO> ReadLines(string path) => IO.liftAsync(() => File.ReadAllLinesAsync(path)).Map(toSeq); static IO WriteLines(string path, Seq lines) => IO.liftAsync(async () => { await File.WriteAllLinesAsync(path, lines); return unit; }); } ================================================ FILE: Samples/TestBed/Issues/Discussion1527.cs ================================================ using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using LanguageExt; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace Issues; public static class Discussion1527 { public static void Run() { Eff r1 = +Think(1000); IO r2 = +Think(1000); Console.WriteLine(r1.Run()); Console.WriteLine(r2.Run()); /*var res = Think(1000).Run(); Console.WriteLine($"Output: {res}"); var sum1 = Source.forever(1) .FoldWhile( (s, x) => s + x, (s, _) => s <= 10, 0 ) .Take(1) .Last() .Run(); Console.WriteLine(sum1); var sum2 = SourceT.forever(1) .FoldWhile( (s, x) => s + x, x => x.State <= 10, 0 ) .As() .Take(1) .Last() .Run(); Console.WriteLine(sum2);*/ } static IO> Think(Duration duration, Func? predicate = null) { var observe = Observations() .FoldUntil((s, o) => s + o, x => predicate?.Invoke(x.Value) ?? false, Seq()) .TakeFor(duration); var timeout = SourceT.pure>([]).Delay(duration); return +(observe | timeout).Last(); } static IO> OneWithTimeout(Duration duration) { var empty = SourceT.pure>(None); var timeout = empty.Delay(duration); var observations = Observations().Map(Some); return +(observations | timeout).Take(1).Last(); } static IO> AllForPeriod(Duration duration) => +Observations() .TakeFor(duration) .Collect(); static IO> AllForPeriod(Duration duration, Func predicate) => +Observations() .Filter(predicate) .TakeFor(duration) .Collect(); public static K Think(Duration duration) where M : MonadIO, Fallible, Alternative { var empty = SourceT.liftM(M.Empty()); var timeout = empty.Delay(duration); var observations = Observations(); return (observations | timeout) .Take(1) .Last(); } public interface IObservation; public record Obs(DateTime When) : IObservation; static IObservation MakeObservation(DateTime dt) { //Console.WriteLine($"Making observation: {dt.Ticks}"); return new Obs(dt); } static SourceT tickTockIO() where M : MonadIO, Alternative { return from token in M.LiftIO(IO.token) from value in SourceT.lift(go(token)) select value; static async IAsyncEnumerable go(CancellationToken token) { while (true) { if(token.IsCancellationRequested) yield break; await Task.Delay(TimeSpan.FromMilliseconds(5000), token); var now = DateTime.Now; yield return now; } } } public static SourceT Observations() where M : MonadIO, Alternative => tickTockIO().Map(MakeObservation); /* public static IO> Think(Duration duration) => Observations .Take(1) .TakeFor(duration) .Last() .Map(Some) | NoObservations; public static IO> Think2(Duration duration) => +Observations .TakeFor(duration) .Collect();*/ static readonly IO> NoObservations = IO.pure>(None); } ================================================ FILE: Samples/TestBed/Issues/Issue1497.cs ================================================ using System.Collections.Generic; using System.Linq; namespace Issues; using static AppPrelude; using LanguageExt.Pipes; using LanguageExt; using static LanguageExt.Prelude; using LanguageExt.Traits; using System.Runtime.CompilerServices; using System.Net.Sockets; using System.Net; using System.Text; using LanguageExt.Common; using LanguageExt.Sys; using M = Application; public static class AppPrelude { public static void Test() { var effect = Module.producer | Module.pipe | Module.consumer; // effect.Run().As().Run(); effect.Run().As().Run.Run(new (AtomHashMap(("", "")))).Run().Ignore(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Application As(this K self) => (Application)self; } public record ApplicationState(AtomHashMap Cache); public record Application(StateT Run) : K; public class Application : Deriving.Monad>, Deriving.Stateful, ApplicationState>, MonadIO { public static K LiftIO(IO ma) { return new Application(StateT.liftIO(ma)); } public static K, A> Transform(K fa) { return fa.As().Run; } public static K CoTransform(K, A> fa) { return new Application(fa.As()); } public static K Actions(IterableNE> fas) => new Application( from s in StateT.get() from r in fas.Select(fa => fa.As().Run.Run(s).Kind()).Actions() from _ in StateT.put(r.State) select r.Value); } public static class Module where M : MonadIO { public static ProducerT producer => from _1 in IO.lift(() => System.Console.WriteLine("Producer")) from _2 in ProducerT.yieldRepeatIO(IO.pure(1)) select unit; public static PipeT pipe = from i in PipeT.awaiting() from _1 in IO.lift(() => System.Console.WriteLine("Pipe")) from _2 in PipeT.yield(i) select unit; public static ConsumerT consumer = from x in ConsumerT.awaiting() from _1 in IO.lift(() => System.Console.WriteLine("Consumer")) select unit; } ================================================ FILE: Samples/TestBed/Issues/IssueTests.cs ================================================ using System; using System.Runtime.Serialization; using LanguageExt; namespace Issues; public static class Issue1453 { public static void Test() { Deserialize("invalid") .BindFail(_ => Fallback()) .Match(Succ: time => { Console.WriteLine(time); return Unit.Default; }, Fail: _ => { Console.WriteLine("Fail"); return Unit.Default; }); Try Deserialize(string dateTime) => Try.lift(() => throw new SerializationException()); Try Fallback() => Try.Succ(DateTime.MinValue); } } ================================================ FILE: Samples/TestBed/PipesTest.cs ================================================ using System; using LanguageExt; using LanguageExt.Common; using LanguageExt.Pipes; using LanguageExt.Sys; using LanguageExt.Sys.Live; using static LanguageExt.Prelude; using static LanguageExt.Pipes.Producer; using static LanguageExt.Pipes.Consumer; using static LanguageExt.Pipes.Pipe; using static LanguageExt.UnitsOfMeasure; namespace TestBed; public static class PipesTestBed { static Producer numbers(int n, int failOn) => from _ in yield(n) from t in Time.sleepFor(1000 * ms) from x in failOn == n ? FailEff(Error.New($"failed on {n}")) : unitEff from r in n < 0 ? Pure(unit) : numbers(n - 1, failOn) select r; public static Effect effect => merge(numbers(10, 5), numbers(20, 0)) | writeLine(); static Consumer writeLine() => from x in awaiting() from _ in Console.writeLine($"{x}") from r in writeLine() select r; public static Effect effect1 => repeat(producer) | doubleIt | consumer; static Producer producer => from _1 in Console.writeLine("before") from _2 in yieldAll(Range(1, 5)) from _3 in Console.writeLine("after") select unit; static Pipe doubleIt => from x in awaiting() from _ in yield(x * 2) select unit; static Consumer consumer => from i in awaiting() from _ in Console.writeLine(i.ToString()) from r in consumer select r; } ================================================ FILE: Samples/TestBed/Program.cs ================================================ //////////////////////////////////////////////////////////////////////////////////////////////////////// // // // // // NOTE: This is just my scratch pad for quickly testing stuff, not for human consumption // // // // // //////////////////////////////////////////////////////////////////////////////////////////////////////// using System; using System.Collections.Generic; using LanguageExt; using System.Threading.Tasks; using Issues; using LanguageExt.Common; using LanguageExt.Traits; using TestBed; using static LanguageExt.Prelude; public class Program { static void Main(string[] args) { //////////////////////////////////////////////////////////////////////////////////////////////////////// // // // // // NOTE: This is just my scratch pad for quickly testing stuff, not for human consumption // // // // // ///////////////////////////////////////////v//////////////////////////////////////////////////////////// Discussion1527.Run(); //SourceTTests.Run(); //Issues.Discussion1527.Run(); //RecurTests.Run(); //BracketTest.Run(); //AwaitAnyTest.Run(); //TestBed.StateStuff.StateForkIO.forkTest.Run(4).Run().Ignore(); //Issue1453.Test(); //UseTest.Main().GetAwaiter().GetResult(); //Issue1426().GetAwaiter().GetResult(); //SeqConstructTests.Test(); //ResourcesDiscussion1366.Run(); //StateTTest(); //AtomHashMapTests.Test(); //await AtomHashMapPerf.Test(); // await PipesTest(); // await ObsAffTests.Test(); // await AsyncTests(); //testing.Run(Runtime.New()); //var _ = QueueExample.Issue1065().RunUnit(new Runtime()).Result; //ScheduleTests.Run(); //ApplicativeTest.Test().GetAwaiter().GetResult(); //Console.WriteLine(PipesTestBed.effect.RunEffect().Run(Runtime.New()).Result); //Issue1230.Run(); //Issue1234.Test(); //SequenceParallelTest.Run(); //FreeTests.Test(); Console.WriteLine("Goodbye, World"); } } ================================================ FILE: Samples/TestBed/RWSTTests.cs ================================================ using System; using LanguageExt; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace TestBed; /* public record AppConfig(int X, int Y); public record AppState(int Value) { public AppState SetValue(int value) => this with { Value = value }; } public static class App { public static App As(this K, A> ma) where M : Monad, SemigroupK => (App)ma; public static K Run(this K, A> ma, AppConfig config, AppState state) where M : Monad, SemigroupK => ma.As().runApp.Run(config, state).Map( ma => ma switch { var (value, _, newState) => (value, newState) }); public static App config() where M : Monad, SemigroupK => Readable.ask, AppConfig>().As(); public static App modify(Func f) where M : Monad, SemigroupK => Stateful.modify, AppState>(f).As(); } public class App : MonadT, M>, Readable, AppConfig>, Stateful, AppState> where M : Monad, SemigroupK { public static K, B> Bind(K, A> ma, Func, B>> f) => new App(ma.As().runApp.Bind(x => f(x).As().runApp)); public static K, B> Map(Func f, K, A> ma) => new App(ma.As().runApp.Map(f)); public static K, A> Pure(A value) => new App(RWST.Pure(value)); public static K, B> Apply(K, Func> mf, K, A> ma) => new App(mf.As().runApp.Apply(ma.As().runApp)); public static K, A> Lift(K ma) => new App(RWST.Lift(ma)); public static K, A> Asks(Func f) => new App(RWST.Asks(f)); public static K, A> Local(Func f, K, A> ma) => new App(ma.As().runApp.Local(f)); public static K, Unit> Put(AppState value) => new App(RWST.Put(value)); public static K, Unit> Modify(Func modify) => new App(RWST.Modify(modify)); public static K, A> Gets(Func f) => new App(RWST.Gets(f)); } public readonly record struct App(RWST runApp) : K, A> where M : Monad, SemigroupK { // Your application monad implementation } public static class RwstTest { static IO writeLine(object value) => IO.lift(() => Console.WriteLine(value)); static void Test() { var app = from config in App.config() from value in App.Pure(config.X * config.Y) from _1 in App.modify(s => s.SetValue(value)) from _2 in writeLine(value) select unit; } } */ ================================================ FILE: Samples/TestBed/RecurTests.cs ================================================ using System; using System.Diagnostics; using LanguageExt; using LanguageExt.Common; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace TestBed; public class RecurTests { public static void Run() { recurIsSameAsBind(); recurIsSameAsBind(); recurIsSameAsBind>(); recurIsSameAsBind { } } */ ================================================ FILE: Samples/TestBed/ScheduleTests.cs ================================================ using LanguageExt; using static LanguageExt.Prelude; using static LanguageExt.UnitsOfMeasure; using System; namespace TestBed; public class ScheduleTests { public static void Run() { var results = Schedule.linear(1 * sec) | Schedule.recurs(3) | Schedule.repeat(3); Console.WriteLine(results.Run().ToSeq()); } } ================================================ FILE: Samples/TestBed/SeqConstructTests.cs ================================================ using System.Diagnostics; using LanguageExt; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace TestBed; public static class SeqConstructTests { public static void Test() { Create_Seq_with_constructor(); Create_Seq_with_collection_expression(); } public static void Create_Seq_with_constructor() { var seq = new Seq(["a", "b"]); Debug.Assert(seq.Count == 2); } public static void Create_Seq_with_collection_expression() { Seq seq = ["a", "b"]; Debug.Assert(seq.Count == 2); } } ================================================ FILE: Samples/TestBed/SequenceParallelTest.cs ================================================ using System.Diagnostics; using System.Threading.Tasks; using LanguageExt; using LanguageExt.Sys; using static LanguageExt.Prelude; namespace TestBed; public class SequenceParallelTest { public static void Run() { SequenceParallelRandomDelayTest().GetAwaiter().GetResult(); } public static async Task SequenceParallelRandomDelayTest() { var sw = Stopwatch.StartNew(); var input = Seq(1, 2, 3, 2, 5, 1, 1, 2, 3, 2, 1, 2, 4, 2, 1, 5, 6, 1, 3, 6, 2); var eitherIO = input.Map(DoDelay).Traverse(x => x).As(); var either = eitherIO.Run().As().Run(); Debug.Assert(either.IsRight); either.IfRight(right => Debug.Assert(right == input)); sw.Stop(); System.Console.WriteLine(sw.Elapsed); } static EitherT DoDelay(int seconds) { return liftIO(() => F(seconds)); static async Task> F(int seconds) { await Task.Delay(seconds * 1000); return seconds; } } } ================================================ FILE: Samples/TestBed/SourceTTests.cs ================================================ using System; using LanguageExt; using LanguageExt.Traits; using System.Threading.Tasks; using static LanguageExt.Prelude; namespace TestBed; public static class SourceExt { public static SourceT Log(this SourceT src, Func log) where M: MonadIO, Alternative => src.Map(x => { Console.WriteLine(log(x)); return x; }); public static Source Log(this Source src, Func log) => src.Map(x => { Console.WriteLine(log(x)); return x; }); } public delegate void TickEvent(DateTime dt); public class SourceTTests { public static event Action Tick; public static readonly Event tickEvent = Event.from(ref Tick); public static void Run() { var t = Task.Run(Clock); var ticks = SourceT.iter(from v in tickEvent.Subscribe() from _ in writeLine(v) select unit); var app = from f in ticks.ForkIO() from k in readKey from _ in f.Cancel from i in writeLine("The messages should have stopped") select unit; ; ignore(app.Run()); Console.WriteLine("Press any key to exit"); Console.ReadKey(); } static async Task Clock() { while (true) { await Task.Delay(1000); Tick?.Invoke(DateTime.Now); } } static IO writeLine(object? s) => IO.lift(() => Console.WriteLine(s)); static IO readKey => IO.lift(Console.ReadKey); } ================================================ FILE: Samples/TestBed/StateAsync.cs ================================================ using System; using LanguageExt; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace TestBed.StateStuff; public static class StateForkIO { public static StateIO forkTest => from p1 in showState("parent") from f1 in fork(countTo10("fst")) from f2 in fork(countTo10("snd")) from _ in awaitAll(f1, f2) from p2 in showState("parent") select unit; static StateIO countTo10(string branch) => from _1 in addToState from st in showState(branch) from _2 in when(st < 10, countTo10(branch)) select unit; static StateIO addToState => Stateful.modify, int>(x => x + 1).As(); static StateIO showState(string branch) => from s in Stateful.get, int>().As() from _ in Console.writeLine($"{branch}: {s}") select s; } public static class Console { public static IO writeLine(object? obj) => IO.lift(() => System.Console.WriteLine(obj)); } /// /// Wrapper around `StateT IO` so we can add a `MapIO` and `ToIO` /// public record StateIO(StateT runState) : K, A> { public IO<(A Value, S State)> Run(S initialState) => runState.Run(initialState).As(); public StateIO Map(Func f) => this.Kind().Map(f).As(); public StateIO Bind(Func, B>> f) => this.Kind().Bind(f).As(); public StateIO SelectMany(Func, B>> f, Func g) => Bind(x => f(x).Map(y => g(x, y))); public StateIO SelectMany(Func> f, Func g) => SelectMany(x => MonadIO.liftIO, B>(f(x)), g); } /// /// StateIO extensions /// public static class StateIOExtensions { public static StateIO As(this K, A> ma) => (StateIO)ma; public static IO<(A Value, S State)> Run(this K, A> ma, S initialState) => ma.As().Run(initialState); } /// /// StateIO trait implementation /// public class StateIO : Deriving.Monad, StateT>, Deriving.Stateful, StateT, S>, MonadUnliftIO> { static K, A> Natural, StateT>.Transform(K, A> fa) => fa.As().runState; static K, A> CoNatural, StateT>.CoTransform(K, A> fa) => new StateIO(fa.As()); static K, B> MonadUnliftIO>.MapIO(K, A> ma, Func, IO> f) => from s in Stateful.get, S>() let a = Atom(s) from r in CoNatural.transform, StateT, B>( StateT.lift( ma.As().runState.Run(s).Map(p => { a.Swap(_ => p.State); return p.Value; }).MapIO(f))) from _ in Stateful.put, S>(a.Value) select r; static K, IO> MonadUnliftIO>.ToIO(K, A> ma) => from s in Stateful.get, S>() let a = Atom(s) from r in CoNatural.transform, StateT, IO>( StateT.lift>( ma.As().runState.Run(s).Map(p => { a.Swap(_ => p.State); return p.Value; }).ToIO())) from _ in Stateful.put, S>(a.Value) select r; static K, A> MonadIO>.LiftIO(IO ma) => new StateIO(StateT.lift(ma)); } ================================================ FILE: Samples/TestBed/StateEff.cs ================================================ using System; using LanguageExt; using LanguageExt.Common; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace TestBed; public record StateEff(Func> runStateEff) : K, A> where E : Error; public static class StateEff { public static StateEff As(this K, A> ma) where E : Error => (StateEff)ma; public static IO> Run( this K, A> ma, S initialState) where E : Error => IO.lift(envIO => ma.As().runStateEff(envIO, initialState)); } public class StateEff : Monad>, Stateful, S>, Choice> where E : Error { static K, B> Monad>.Bind( K, A> ma, Func, B>> f) => new StateEff( (eio, state) => ma.As().runStateEff(eio, state) switch { Either.Right(var (s, v)) => f(v).As().runStateEff(eio, s), Either.Left(var e) => Left(e) }); public static K, B> Recur(A value, Func, Next>> f) => Monad.unsafeRecur(value, f); static K, B> Functor>.Map(Func f, K, A> ma) => new StateEff( (eio, state) => ma.As().runStateEff(eio, state) switch { Either.Right(var (s, v)) => Right((s, f(v))), Either.Left(var e) => Left(e) }); static K, A> Applicative>.Pure(A value) => new StateEff((_, s) => Right((s, value))); static K, B> Applicative>.Apply( K, Func> mf, K, A> ma) => new StateEff( (eio, state) => mf.As().runStateEff(eio, state) switch { Either Value)>.Right(var (s1, f)) => ma.As().runStateEff(eio, s1) switch { Either.Right(var (s2, x)) => Right((s2, f(x))), Either.Left(var e) => Left(e) }, Either Value)>.Left(var e) => Left(e) }); static K, B> Applicative>.Apply( K, Func> mf, Memo, A> ma) => new StateEff( (eio, state) => mf.As().runStateEff(eio, state) switch { Either Value)>.Right(var (s1, f)) => ma.Value.As().runStateEff(eio, s1) switch { Either.Right(var (s2, x)) => Right((s2, f(x))), Either.Left(var e) => Left(e) }, Either Value)>.Left(var e) => Left(e) }); static K, Unit> Stateful, S>.Put(S value) => new StateEff((_, _) => Right((value, unit))); static K, Unit> Stateful, S>.Modify(Func modify) => new StateEff((_, state) => Right((modify(state), unit))); static K, A> Stateful, S>.Gets(Func f) => new StateEff((_, state) => Right((state, f(state)))); static K, A> Choice>.Choose(K, A> fa, K, A> fb) => new StateEff( (eio, state) => fa.As().runStateEff(eio, state) switch { Either.Right succ => succ, Either.Left => fb.As().runStateEff(eio, state) }); static K, A> Choice>.Choose(K, A> fa, Memo, A> fb) => new StateEff( (eio, state) => fa.As().runStateEff(eio, state) switch { Either.Right succ => succ, Either.Left => fb.Value.As().runStateEff(eio, state) }); static K, IO> Maybe.MonadUnliftIO>.ToIOMaybe(K, A> ma) => new StateEff>( (eio, state) => ma.As().runStateEff(eio, state) switch { Either.Right(var (s, v)) => Right((s, IO.pure(v))), Either.Left(var e) => Left(e) }); static K, A> Maybe.MonadIO>.LiftIOMaybe(K ma) => new StateEff( (eio, state) => { try { return Right((state, ma.As().Run(eio))); } catch (Exception e) { // TODO: Decide how to convert an Exception to `E` -- this is bespoke to your code // But if you derive your `E` type from `Error` then you'll find it's easier throw new NotImplementedException(); } } ); } ================================================ FILE: Samples/TestBed/TestBed.csproj ================================================ Exe net10.0 default Program 0.6.1 enable ================================================ FILE: Samples/TestBed/TestCodeGen.cs ================================================ /* TODO: RESTORE WITH NEW CODE-GEN SYSTEM //////////////////////////////////////////////////////////////////////////////////////////////////////// // // // // // NOTE: This is just my scratch pad for quickly testing stuff, not for human consumption // // // // // //////////////////////////////////////////////////////////////////////////////////////////////////////// using System; using System.Diagnostics.Contracts; using System.IO; using System.Threading.Tasks; using LanguageExt; using LanguageExt.ClassInstances; using LanguageExt.Common; using LanguageExt.TypeClasses; using static LanguageExt.Prelude; namespace TestBed { [Free] public interface FreeIO { [Pure] T Pure(T value); [Fail] T Fail(Error error); string ReadAllText(string path); Unit WriteAllText(string path, string text); } public static partial class FreeIO { public static FreeIO Flatten2(this FreeIO> ma) => ma switch { Pure> v => v.Value, Fail> v => new Fail(v.Error), ReadAllText> v => new ReadAllText(v.Path, n => Flatten(v.Next(n)), fn => Flatten(v.FailNext(fn))), WriteAllText> v => new WriteAllText(v.Path, v.Text, n => Flatten(v.Next(n)), fn => Flatten(v.FailNext(fn))), _ => throw new System.NotSupportedException() }; } public static class FreeIOTest { public async static Task Test1() { var dsl = from t in FreeIO.ReadAllText("I:\\temp\\test.txt") from _ in FreeIO.WriteAllText("I:\\temp\\test2.txt", t) select unit; var res1 = Interpret(dsl); var res2 = await InterpretAsync(dsl); } public static Either Interpret(FreeIO ma) => ma switch { Pure(var value) => value, Fail(var error) => error, ReadAllText(var path, var next, var failNext) => Interpret(next(Read(path))), WriteAllText(var path, var text, var next, var failNext) => Interpret(next(Write(path, text))), _ => throw new NotSupportedException() }; static string Read(string path) => File.ReadAllText(path); static Unit Write(string path, string text) { File.WriteAllText(path, text); return unit; } public static async Task InterpretAsync(FreeIO ma) => ma switch { Pure(var value) => value, Fail(var error) => await Task.FromException(error), ReadAllText(var path, var next, var failNext) => await InterpretAsync(next(await File.ReadAllTextAsync(path))), WriteAllText(var path, var text, var next, var failNext) => await InterpretAsync(next(await File.WriteAllTextAsync(path, text).ToUnit())), _ => throw new NotSupportedException() }; } [Free] internal interface Maybe { [Pure] A Just(A value); [Pure] A Nothing(); public static Maybe Map(Maybe ma, Func f) => ma switch { Just(var x) => Maybe.Just(f(x)), _ => Maybe.Nothing() }; } public static class MaybeFreeTest { public static void Test1() { var ma = Maybe.Just(10); var mb = Maybe.Just(20); var mn = Maybe.Nothing(); var mr = from a in ma from b in mb select a + b; var mnn = from a in ma from b in mb from _ in mn select a + b; var r3 = mr switch { Just(var x) => $"Value is {x}", _ => "No value" }; Console.WriteLine(mr); Console.WriteLine(mnn); } } [WithLens] public partial class TestWith : Record { public readonly string Name; public readonly string Surname; public TestWith(string name, string surname) { Name = name; Surname = surname; } } public interface MIO { Seq ReadAllLines(string fileName); Unit WriteAllLines(string fileName, Seq lines); Person ReadFromDB(); int Zero { get; } } public class RealIO : MIO { public Seq ReadAllLines(string path) => File.ReadAllLines(path).ToSeq(); public Unit WriteAllLines(string path, Seq lines) { File.WriteAllLines(path, lines); return unit; } public Person ReadFromDB() => new Person("Spider", "Man", 50); public int Zero => 0; } public static class TestSubs { public static void Test() { var comp = from ze in Subsystem.Zero from ls in Subsystem.ReadAllLines("c:/test.txt") from _ in Subsystem.WriteAllLines("c:/test-copy.txt", ls) select ls.Count; var res = comp.Run(new RealIO()).IfFail(0); } } [Reader(Env: typeof(MIO), Constructor: "Pure")] public partial struct Subsystem { } //[RWS(WriterMonoid: typeof(MSeq), Env: typeof(IO), State: typeof(string), // Constructor: "LiftSub", Fail: "FailSub")] //public partial struct Subsys //{ //} [RWS(WriterMonoid: typeof(MSeq), Env: typeof(MIO), State: typeof(Person), Constructor: "Pure", Fail: "Error")] public partial struct Subsys { } public interface IEnv { } public class MultiAccessFields { public MultiAccessFields(string publicString, string internalString, string privateString, string publicStringProp, string internalStringProp, string privateStringProp) { PublicString = publicString; InternalString = internalString; PrivateString = privateString; PublicStringProp = publicStringProp; InternalStringProp = internalStringProp; PrivateStringProp = privateStringProp; } public string PublicString; internal string InternalString; private string PrivateString; public string PublicStringProp { get; set; } internal string InternalStringProp { get; set; } private string PrivateStringProp { get; set; } } [RWS(WriterMonoid: typeof(MSeq), Env: typeof(IEnv), State: typeof(MultiAccessFields))] public partial struct XClientRws { } [WithLens] public partial class TestWith2 : Record { public readonly Option Name; public readonly Option Surname; public TestWith2(Option name, Option surname) { Name = name; Surname = surname; } } [WithLens] internal partial class TestWith3 : Record> where A : class { public readonly A Value; public readonly Option Name; public readonly Option Surname; public TestWith3(A value, Option name, Option surname) { Value = value; Name = name; Surname = surname; } } [WithLens] public partial class TestWith4 : Record { public readonly string New; public readonly string Class; public readonly string Static; public readonly string While; public TestWith4(string @new, string @class, string @static, string @while) { New = @new; Class = @class; Static = @static; While = @while; } } [Union] public abstract partial class MaybeUnion { public abstract MaybeUnion JustValue(A value); public abstract MaybeUnion NothingValue(); } [Union] internal interface Test { Test TestSome(MaybeUnion ma); Test TestNone(); } [Union] public abstract partial class TestSimpleUnion { public abstract TestSimpleUnion Simple1(int x, int y); public abstract TestSimpleUnion Simple2(int x); } //[Union] //public abstract partial class Maybe //{ // public abstract Maybe Just(A value); // public abstract Maybe Nothing(); //} //[Union] //public interface Shape //{ // Shape Rectangle(float width, float length); // Shape Circle(float radius); // Shape Prism(float width, float height); //} [Union] public abstract partial class Shape where NumA : struct, Num { public abstract Shape Rectangle(A width, A length); public abstract Shape Circle(A radius); public abstract Shape Prism(A width, A height); } [WithLens] public partial class CustomClass : Record { public readonly Option Value; public CustomClass(Option value) { Value = value; } public Option ValueLengthAsExpressionBodiedProperty => Value.Map(x => x.Length); public Option ValueLengthAsGetProperty { get { return Value.Map(x => x.Length); } } public Option ValueLengthAsExpressionBodiedMethod() => Value.Map(x => x.Length); } [Record] public partial struct Person { [Eq(typeof(EqStringOrdinalIgnoreCase))] [Ord(typeof(OrdStringOrdinalIgnoreCase))] [Hashable(typeof(HashableStringOrdinalIgnoreCase))] public readonly string Forename; [Eq(typeof(EqStringOrdinalIgnoreCase))] [Ord(typeof(OrdStringOrdinalIgnoreCase))] [Hashable(typeof(HashableStringOrdinalIgnoreCase))] public readonly string Surname; public readonly int? Age; } } */ ================================================ FILE: Samples/TestBed/UseTest.cs ================================================ using System; using System.Threading; using System.Threading.Tasks; using LanguageExt; namespace TestBed; internal class UseTest { public static Task Main() => Method1(); static async Task Method1() { var op = from disposable in Prelude.use(() => new Disposable1()) from _1 in Method2() from _2 in IO.lift(() => Console.WriteLine($"Do hard work: {disposable.Value}")) select Unit.Default; await op.RunAsync(); Console.WriteLine("IO completed"); } static IO Method2() => IO.liftAsync(() => Task.Delay(TimeSpan.FromSeconds(1)).ToUnit()); } public sealed class Disposable1 : IDisposable { volatile int Disposed; public string Value => Disposed == 0 ? "This is a valid value" : throw new ObjectDisposedException(nameof(Disposable1)); public void Dispose() { if (Interlocked.CompareExchange(ref Disposed, 1, 0) == 0) { Console.WriteLine("Disposed"); } else { Console.WriteLine("Already disposed"); } } } ================================================ FILE: Samples/TestBed.WPF/App.xaml ================================================  ================================================ FILE: Samples/TestBed.WPF/App.xaml.cs ================================================ using System.Windows; using LanguageExt.Effects; namespace TestBed.WPF { /// /// Interaction logic for App.xaml /// public partial class App : Application { public static readonly MinRT Runtime = new (); } } ================================================ FILE: Samples/TestBed.WPF/AssemblyInfo.cs ================================================ using System.Windows; [assembly: ThemeInfo( ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located //(used if a resource is not found in the page, // or application resource dictionaries) ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located //(used if a resource is not found in the page, // app, or any theme specific resource dictionaries) )] ================================================ FILE: Samples/TestBed.WPF/AtomIO.cs ================================================ /* using System; using LanguageExt; using LanguageExt.Effects.Traits; using static LanguageExt.Prelude; using static LanguageExt.Transducer; namespace TestBed.WPF; /// /// Placeholder implementation: waiting for the real one to be implemented in LanguageExt.Core /// public class AtomIO where RT : HasIO { public AtomIO(A value) => Value = value; public A Value { get; private set; } public IO Swap(Func swap) => lift(() => { Value = swap(Value); return unit; }); public override string ToString() => Value?.ToString() ?? "[null]"; } */ ================================================ FILE: Samples/TestBed.WPF/MainWindow.xaml ================================================  100 200 ================================================ FILE: Samples/TestBed.WPF/MainWindow.xaml.cs ================================================ using System; using System.Windows; using System.Windows.Input; using LanguageExt; using LanguageExt.Effects; using LanguageExt.Pipes; using static LanguageExt.Prelude; namespace TestBed.WPF; /// /// Main application window /// public partial class MainWindow : WindowRT { /// /// Mutable counter /// readonly Atom count = Atom(0); /// /// Construct the window and register the events /// public MainWindow() : base(App.Runtime) { InitializeComponent(); onStart(startup); } /// /// Register the window events /// Eff startup => from _1 in tickIO.ForkIO().As() from _2 in (onMouseMove | Proxy.repeat(showMousePos)).RunEffect().ForkIO() select unit; /// /// Infinite loop that ticks every second /// Eff tickIO => from _1 in modifyCount(x => x + 1) from _2 in postIO(setContent(CounterButton, $"{count}")) from _3 in waitFor(1) from _4 in tickIO // tail(tickIO) select unit; Consumer, Unit> showMousePos => from e in Proxy.awaiting() from _ in postIO(from p in getPosition(e) from x in setContent(CursorTextBoxX, $"X: {p.X:F0}") from y in setContent(CursorTextBoxY, $"Y: {p.Y:F0}") select unit) select unit; /// /// Standard button click-handler /// void ButtonOnClick(object? sender, RoutedEventArgs e) => handle(buttonOnClickIO); /// /// Button handler in the IO monad /// Eff buttonOnClickIO => from _1 in resetCount from _2 in postIO(setContent(CounterButton, $"{count}")) select unit; /// /// Set the count value /// Eff setCount(int value) => modifyCount(_ => value); /// /// Reset the count value /// Eff resetCount => modifyCount(_ => 0); /// /// Set the count value /// Eff modifyCount(Func f) => lift(() =>count.Swap(f)); } ================================================ FILE: Samples/TestBed.WPF/TestBed.WPF.csproj ================================================ WinExe net8.0-windows enable true ================================================ FILE: Samples/TestBed.WPF/WindowIO.cs ================================================ using System; using System.Windows; using System.Windows.Controls; using System.Windows.Input; using LanguageExt; using LanguageExt.Pipes; using LanguageExt.Traits; using static LanguageExt.Prelude; namespace TestBed.WPF; /// /// A window base-type that supports some common IO behaviours for use in /// derived window-types. /// public class WindowIO : Window { readonly RT Runtime; readonly EnvIO EnvIO; /// /// Constructor /// protected WindowIO(RT runtime) { Runtime = runtime; EnvIO = EnvIO.New(); } /// /// Startup launch /// protected Unit onStart(Eff operation) => operation.Run(Runtime, EnvIO) .IfFail(e => e.Throw()); /// /// Closes any forks created by the window /// protected override void OnClosed(EventArgs e) { EnvIO.Source.Cancel(); base.OnClosed(e); } /// /// Turns an IO operation into a task that is run /// /// /// Useful for wrapping IO event-handlers into a Task base event-handler /// protected void handle(Eff operation) => operation.ForkIO().Run(Runtime, EnvIO).ThrowIfFail(); /// /// Helper IO for setting control text /// protected static Eff setContent(ContentControl control, string text) => lift(action: () => control.Content = text); /// /// Helper IO for setting control text /// protected static Eff setContent(TextBlock control, string text) => lift(action: () => control.Text = text); /// /// Get mouse position /// protected Eff getPosition(MouseEventArgs @event) => lift(() => @event.GetPosition(this)); protected Producer, Unit> onMouseMove => from rtime in runtime() let queue = Proxy.Queue, MouseEventArgs>() from hndlr in use( acquire: () => (_, e) => queue.Enqueue(e), release: h => RemoveHandler(Mouse.MouseMoveEvent, h)) from _ in liftEff(() => AddHandler(Mouse.MouseMoveEvent, hndlr, false)) from result in queue select result; /// /// Async delay /// protected static Eff waitFor(double ms) => IO.yieldFor(ms); } ================================================ FILE: Samples/TestBed.WPF/WindowRT.cs ================================================ using LanguageExt.Common; using LanguageExt.Effects; namespace TestBed.WPF; /// /// A window base-type that bakes in a runtime /// public class WindowRT : WindowIO { public WindowRT(MinRT runtime) : base(runtime) { } } ================================================ FILE: Samples/TestBed.Web/Program.cs ================================================ using LanguageExt; using static LanguageExt.Prelude; var builder = WebApplication.CreateBuilder(args); var app = builder.Build(); app.MapGet("/sync", () => { var effect = yieldFor(1000).Map("Hello, World"); return effect.Run(); }); app.MapGet("/async", async () => { var effect = yieldFor(1000).Map("Hello, World"); return await effect.RunAsync(); }); app.MapGet("/fork-sync", () => { var effect = yieldFor(1000).Map("Hello, World"); var computation = awaitIO(fork(effect)); return computation.Run(); }); app.MapGet("/fork-async", () => { var effect = yieldFor(1000).Map("Hello, World"); var computation = awaitIO(fork(effect)); return computation.Run(); }); app.Run(); ================================================ FILE: Samples/TestBed.Web/TestBed.Web.csproj ================================================  net10.0 enable enable ================================================ FILE: Samples/TestBed.Web.Runner/Program.cs ================================================ using NBomber.Contracts; using NBomber.CSharp; using NBomber.Http.CSharp; var http = new HttpClient(); var sim = (string name) => Scenario.Create(name, async context => { var request = Http.CreateRequest("GET", $"http://localhost:5000/{name}"); var response = await Http.Send(http, request).ConfigureAwait(false); return response; }) //.WithoutWarmUp() .WithLoadSimulations( Simulation.Inject(rate: 500, interval: TimeSpan.FromSeconds(1), during: TimeSpan.FromSeconds(30))); NBomberRunner.RegisterScenarios(sim("async")).Run(); NBomberRunner.RegisterScenarios(sim("sync")).Run(); NBomberRunner.RegisterScenarios(sim("fork-async")).Run(); NBomberRunner.RegisterScenarios(sim("fork-sync")).Run(); ================================================ FILE: Samples/TestBed.Web.Runner/TestBed.Web.Runner.csproj ================================================  Exe net10.0 enable enable ================================================ FILE: clean.sh ================================================ echo cleaing bin, obj, and /louthy.github.io/language-ext # Artifacts is where the DLLs are compiled to Artifacts=/media/paul/raid/dev/artifacts # $LangExtRoot is where the source code root should be LangExtRoot=/media/paul/raid/dev/language-ext rm -rf $Artifacts rm -rf $LangExtRoot/LanguageExt.Core/bin rm -rf $LangExtRoot/LanguageExt.Pipes/bin rm -rf $LangExtRoot/LanguageExt.Streaming/bin rm -rf $LangExtRoot/LanguageExt.Parsec/bin rm -rf $LangExtRoot/LanguageExt.FSharp/bin rm -rf $LangExtRoot/LanguageExt.Rx/bin rm -rf $LangExtRoot/LanguageExt.Sys/bin rm -rf $LangExtRoot/LanguageExt.Core/obj rm -rf $LangExtRoot/LanguageExt.Pipes/obj rm -rf $LangExtRoot/LanguageExt.Streaming/obj rm -rf $LangExtRoot/LanguageExt.Parsec/obj rm -rf $LangExtRoot/LanguageExt.FSharp/obj rm -rf $LangExtRoot/LanguageExt.Rx/obj rm -rf $LangExtRoot/LanguageExt.Sys/obj echo cleaned ================================================ FILE: docs.sh ================================================ # $BestFormBin is where the bestform.exe is compiled to BestFormBin=/media/paul/raid/dev/best-form/bestform # $LangExtRoot is where the source code root should be LangExtRoot=/media/paul/raid/dev/language-ext # $LangExtDocs is where the docs root should be LangExtDocs=/media/paul/raid/dev/louthy.github.io echo cleaning the docs rm -rf $LangExtDocs/language-ext/LanguageExt.Core rm -rf $LangExtDocs/language-ext/LanguageExt.Streaming rm -rf $LangExtDocs/language-ext/LanguageExt.Parsec rm -rf $LangExtDocs/language-ext/LanguageExt.FSharp rm -rf $LangExtDocs/language-ext/LanguageExt.Rx rm -rf $LangExtDocs/language-ext/LanguageExt.Sys echo building the docs dotnet build $BestFormBin/bestform.csproj -c Release dotnet run --project $BestFormBin -c Release --no-build "LanguageExt.Core" "$LangExtRoot/LanguageExt.Core" "$LangExtDocs/language-ext" "https://github.com/louthy/language-ext/tree/main" dotnet run --project $BestFormBin -c Release --no-build "LanguageExt.Streaming" "$LangExtRoot/LanguageExt.Streaming" "$LangExtDocs/language-ext" "https://github.com/louthy/language-ext/tree/main" dotnet run --project $BestFormBin -c Release --no-build "LanguageExt.Parsec" "$LangExtRoot/LanguageExt.Parsec" "$LangExtDocs/language-ext" "https://github.com/louthy/language-ext/tree/main" dotnet run --project $BestFormBin -c Release --no-build "LanguageExt.FSharp" "$LangExtRoot/LanguageExt.FSharp" "$LangExtDocs/language-ext" "https://github.com/louthy/language-ext/tree/main" dotnet run --project $BestFormBin -c Release --no-build "LanguageExt.Rx" "$LangExtRoot/LanguageExt.Rx" "$LangExtDocs/language-ext" "https://github.com/louthy/language-ext/tree/main" dotnet run --project $BestFormBin -c Release --no-build "LanguageExt.Sys" "$LangExtRoot/LanguageExt.Sys" "$LangExtDocs/language-ext" "https://github.com/louthy/language-ext/tree/main" echo committing docs to git cd $LangExtDocs || exit git add . git commit -m "Language-ext documentation update" git push echo finished commmitting docs to git ================================================ FILE: inc.bat ================================================ pjv\pjv.exe inc ================================================ FILE: language-ext.sln ================================================  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 16 VisualStudioVersion = 16.0.28803.156 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LanguageExt.Core", "LanguageExt.Core\LanguageExt.Core.csproj", "{4537B8A8-6CA4-4DCB-B30E-7CADD29A324F}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LanguageExt.FSharp", "LanguageExt.FSharp\LanguageExt.FSharp.csproj", "{F7218BC7-BB70-48D7-8D43-0006E880F5BA}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LanguageExt.Parsec", "LanguageExt.Parsec\LanguageExt.Parsec.csproj", "{29E65953-E2D9-4C9F-82FE-DEF50BBA4A48}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Samples", "Samples", "{1ABB8B79-9BF2-4C51-A920-9C17BF11273F}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestBed", "Samples\TestBed\TestBed.csproj", "{3D165617-8A90-4BEB-B951-6168EDDA232D}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LanguageExt.Tests", "LanguageExt.Tests\LanguageExt.Tests.csproj", "{E882F67B-24EC-444B-B748-EE9BEFF904E2}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LanguageExt.Rx", "LanguageExt.Rx\LanguageExt.Rx.csproj", "{A191CF80-FC53-4142-9769-CF949F3277B9}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LanguageExt.Benchmarks", "LanguageExt.Benchmarks\LanguageExt.Benchmarks.csproj", "{4F7FCDBC-F37A-493B-A648-107F69F10EFF}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LanguageExt.Sys", "LanguageExt.Sys\LanguageExt.Sys.csproj", "{8D43E156-DE9A-46CD-A732-9F66A1F84B1D}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EffectsExamples", "Samples\EffectsExamples\EffectsExamples.csproj", "{A23ED3D6-6A1A-457C-A55A-21BCDC886A47}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestBed.WPF", "Samples\TestBed.WPF\TestBed.WPF.csproj", "{F17CB314-8CC5-4343-AAE8-7292B700E5BA}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IOExmples", "Samples\IOExmples\IOExmples.csproj", "{BA5DF6B6-378C-4880-9CC3-802F3B560EFF}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CardGame", "Samples\CardGame\CardGame.csproj", "{B309F69E-87C4-43F3-9766-FCCDE6AC66A2}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Newsletter", "Samples\Newsletter\Newsletter\Newsletter.csproj", "{32BB0F08-5A9D-4245-8721-2A3B12607270}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Streams", "Samples\Streams\Streams.csproj", "{B4D4721B-A599-4149-BD3A-822C277FC0FE}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestBed.Web", "Samples\TestBed.Web\TestBed.Web.csproj", "{A7058669-DC59-4FFA-9A04-143D9569F55D}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestBed.Web.Runner", "Samples\TestBed.Web.Runner\TestBed.Web.Runner.csproj", "{F6CB0C45-09D5-4178-AEA6-F113E24FEEC5}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DomainTypesExamples", "Samples\DomainTypesExamples\DomainTypesExamples.csproj", "{72F07BEF-4379-4FD9-8CBA-CFB01D004718}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CreditCardValidation", "Samples\CreditCardValidation\CreditCardValidation.csproj", "{AF8ED740-D77D-4080-9952-59232C57767C}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LanguageExt.Streaming", "LanguageExt.Streaming\LanguageExt.Streaming.csproj", "{6661A967-81D7-4236-99BC-AD42F71B6C1F}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PipesExamples", "Samples\PipesExamples\PipesExamples.csproj", "{8CE4D269-B685-438C-AA1B-48D3E73C60DB}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LanguageExt.XUnitExt", "LanguageExt.XUnitExt\LanguageExt.XUnitExt.csproj", "{3F01D095-EA20-44B5-8B0D-4713601B0D9E}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LanguageExt.Megaparsec", "LanguageExt.Megaparsec\LanguageExt.Megaparsec.csproj", "{9A2A964B-EF1B-4BB9-B57F-7E4798BFC1D6}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BlazorApp", "Samples\BlazorApp\BlazorApp.csproj", "{B5D1D911-24D0-4910-84C7-82F57EA65B33}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Debug|x64 = Debug|x64 Debug|x86 = Debug|x86 Release|Any CPU = Release|Any CPU Release|x64 = Release|x64 Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {4537B8A8-6CA4-4DCB-B30E-7CADD29A324F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {4537B8A8-6CA4-4DCB-B30E-7CADD29A324F}.Debug|Any CPU.Build.0 = Debug|Any CPU {4537B8A8-6CA4-4DCB-B30E-7CADD29A324F}.Debug|x64.ActiveCfg = Debug|Any CPU {4537B8A8-6CA4-4DCB-B30E-7CADD29A324F}.Debug|x64.Build.0 = Debug|Any CPU {4537B8A8-6CA4-4DCB-B30E-7CADD29A324F}.Debug|x86.ActiveCfg = Debug|Any CPU {4537B8A8-6CA4-4DCB-B30E-7CADD29A324F}.Debug|x86.Build.0 = Debug|Any CPU {4537B8A8-6CA4-4DCB-B30E-7CADD29A324F}.Release|Any CPU.ActiveCfg = Release|Any CPU {4537B8A8-6CA4-4DCB-B30E-7CADD29A324F}.Release|Any CPU.Build.0 = Release|Any CPU {4537B8A8-6CA4-4DCB-B30E-7CADD29A324F}.Release|x64.ActiveCfg = Release|Any CPU {4537B8A8-6CA4-4DCB-B30E-7CADD29A324F}.Release|x64.Build.0 = Release|Any CPU {4537B8A8-6CA4-4DCB-B30E-7CADD29A324F}.Release|x86.ActiveCfg = Release|Any CPU {4537B8A8-6CA4-4DCB-B30E-7CADD29A324F}.Release|x86.Build.0 = Release|Any CPU {F7218BC7-BB70-48D7-8D43-0006E880F5BA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {F7218BC7-BB70-48D7-8D43-0006E880F5BA}.Debug|Any CPU.Build.0 = Debug|Any CPU {F7218BC7-BB70-48D7-8D43-0006E880F5BA}.Debug|x64.ActiveCfg = Debug|Any CPU {F7218BC7-BB70-48D7-8D43-0006E880F5BA}.Debug|x64.Build.0 = Debug|Any CPU {F7218BC7-BB70-48D7-8D43-0006E880F5BA}.Debug|x86.ActiveCfg = Debug|Any CPU {F7218BC7-BB70-48D7-8D43-0006E880F5BA}.Debug|x86.Build.0 = Debug|Any CPU {F7218BC7-BB70-48D7-8D43-0006E880F5BA}.Release|Any CPU.ActiveCfg = Release|Any CPU {F7218BC7-BB70-48D7-8D43-0006E880F5BA}.Release|Any CPU.Build.0 = Release|Any CPU {F7218BC7-BB70-48D7-8D43-0006E880F5BA}.Release|x64.ActiveCfg = Release|Any CPU {F7218BC7-BB70-48D7-8D43-0006E880F5BA}.Release|x64.Build.0 = Release|Any CPU {F7218BC7-BB70-48D7-8D43-0006E880F5BA}.Release|x86.ActiveCfg = Release|Any CPU {F7218BC7-BB70-48D7-8D43-0006E880F5BA}.Release|x86.Build.0 = Release|Any CPU {29E65953-E2D9-4C9F-82FE-DEF50BBA4A48}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {29E65953-E2D9-4C9F-82FE-DEF50BBA4A48}.Debug|Any CPU.Build.0 = Debug|Any CPU {29E65953-E2D9-4C9F-82FE-DEF50BBA4A48}.Debug|x64.ActiveCfg = Debug|Any CPU {29E65953-E2D9-4C9F-82FE-DEF50BBA4A48}.Debug|x64.Build.0 = Debug|Any CPU {29E65953-E2D9-4C9F-82FE-DEF50BBA4A48}.Debug|x86.ActiveCfg = Debug|Any CPU {29E65953-E2D9-4C9F-82FE-DEF50BBA4A48}.Debug|x86.Build.0 = Debug|Any CPU {29E65953-E2D9-4C9F-82FE-DEF50BBA4A48}.Release|Any CPU.ActiveCfg = Release|Any CPU {29E65953-E2D9-4C9F-82FE-DEF50BBA4A48}.Release|Any CPU.Build.0 = Release|Any CPU {29E65953-E2D9-4C9F-82FE-DEF50BBA4A48}.Release|x64.ActiveCfg = Release|Any CPU {29E65953-E2D9-4C9F-82FE-DEF50BBA4A48}.Release|x64.Build.0 = Release|Any CPU {29E65953-E2D9-4C9F-82FE-DEF50BBA4A48}.Release|x86.ActiveCfg = Release|Any CPU {29E65953-E2D9-4C9F-82FE-DEF50BBA4A48}.Release|x86.Build.0 = Release|Any CPU {3D165617-8A90-4BEB-B951-6168EDDA232D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {3D165617-8A90-4BEB-B951-6168EDDA232D}.Debug|Any CPU.Build.0 = Debug|Any CPU {3D165617-8A90-4BEB-B951-6168EDDA232D}.Debug|x64.ActiveCfg = Debug|Any CPU {3D165617-8A90-4BEB-B951-6168EDDA232D}.Debug|x64.Build.0 = Debug|Any CPU {3D165617-8A90-4BEB-B951-6168EDDA232D}.Debug|x86.ActiveCfg = Debug|Any CPU {3D165617-8A90-4BEB-B951-6168EDDA232D}.Debug|x86.Build.0 = Debug|Any CPU {3D165617-8A90-4BEB-B951-6168EDDA232D}.Release|Any CPU.ActiveCfg = Release|Any CPU {3D165617-8A90-4BEB-B951-6168EDDA232D}.Release|Any CPU.Build.0 = Release|Any CPU {3D165617-8A90-4BEB-B951-6168EDDA232D}.Release|x64.ActiveCfg = Release|Any CPU {3D165617-8A90-4BEB-B951-6168EDDA232D}.Release|x64.Build.0 = Release|Any CPU {3D165617-8A90-4BEB-B951-6168EDDA232D}.Release|x86.ActiveCfg = Release|Any CPU {3D165617-8A90-4BEB-B951-6168EDDA232D}.Release|x86.Build.0 = Release|Any CPU {E882F67B-24EC-444B-B748-EE9BEFF904E2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {E882F67B-24EC-444B-B748-EE9BEFF904E2}.Debug|Any CPU.Build.0 = Debug|Any CPU {E882F67B-24EC-444B-B748-EE9BEFF904E2}.Debug|x64.ActiveCfg = Debug|Any CPU {E882F67B-24EC-444B-B748-EE9BEFF904E2}.Debug|x64.Build.0 = Debug|Any CPU {E882F67B-24EC-444B-B748-EE9BEFF904E2}.Debug|x86.ActiveCfg = Debug|Any CPU {E882F67B-24EC-444B-B748-EE9BEFF904E2}.Debug|x86.Build.0 = Debug|Any CPU {E882F67B-24EC-444B-B748-EE9BEFF904E2}.Release|Any CPU.ActiveCfg = Release|Any CPU {E882F67B-24EC-444B-B748-EE9BEFF904E2}.Release|Any CPU.Build.0 = Release|Any CPU {E882F67B-24EC-444B-B748-EE9BEFF904E2}.Release|x64.ActiveCfg = Release|Any CPU {E882F67B-24EC-444B-B748-EE9BEFF904E2}.Release|x64.Build.0 = Release|Any CPU {E882F67B-24EC-444B-B748-EE9BEFF904E2}.Release|x86.ActiveCfg = Release|Any CPU {E882F67B-24EC-444B-B748-EE9BEFF904E2}.Release|x86.Build.0 = Release|Any CPU {A191CF80-FC53-4142-9769-CF949F3277B9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A191CF80-FC53-4142-9769-CF949F3277B9}.Debug|Any CPU.Build.0 = Debug|Any CPU {A191CF80-FC53-4142-9769-CF949F3277B9}.Debug|x64.ActiveCfg = Debug|Any CPU {A191CF80-FC53-4142-9769-CF949F3277B9}.Debug|x64.Build.0 = Debug|Any CPU {A191CF80-FC53-4142-9769-CF949F3277B9}.Debug|x86.ActiveCfg = Debug|Any CPU {A191CF80-FC53-4142-9769-CF949F3277B9}.Debug|x86.Build.0 = Debug|Any CPU {A191CF80-FC53-4142-9769-CF949F3277B9}.Release|Any CPU.ActiveCfg = Release|Any CPU {A191CF80-FC53-4142-9769-CF949F3277B9}.Release|Any CPU.Build.0 = Release|Any CPU {A191CF80-FC53-4142-9769-CF949F3277B9}.Release|x64.ActiveCfg = Release|Any CPU {A191CF80-FC53-4142-9769-CF949F3277B9}.Release|x64.Build.0 = Release|Any CPU {A191CF80-FC53-4142-9769-CF949F3277B9}.Release|x86.ActiveCfg = Release|Any CPU {A191CF80-FC53-4142-9769-CF949F3277B9}.Release|x86.Build.0 = Release|Any CPU {4F7FCDBC-F37A-493B-A648-107F69F10EFF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {4F7FCDBC-F37A-493B-A648-107F69F10EFF}.Debug|Any CPU.Build.0 = Debug|Any CPU {4F7FCDBC-F37A-493B-A648-107F69F10EFF}.Debug|x64.ActiveCfg = Debug|Any CPU {4F7FCDBC-F37A-493B-A648-107F69F10EFF}.Debug|x64.Build.0 = Debug|Any CPU {4F7FCDBC-F37A-493B-A648-107F69F10EFF}.Debug|x86.ActiveCfg = Debug|Any CPU {4F7FCDBC-F37A-493B-A648-107F69F10EFF}.Debug|x86.Build.0 = Debug|Any CPU {4F7FCDBC-F37A-493B-A648-107F69F10EFF}.Release|Any CPU.ActiveCfg = Release|Any CPU {4F7FCDBC-F37A-493B-A648-107F69F10EFF}.Release|Any CPU.Build.0 = Release|Any CPU {4F7FCDBC-F37A-493B-A648-107F69F10EFF}.Release|x64.ActiveCfg = Release|Any CPU {4F7FCDBC-F37A-493B-A648-107F69F10EFF}.Release|x64.Build.0 = Release|Any CPU {4F7FCDBC-F37A-493B-A648-107F69F10EFF}.Release|x86.ActiveCfg = Release|Any CPU {4F7FCDBC-F37A-493B-A648-107F69F10EFF}.Release|x86.Build.0 = Release|Any CPU {8D43E156-DE9A-46CD-A732-9F66A1F84B1D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {8D43E156-DE9A-46CD-A732-9F66A1F84B1D}.Debug|Any CPU.Build.0 = Debug|Any CPU {8D43E156-DE9A-46CD-A732-9F66A1F84B1D}.Debug|x64.ActiveCfg = Debug|Any CPU {8D43E156-DE9A-46CD-A732-9F66A1F84B1D}.Debug|x64.Build.0 = Debug|Any CPU {8D43E156-DE9A-46CD-A732-9F66A1F84B1D}.Debug|x86.ActiveCfg = Debug|Any CPU {8D43E156-DE9A-46CD-A732-9F66A1F84B1D}.Debug|x86.Build.0 = Debug|Any CPU {8D43E156-DE9A-46CD-A732-9F66A1F84B1D}.Release|Any CPU.ActiveCfg = Release|Any CPU {8D43E156-DE9A-46CD-A732-9F66A1F84B1D}.Release|Any CPU.Build.0 = Release|Any CPU {8D43E156-DE9A-46CD-A732-9F66A1F84B1D}.Release|x64.ActiveCfg = Release|Any CPU {8D43E156-DE9A-46CD-A732-9F66A1F84B1D}.Release|x64.Build.0 = Release|Any CPU {8D43E156-DE9A-46CD-A732-9F66A1F84B1D}.Release|x86.ActiveCfg = Release|Any CPU {8D43E156-DE9A-46CD-A732-9F66A1F84B1D}.Release|x86.Build.0 = Release|Any CPU {A23ED3D6-6A1A-457C-A55A-21BCDC886A47}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A23ED3D6-6A1A-457C-A55A-21BCDC886A47}.Debug|Any CPU.Build.0 = Debug|Any CPU {A23ED3D6-6A1A-457C-A55A-21BCDC886A47}.Debug|x64.ActiveCfg = Debug|Any CPU {A23ED3D6-6A1A-457C-A55A-21BCDC886A47}.Debug|x64.Build.0 = Debug|Any CPU {A23ED3D6-6A1A-457C-A55A-21BCDC886A47}.Debug|x86.ActiveCfg = Debug|Any CPU {A23ED3D6-6A1A-457C-A55A-21BCDC886A47}.Debug|x86.Build.0 = Debug|Any CPU {A23ED3D6-6A1A-457C-A55A-21BCDC886A47}.Release|Any CPU.ActiveCfg = Release|Any CPU {A23ED3D6-6A1A-457C-A55A-21BCDC886A47}.Release|Any CPU.Build.0 = Release|Any CPU {A23ED3D6-6A1A-457C-A55A-21BCDC886A47}.Release|x64.ActiveCfg = Release|Any CPU {A23ED3D6-6A1A-457C-A55A-21BCDC886A47}.Release|x64.Build.0 = Release|Any CPU {A23ED3D6-6A1A-457C-A55A-21BCDC886A47}.Release|x86.ActiveCfg = Release|Any CPU {A23ED3D6-6A1A-457C-A55A-21BCDC886A47}.Release|x86.Build.0 = Release|Any CPU {A5CEDB48-F840-4462-8BE9-65BF35C8D5A2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A5CEDB48-F840-4462-8BE9-65BF35C8D5A2}.Debug|Any CPU.Build.0 = Debug|Any CPU {A5CEDB48-F840-4462-8BE9-65BF35C8D5A2}.Debug|x64.ActiveCfg = Debug|Any CPU {A5CEDB48-F840-4462-8BE9-65BF35C8D5A2}.Debug|x64.Build.0 = Debug|Any CPU {A5CEDB48-F840-4462-8BE9-65BF35C8D5A2}.Debug|x86.ActiveCfg = Debug|Any CPU {A5CEDB48-F840-4462-8BE9-65BF35C8D5A2}.Debug|x86.Build.0 = Debug|Any CPU {A5CEDB48-F840-4462-8BE9-65BF35C8D5A2}.Release|Any CPU.ActiveCfg = Release|Any CPU {A5CEDB48-F840-4462-8BE9-65BF35C8D5A2}.Release|Any CPU.Build.0 = Release|Any CPU {A5CEDB48-F840-4462-8BE9-65BF35C8D5A2}.Release|x64.ActiveCfg = Release|Any CPU {A5CEDB48-F840-4462-8BE9-65BF35C8D5A2}.Release|x64.Build.0 = Release|Any CPU {A5CEDB48-F840-4462-8BE9-65BF35C8D5A2}.Release|x86.ActiveCfg = Release|Any CPU {A5CEDB48-F840-4462-8BE9-65BF35C8D5A2}.Release|x86.Build.0 = Release|Any CPU {F17CB314-8CC5-4343-AAE8-7292B700E5BA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {F17CB314-8CC5-4343-AAE8-7292B700E5BA}.Debug|Any CPU.Build.0 = Debug|Any CPU {F17CB314-8CC5-4343-AAE8-7292B700E5BA}.Debug|x64.ActiveCfg = Debug|Any CPU {F17CB314-8CC5-4343-AAE8-7292B700E5BA}.Debug|x64.Build.0 = Debug|Any CPU {F17CB314-8CC5-4343-AAE8-7292B700E5BA}.Debug|x86.ActiveCfg = Debug|Any CPU {F17CB314-8CC5-4343-AAE8-7292B700E5BA}.Debug|x86.Build.0 = Debug|Any CPU {F17CB314-8CC5-4343-AAE8-7292B700E5BA}.Release|Any CPU.ActiveCfg = Release|Any CPU {F17CB314-8CC5-4343-AAE8-7292B700E5BA}.Release|Any CPU.Build.0 = Release|Any CPU {F17CB314-8CC5-4343-AAE8-7292B700E5BA}.Release|x64.ActiveCfg = Release|Any CPU {F17CB314-8CC5-4343-AAE8-7292B700E5BA}.Release|x64.Build.0 = Release|Any CPU {F17CB314-8CC5-4343-AAE8-7292B700E5BA}.Release|x86.ActiveCfg = Release|Any CPU {F17CB314-8CC5-4343-AAE8-7292B700E5BA}.Release|x86.Build.0 = Release|Any CPU {BA5DF6B6-378C-4880-9CC3-802F3B560EFF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {BA5DF6B6-378C-4880-9CC3-802F3B560EFF}.Debug|Any CPU.Build.0 = Debug|Any CPU {BA5DF6B6-378C-4880-9CC3-802F3B560EFF}.Debug|x64.ActiveCfg = Debug|Any CPU {BA5DF6B6-378C-4880-9CC3-802F3B560EFF}.Debug|x64.Build.0 = Debug|Any CPU {BA5DF6B6-378C-4880-9CC3-802F3B560EFF}.Debug|x86.ActiveCfg = Debug|Any CPU {BA5DF6B6-378C-4880-9CC3-802F3B560EFF}.Debug|x86.Build.0 = Debug|Any CPU {BA5DF6B6-378C-4880-9CC3-802F3B560EFF}.Release|Any CPU.ActiveCfg = Release|Any CPU {BA5DF6B6-378C-4880-9CC3-802F3B560EFF}.Release|Any CPU.Build.0 = Release|Any CPU {BA5DF6B6-378C-4880-9CC3-802F3B560EFF}.Release|x64.ActiveCfg = Release|Any CPU {BA5DF6B6-378C-4880-9CC3-802F3B560EFF}.Release|x64.Build.0 = Release|Any CPU {BA5DF6B6-378C-4880-9CC3-802F3B560EFF}.Release|x86.ActiveCfg = Release|Any CPU {BA5DF6B6-378C-4880-9CC3-802F3B560EFF}.Release|x86.Build.0 = Release|Any CPU {B309F69E-87C4-43F3-9766-FCCDE6AC66A2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {B309F69E-87C4-43F3-9766-FCCDE6AC66A2}.Debug|Any CPU.Build.0 = Debug|Any CPU {B309F69E-87C4-43F3-9766-FCCDE6AC66A2}.Debug|x64.ActiveCfg = Debug|Any CPU {B309F69E-87C4-43F3-9766-FCCDE6AC66A2}.Debug|x64.Build.0 = Debug|Any CPU {B309F69E-87C4-43F3-9766-FCCDE6AC66A2}.Debug|x86.ActiveCfg = Debug|Any CPU {B309F69E-87C4-43F3-9766-FCCDE6AC66A2}.Debug|x86.Build.0 = Debug|Any CPU {B309F69E-87C4-43F3-9766-FCCDE6AC66A2}.Release|Any CPU.ActiveCfg = Release|Any CPU {B309F69E-87C4-43F3-9766-FCCDE6AC66A2}.Release|Any CPU.Build.0 = Release|Any CPU {B309F69E-87C4-43F3-9766-FCCDE6AC66A2}.Release|x64.ActiveCfg = Release|Any CPU {B309F69E-87C4-43F3-9766-FCCDE6AC66A2}.Release|x64.Build.0 = Release|Any CPU {B309F69E-87C4-43F3-9766-FCCDE6AC66A2}.Release|x86.ActiveCfg = Release|Any CPU {B309F69E-87C4-43F3-9766-FCCDE6AC66A2}.Release|x86.Build.0 = Release|Any CPU {32BB0F08-5A9D-4245-8721-2A3B12607270}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {32BB0F08-5A9D-4245-8721-2A3B12607270}.Debug|Any CPU.Build.0 = Debug|Any CPU {32BB0F08-5A9D-4245-8721-2A3B12607270}.Debug|x64.ActiveCfg = Debug|Any CPU {32BB0F08-5A9D-4245-8721-2A3B12607270}.Debug|x64.Build.0 = Debug|Any CPU {32BB0F08-5A9D-4245-8721-2A3B12607270}.Debug|x86.ActiveCfg = Debug|Any CPU {32BB0F08-5A9D-4245-8721-2A3B12607270}.Debug|x86.Build.0 = Debug|Any CPU {32BB0F08-5A9D-4245-8721-2A3B12607270}.Release|Any CPU.ActiveCfg = Release|Any CPU {32BB0F08-5A9D-4245-8721-2A3B12607270}.Release|Any CPU.Build.0 = Release|Any CPU {32BB0F08-5A9D-4245-8721-2A3B12607270}.Release|x64.ActiveCfg = Release|Any CPU {32BB0F08-5A9D-4245-8721-2A3B12607270}.Release|x64.Build.0 = Release|Any CPU {32BB0F08-5A9D-4245-8721-2A3B12607270}.Release|x86.ActiveCfg = Release|Any CPU {32BB0F08-5A9D-4245-8721-2A3B12607270}.Release|x86.Build.0 = Release|Any CPU {B4D4721B-A599-4149-BD3A-822C277FC0FE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {B4D4721B-A599-4149-BD3A-822C277FC0FE}.Debug|Any CPU.Build.0 = Debug|Any CPU {B4D4721B-A599-4149-BD3A-822C277FC0FE}.Debug|x64.ActiveCfg = Debug|Any CPU {B4D4721B-A599-4149-BD3A-822C277FC0FE}.Debug|x64.Build.0 = Debug|Any CPU {B4D4721B-A599-4149-BD3A-822C277FC0FE}.Debug|x86.ActiveCfg = Debug|Any CPU {B4D4721B-A599-4149-BD3A-822C277FC0FE}.Debug|x86.Build.0 = Debug|Any CPU {B4D4721B-A599-4149-BD3A-822C277FC0FE}.Release|Any CPU.ActiveCfg = Release|Any CPU {B4D4721B-A599-4149-BD3A-822C277FC0FE}.Release|Any CPU.Build.0 = Release|Any CPU {B4D4721B-A599-4149-BD3A-822C277FC0FE}.Release|x64.ActiveCfg = Release|Any CPU {B4D4721B-A599-4149-BD3A-822C277FC0FE}.Release|x64.Build.0 = Release|Any CPU {B4D4721B-A599-4149-BD3A-822C277FC0FE}.Release|x86.ActiveCfg = Release|Any CPU {B4D4721B-A599-4149-BD3A-822C277FC0FE}.Release|x86.Build.0 = Release|Any CPU {A7058669-DC59-4FFA-9A04-143D9569F55D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A7058669-DC59-4FFA-9A04-143D9569F55D}.Debug|Any CPU.Build.0 = Debug|Any CPU {A7058669-DC59-4FFA-9A04-143D9569F55D}.Debug|x64.ActiveCfg = Debug|Any CPU {A7058669-DC59-4FFA-9A04-143D9569F55D}.Debug|x64.Build.0 = Debug|Any CPU {A7058669-DC59-4FFA-9A04-143D9569F55D}.Debug|x86.ActiveCfg = Debug|Any CPU {A7058669-DC59-4FFA-9A04-143D9569F55D}.Debug|x86.Build.0 = Debug|Any CPU {A7058669-DC59-4FFA-9A04-143D9569F55D}.Release|Any CPU.ActiveCfg = Release|Any CPU {A7058669-DC59-4FFA-9A04-143D9569F55D}.Release|Any CPU.Build.0 = Release|Any CPU {A7058669-DC59-4FFA-9A04-143D9569F55D}.Release|x64.ActiveCfg = Release|Any CPU {A7058669-DC59-4FFA-9A04-143D9569F55D}.Release|x64.Build.0 = Release|Any CPU {A7058669-DC59-4FFA-9A04-143D9569F55D}.Release|x86.ActiveCfg = Release|Any CPU {A7058669-DC59-4FFA-9A04-143D9569F55D}.Release|x86.Build.0 = Release|Any CPU {F6CB0C45-09D5-4178-AEA6-F113E24FEEC5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {F6CB0C45-09D5-4178-AEA6-F113E24FEEC5}.Debug|Any CPU.Build.0 = Debug|Any CPU {F6CB0C45-09D5-4178-AEA6-F113E24FEEC5}.Debug|x64.ActiveCfg = Debug|Any CPU {F6CB0C45-09D5-4178-AEA6-F113E24FEEC5}.Debug|x64.Build.0 = Debug|Any CPU {F6CB0C45-09D5-4178-AEA6-F113E24FEEC5}.Debug|x86.ActiveCfg = Debug|Any CPU {F6CB0C45-09D5-4178-AEA6-F113E24FEEC5}.Debug|x86.Build.0 = Debug|Any CPU {F6CB0C45-09D5-4178-AEA6-F113E24FEEC5}.Release|Any CPU.ActiveCfg = Release|Any CPU {F6CB0C45-09D5-4178-AEA6-F113E24FEEC5}.Release|Any CPU.Build.0 = Release|Any CPU {F6CB0C45-09D5-4178-AEA6-F113E24FEEC5}.Release|x64.ActiveCfg = Release|Any CPU {F6CB0C45-09D5-4178-AEA6-F113E24FEEC5}.Release|x64.Build.0 = Release|Any CPU {F6CB0C45-09D5-4178-AEA6-F113E24FEEC5}.Release|x86.ActiveCfg = Release|Any CPU {F6CB0C45-09D5-4178-AEA6-F113E24FEEC5}.Release|x86.Build.0 = Release|Any CPU {72F07BEF-4379-4FD9-8CBA-CFB01D004718}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {72F07BEF-4379-4FD9-8CBA-CFB01D004718}.Debug|Any CPU.Build.0 = Debug|Any CPU {72F07BEF-4379-4FD9-8CBA-CFB01D004718}.Debug|x64.ActiveCfg = Debug|Any CPU {72F07BEF-4379-4FD9-8CBA-CFB01D004718}.Debug|x64.Build.0 = Debug|Any CPU {72F07BEF-4379-4FD9-8CBA-CFB01D004718}.Debug|x86.ActiveCfg = Debug|Any CPU {72F07BEF-4379-4FD9-8CBA-CFB01D004718}.Debug|x86.Build.0 = Debug|Any CPU {72F07BEF-4379-4FD9-8CBA-CFB01D004718}.Release|Any CPU.ActiveCfg = Release|Any CPU {72F07BEF-4379-4FD9-8CBA-CFB01D004718}.Release|Any CPU.Build.0 = Release|Any CPU {72F07BEF-4379-4FD9-8CBA-CFB01D004718}.Release|x64.ActiveCfg = Release|Any CPU {72F07BEF-4379-4FD9-8CBA-CFB01D004718}.Release|x64.Build.0 = Release|Any CPU {72F07BEF-4379-4FD9-8CBA-CFB01D004718}.Release|x86.ActiveCfg = Release|Any CPU {72F07BEF-4379-4FD9-8CBA-CFB01D004718}.Release|x86.Build.0 = Release|Any CPU {AF8ED740-D77D-4080-9952-59232C57767C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {AF8ED740-D77D-4080-9952-59232C57767C}.Debug|Any CPU.Build.0 = Debug|Any CPU {AF8ED740-D77D-4080-9952-59232C57767C}.Debug|x64.ActiveCfg = Debug|Any CPU {AF8ED740-D77D-4080-9952-59232C57767C}.Debug|x64.Build.0 = Debug|Any CPU {AF8ED740-D77D-4080-9952-59232C57767C}.Debug|x86.ActiveCfg = Debug|Any CPU {AF8ED740-D77D-4080-9952-59232C57767C}.Debug|x86.Build.0 = Debug|Any CPU {AF8ED740-D77D-4080-9952-59232C57767C}.Release|Any CPU.ActiveCfg = Release|Any CPU {AF8ED740-D77D-4080-9952-59232C57767C}.Release|Any CPU.Build.0 = Release|Any CPU {AF8ED740-D77D-4080-9952-59232C57767C}.Release|x64.ActiveCfg = Release|Any CPU {AF8ED740-D77D-4080-9952-59232C57767C}.Release|x64.Build.0 = Release|Any CPU {AF8ED740-D77D-4080-9952-59232C57767C}.Release|x86.ActiveCfg = Release|Any CPU {AF8ED740-D77D-4080-9952-59232C57767C}.Release|x86.Build.0 = Release|Any CPU {6661A967-81D7-4236-99BC-AD42F71B6C1F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {6661A967-81D7-4236-99BC-AD42F71B6C1F}.Debug|Any CPU.Build.0 = Debug|Any CPU {6661A967-81D7-4236-99BC-AD42F71B6C1F}.Debug|x64.ActiveCfg = Debug|Any CPU {6661A967-81D7-4236-99BC-AD42F71B6C1F}.Debug|x64.Build.0 = Debug|Any CPU {6661A967-81D7-4236-99BC-AD42F71B6C1F}.Debug|x86.ActiveCfg = Debug|Any CPU {6661A967-81D7-4236-99BC-AD42F71B6C1F}.Debug|x86.Build.0 = Debug|Any CPU {6661A967-81D7-4236-99BC-AD42F71B6C1F}.Release|Any CPU.ActiveCfg = Release|Any CPU {6661A967-81D7-4236-99BC-AD42F71B6C1F}.Release|Any CPU.Build.0 = Release|Any CPU {6661A967-81D7-4236-99BC-AD42F71B6C1F}.Release|x64.ActiveCfg = Release|Any CPU {6661A967-81D7-4236-99BC-AD42F71B6C1F}.Release|x64.Build.0 = Release|Any CPU {6661A967-81D7-4236-99BC-AD42F71B6C1F}.Release|x86.ActiveCfg = Release|Any CPU {6661A967-81D7-4236-99BC-AD42F71B6C1F}.Release|x86.Build.0 = Release|Any CPU {8CE4D269-B685-438C-AA1B-48D3E73C60DB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {8CE4D269-B685-438C-AA1B-48D3E73C60DB}.Debug|Any CPU.Build.0 = Debug|Any CPU {8CE4D269-B685-438C-AA1B-48D3E73C60DB}.Debug|x64.ActiveCfg = Debug|Any CPU {8CE4D269-B685-438C-AA1B-48D3E73C60DB}.Debug|x64.Build.0 = Debug|Any CPU {8CE4D269-B685-438C-AA1B-48D3E73C60DB}.Debug|x86.ActiveCfg = Debug|Any CPU {8CE4D269-B685-438C-AA1B-48D3E73C60DB}.Debug|x86.Build.0 = Debug|Any CPU {8CE4D269-B685-438C-AA1B-48D3E73C60DB}.Release|Any CPU.ActiveCfg = Release|Any CPU {8CE4D269-B685-438C-AA1B-48D3E73C60DB}.Release|Any CPU.Build.0 = Release|Any CPU {8CE4D269-B685-438C-AA1B-48D3E73C60DB}.Release|x64.ActiveCfg = Release|Any CPU {8CE4D269-B685-438C-AA1B-48D3E73C60DB}.Release|x64.Build.0 = Release|Any CPU {8CE4D269-B685-438C-AA1B-48D3E73C60DB}.Release|x86.ActiveCfg = Release|Any CPU {8CE4D269-B685-438C-AA1B-48D3E73C60DB}.Release|x86.Build.0 = Release|Any CPU {3F01D095-EA20-44B5-8B0D-4713601B0D9E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {3F01D095-EA20-44B5-8B0D-4713601B0D9E}.Debug|Any CPU.Build.0 = Debug|Any CPU {3F01D095-EA20-44B5-8B0D-4713601B0D9E}.Debug|x64.ActiveCfg = Debug|Any CPU {3F01D095-EA20-44B5-8B0D-4713601B0D9E}.Debug|x64.Build.0 = Debug|Any CPU {3F01D095-EA20-44B5-8B0D-4713601B0D9E}.Debug|x86.ActiveCfg = Debug|Any CPU {3F01D095-EA20-44B5-8B0D-4713601B0D9E}.Debug|x86.Build.0 = Debug|Any CPU {3F01D095-EA20-44B5-8B0D-4713601B0D9E}.Release|Any CPU.ActiveCfg = Release|Any CPU {3F01D095-EA20-44B5-8B0D-4713601B0D9E}.Release|Any CPU.Build.0 = Release|Any CPU {3F01D095-EA20-44B5-8B0D-4713601B0D9E}.Release|x64.ActiveCfg = Release|Any CPU {3F01D095-EA20-44B5-8B0D-4713601B0D9E}.Release|x64.Build.0 = Release|Any CPU {3F01D095-EA20-44B5-8B0D-4713601B0D9E}.Release|x86.ActiveCfg = Release|Any CPU {3F01D095-EA20-44B5-8B0D-4713601B0D9E}.Release|x86.Build.0 = Release|Any CPU {9A2A964B-EF1B-4BB9-B57F-7E4798BFC1D6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {9A2A964B-EF1B-4BB9-B57F-7E4798BFC1D6}.Debug|Any CPU.Build.0 = Debug|Any CPU {9A2A964B-EF1B-4BB9-B57F-7E4798BFC1D6}.Debug|x64.ActiveCfg = Debug|Any CPU {9A2A964B-EF1B-4BB9-B57F-7E4798BFC1D6}.Debug|x64.Build.0 = Debug|Any CPU {9A2A964B-EF1B-4BB9-B57F-7E4798BFC1D6}.Debug|x86.ActiveCfg = Debug|Any CPU {9A2A964B-EF1B-4BB9-B57F-7E4798BFC1D6}.Debug|x86.Build.0 = Debug|Any CPU {9A2A964B-EF1B-4BB9-B57F-7E4798BFC1D6}.Release|Any CPU.ActiveCfg = Release|Any CPU {9A2A964B-EF1B-4BB9-B57F-7E4798BFC1D6}.Release|Any CPU.Build.0 = Release|Any CPU {9A2A964B-EF1B-4BB9-B57F-7E4798BFC1D6}.Release|x64.ActiveCfg = Release|Any CPU {9A2A964B-EF1B-4BB9-B57F-7E4798BFC1D6}.Release|x64.Build.0 = Release|Any CPU {9A2A964B-EF1B-4BB9-B57F-7E4798BFC1D6}.Release|x86.ActiveCfg = Release|Any CPU {9A2A964B-EF1B-4BB9-B57F-7E4798BFC1D6}.Release|x86.Build.0 = Release|Any CPU {B5D1D911-24D0-4910-84C7-82F57EA65B33}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {B5D1D911-24D0-4910-84C7-82F57EA65B33}.Debug|Any CPU.Build.0 = Debug|Any CPU {B5D1D911-24D0-4910-84C7-82F57EA65B33}.Debug|x64.ActiveCfg = Debug|Any CPU {B5D1D911-24D0-4910-84C7-82F57EA65B33}.Debug|x64.Build.0 = Debug|Any CPU {B5D1D911-24D0-4910-84C7-82F57EA65B33}.Debug|x86.ActiveCfg = Debug|Any CPU {B5D1D911-24D0-4910-84C7-82F57EA65B33}.Debug|x86.Build.0 = Debug|Any CPU {B5D1D911-24D0-4910-84C7-82F57EA65B33}.Release|Any CPU.ActiveCfg = Release|Any CPU {B5D1D911-24D0-4910-84C7-82F57EA65B33}.Release|Any CPU.Build.0 = Release|Any CPU {B5D1D911-24D0-4910-84C7-82F57EA65B33}.Release|x64.ActiveCfg = Release|Any CPU {B5D1D911-24D0-4910-84C7-82F57EA65B33}.Release|x64.Build.0 = Release|Any CPU {B5D1D911-24D0-4910-84C7-82F57EA65B33}.Release|x86.ActiveCfg = Release|Any CPU {B5D1D911-24D0-4910-84C7-82F57EA65B33}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution {3D165617-8A90-4BEB-B951-6168EDDA232D} = {1ABB8B79-9BF2-4C51-A920-9C17BF11273F} {A23ED3D6-6A1A-457C-A55A-21BCDC886A47} = {1ABB8B79-9BF2-4C51-A920-9C17BF11273F} {F17CB314-8CC5-4343-AAE8-7292B700E5BA} = {1ABB8B79-9BF2-4C51-A920-9C17BF11273F} {BA5DF6B6-378C-4880-9CC3-802F3B560EFF} = {1ABB8B79-9BF2-4C51-A920-9C17BF11273F} {B309F69E-87C4-43F3-9766-FCCDE6AC66A2} = {1ABB8B79-9BF2-4C51-A920-9C17BF11273F} {32BB0F08-5A9D-4245-8721-2A3B12607270} = {1ABB8B79-9BF2-4C51-A920-9C17BF11273F} {B4D4721B-A599-4149-BD3A-822C277FC0FE} = {1ABB8B79-9BF2-4C51-A920-9C17BF11273F} {A7058669-DC59-4FFA-9A04-143D9569F55D} = {1ABB8B79-9BF2-4C51-A920-9C17BF11273F} {F6CB0C45-09D5-4178-AEA6-F113E24FEEC5} = {1ABB8B79-9BF2-4C51-A920-9C17BF11273F} {72F07BEF-4379-4FD9-8CBA-CFB01D004718} = {1ABB8B79-9BF2-4C51-A920-9C17BF11273F} {AF8ED740-D77D-4080-9952-59232C57767C} = {1ABB8B79-9BF2-4C51-A920-9C17BF11273F} {8CE4D269-B685-438C-AA1B-48D3E73C60DB} = {1ABB8B79-9BF2-4C51-A920-9C17BF11273F} {B5D1D911-24D0-4910-84C7-82F57EA65B33} = {1ABB8B79-9BF2-4C51-A920-9C17BF11273F} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {ACEEC9A9-FE4F-43DD-918D-443A19399CE1} EndGlobalSection EndGlobal ================================================ FILE: language-ext.sln.DotSettings ================================================  True True True True True True True True True True True True True True True OUTSIDE_AND_INSIDE OUTSIDE True True True True True True True True True True True False True True True True ================================================ FILE: pack.bat ================================================ echo building the docs :: %LangExtRoot% is where the source code root should be (i.e. c:\dev\language-ext) :: %LangExtDocs% is where the docs root should be (i.e. c:\dev\louthy.github.io) Q: cd Q:\Dev\best-form\bestform\bin\Release\net8.0 bestform.exe "LanguageExt.Core" "%LangExtRoot%\LanguageExt.Core" "%LangExtDocs%\language-ext" "https://github.com/louthy/language-ext/tree/main" bestform.exe "LanguageExt.Parsec" "%LangExtRoot%\LanguageExt.Parsec" "%LangExtDocs%\language-ext" "https://github.com/louthy/language-ext/tree/main" bestform.exe "LanguageExt.FSharp" "%LangExtRoot%\LanguageExt.FSharp" "%LangExtDocs%\language-ext" "https://github.com/louthy/language-ext/tree/main" bestform.exe "LanguageExt.Rx" "%LangExtRoot%\LanguageExt.Rx" "%LangExtDocs%\language-ext" "https://github.com/louthy/language-ext/tree/main" bestform.exe "LanguageExt.Sys" "%LangExtRoot%\LanguageExt.Sys" "%LangExtDocs%\language-ext" "https://github.com/louthy/language-ext/tree/main" bestform.exe "LanguageExt.Streaming" "%LangExtRoot%\LanguageExt.Streaming" "%LangExtDocs%\language-ext" "https://github.com/louthy/language-ext/tree/main" echo committing them to git cd %LangExtDocs% git add . git commit -m "Language-ext documentation update" git push cd %LangExtRoot% echo building the artefacts dotnet restore dotnet pack LanguageExt.Core -c Release -o ../../artifacts/bin dotnet pack LanguageExt.FSharp -c Release -o ../../artifacts/bin dotnet pack LanguageExt.Parsec -c Release -o ../../artifacts/bin dotnet pack LanguageExt.Rx -c Release -o ../../artifacts/bin dotnet pack LanguageExt.Sys -c Release -o ../../artifacts/bin dotnet pack LanguageExt.Streaming -c Release -o ../../artifacts/bin ================================================ FILE: pack.sh ================================================ # Artifacts is where the DLLs are compiled to Artifacts=/media/paul/raid/dev/artifacts # $LangExtRoot is where the source code root should be LangExtRoot=/media/paul/raid/dev/language-ext sh clean.sh sh docs.sh cd $LangExtRoot || exit echo building the artefacts dotnet restore dotnet pack LanguageExt.Core -c Release -o $Artifacts dotnet pack LanguageExt.Streaming -c Release -o $Artifacts dotnet pack LanguageExt.FSharp -c Release -o $Artifacts dotnet pack LanguageExt.Parsec -c Release -o $Artifacts dotnet pack LanguageExt.Rx -c Release -o $Artifacts dotnet pack LanguageExt.Sys -c Release -o $Artifacts sh ../push-language-ext.sh ================================================ FILE: pjv/LanguageExt.Core.3.1.9-beta.nuspec ================================================  LanguageExt.Core 3.1.9-beta LanguageExt.Core Paul Louth Paul Louth https://github.com/louthy/language-ext/blob/master/LICENSE.md https://github.com/louthy/language-ext https://raw.githubusercontent.com/louthy/language-ext/master/backers-images/lang-ext-thumb.png false This library uses and abuses the features of C# 6 and 7 to provide a functional 'Base class library', that, if you squint, can look like extensions to the language itself. Copyright (c) Paul Louth. All rights reserved. C#, Functional, Language Extension, Monad, Option, Either, Reader, Writer, State, List, Set, Map, Queue, Memo, Memoization, Immutable, Lambda, Pattern Matching, Tuple

Notes from a Small Functional Island by Paul Louth

[POST_TITLE]

By Paul Louth [POST_DATE]

A short message from Paul

I thought it would be novel to include the full article as the email newsletter. I don't really care about clicks or any of that nonsense and so this is another potential way to consume the article (that may even be more accessible for some)!

However, due to the way that email clients format and butcher HTML, the experience might not be as good. I have noticed that, on my iPhone, the code snippets get line-wrapped, making them pretty hard to decipher. Still, I like the idea that if you've downloaded emails to your device, you can read the article offline, so I'm going to go with it for now. Feel free to let me know what you think.

But it is better to read the original article on paullouth.com

The formatting is much better there!

[POST_CONTENT]

Keep reading

[RECENT_ARTICLES]

Paul Louth © [YEAR_RANGE]

You're receiving this email because you signed up to the newsletter at paullouth.com. You can unsubscribe by going to the site, logging in, and toggling the 'Email Newsletter' option. Any problems or if you need to reach out for any reason, drop me an email to: paul@paullouth.com