diff --git a/FSharpPlus.sln b/FSharpPlus.sln index be3936bc9..9a22ec243 100644 --- a/FSharpPlus.sln +++ b/FSharpPlus.sln @@ -94,6 +94,8 @@ Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "FSharpPlus.TypeLevel", "src EndProject Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "FSharpPlus.Providers", "src\FSharpPlus\Providers\FSharpPlus.Providers.fsproj", "{9B93F5E5-3D53-42F1-96E2-06E6A7B496A0}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CSharpLib", "tests\CSharpLib\CSharpLib.csproj", "{7A5B766E-8141-4D8A-B3EB-91422FDBDF71}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -168,6 +170,18 @@ Global {9B93F5E5-3D53-42F1-96E2-06E6A7B496A0}.Release|x64.Build.0 = Release|Any CPU {9B93F5E5-3D53-42F1-96E2-06E6A7B496A0}.Release|x86.ActiveCfg = Release|Any CPU {9B93F5E5-3D53-42F1-96E2-06E6A7B496A0}.Release|x86.Build.0 = Release|Any CPU + {7A5B766E-8141-4D8A-B3EB-91422FDBDF71}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7A5B766E-8141-4D8A-B3EB-91422FDBDF71}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7A5B766E-8141-4D8A-B3EB-91422FDBDF71}.Debug|x64.ActiveCfg = Debug|Any CPU + {7A5B766E-8141-4D8A-B3EB-91422FDBDF71}.Debug|x64.Build.0 = Debug|Any CPU + {7A5B766E-8141-4D8A-B3EB-91422FDBDF71}.Debug|x86.ActiveCfg = Debug|Any CPU + {7A5B766E-8141-4D8A-B3EB-91422FDBDF71}.Debug|x86.Build.0 = Debug|Any CPU + {7A5B766E-8141-4D8A-B3EB-91422FDBDF71}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7A5B766E-8141-4D8A-B3EB-91422FDBDF71}.Release|Any CPU.Build.0 = Release|Any CPU + {7A5B766E-8141-4D8A-B3EB-91422FDBDF71}.Release|x64.ActiveCfg = Release|Any CPU + {7A5B766E-8141-4D8A-B3EB-91422FDBDF71}.Release|x64.Build.0 = Release|Any CPU + {7A5B766E-8141-4D8A-B3EB-91422FDBDF71}.Release|x86.ActiveCfg = Release|Any CPU + {7A5B766E-8141-4D8A-B3EB-91422FDBDF71}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -178,6 +192,7 @@ Global {FED64B0A-3FD7-4357-98B6-0B01A3A26EC7} = {ED8079DD-2B06-4030-9F0F-DC548F98E1C4} {B725BEFA-524E-4FD8-BFFF-4AEBCD03D8CF} = {81F5F559-FD23-4E90-9EE6-3E2A2C1A7F96} {9B93F5E5-3D53-42F1-96E2-06E6A7B496A0} = {81F5F559-FD23-4E90-9EE6-3E2A2C1A7F96} + {7A5B766E-8141-4D8A-B3EB-91422FDBDF71} = {ED8079DD-2B06-4030-9F0F-DC548F98E1C4} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {789B5FFA-7891-4F60-831E-42C3C5ED2C51} diff --git a/docsrc/content/index.fsx b/docsrc/content/index.fsx index 5b080b358..ee1e06ed1 100644 --- a/docsrc/content/index.fsx +++ b/docsrc/content/index.fsx @@ -36,6 +36,8 @@ The additions can be summarised as: * A polymorphic [Lenses/Optics](tutorial.html#Lens) to easily read and update parts of immutable data + * Generic methods that help you with [parsing](parsing.html) + Note, however, that F#+ does not go into solving a specific thing for a specific technology, such as JSON parsing. diff --git a/docsrc/content/operators-common.fsx b/docsrc/content/operators-common.fsx index 68266b60c..59a5ed375 100644 --- a/docsrc/content/operators-common.fsx +++ b/docsrc/content/operators-common.fsx @@ -7,22 +7,19 @@ open FSharpPlus (** -Operators - Common Combinators -=============================== +# Operators - Common Combinators These generic functions and operators are used commonly and are not part of any other abstraction. You can find these in the API docs: [Operators.fs](reference/operators.html) -flip -==== +## flip Creates a new function with first two arguments flipped. -konst -===== +## konst Create a function that always returns the given argument. This is known as a 'constant' function. @@ -36,8 +33,7 @@ let a = [1;2;3] |> filter (konst true);; // val a : int list = [1; 2; 3] (** -curry, uncurry, curryN, uncurryN -================================ +## curry, uncurry, curryN, uncurryN Currying is the process of taking a function expecting a tuple, and returning a function with the same number of arguments as the tuple size. @@ -56,8 +52,7 @@ let b = curryN addThreeNums 1 2 3;; // val it : int = 6 (** -Functions as operators - -============================== +## Functions as operators - A pair of operators `` are defined to allow any function to be used as an operator. It will flip the args of your function so that it makes sense when @@ -72,8 +67,7 @@ let c = 10 3;; // val c : bool = true (** -tap -=== +## tap Tap executes a side-effect function, then returns the original input value. Consider this as 'tapping into' a chain of functions. @@ -91,8 +85,7 @@ names |> map String.toUpper |> tap (printfn "%A") |> map String.toLower;; // val it : string list = ["john"; "smith"] (** -either -====== +## either Extracts the value inside a Result from either side - whether Ok or Error. @@ -123,8 +116,7 @@ Don't confuse the `either` function with `result` which lifts a value into a Functor, just like `return` when in a computation expression. -option -====== +## option Takes a function, a default value and a option value. If the option value is None, the function returns the default value. Otherwise, it applies the function to the value inside Some and returns the result. @@ -133,8 +125,7 @@ let inline option f n = function Some x -> f x | None -> n (** -tuple2, tuple3, ...tuple8 -========================= +## tuple2, tuple3, ...tuple8 Functions that generate a tuple. The number indicates the number of arguments that are defined, and the corresponding size of tuple. @@ -147,3 +138,48 @@ let inline tuple5 a b c d e = a,b,c,d,e let inline tuple6 a b c d e f = a,b,c,d,e,f let inline tuple7 a b c d e f g = a,b,c,d,e,f,g let inline tuple8 a b c d e f g h = a,b,c,d,e,f,g,h + + +(** +## Explicit + +Explicit allows you to use `explicit` generic method for standard types and types that implement the static explicit type cast signature. + +### Minimal definition + +In order to use the `explicit` generic method together with a type it needs to implement the following: +*) + +(** +```f# +static member op_Explicit (x:'r) :'T +``` +or in C# +```c# +public static explicit operator T(R s) +``` + +This is useful when dealing with C# libraries that make heavy use of explicit conversions. +*) + +(** +## Implicit + +Implicit allows you to use `implicit` generic method for standard types and types that implement the static implicit type cast signature. + +### Minimal definition + +In order to use the `implicit` generic method together with a type it needs to implement the following: +*) + +(** +```f# +static member op_Implicit (x:'r) :'T +``` +or in C# +```c# +public static implicit operator T(R s) +``` + +This is useful when dealing with C# libraries that make heavy use of implicit conversions. +*) \ No newline at end of file diff --git a/docsrc/content/parsing.fsx b/docsrc/content/parsing.fsx new file mode 100644 index 000000000..bdbb6b4aa --- /dev/null +++ b/docsrc/content/parsing.fsx @@ -0,0 +1,76 @@ +(*** hide ***) +// This block of code is omitted in the generated HTML documentation. Use +// it to define helpers that you do not want to show in the documentation. +#I "../../bin" +open System +#r @"../../src/FSharpPlus/bin/Release/net45/FSharpPlus.dll" +open FSharpPlus +(** +# Parsing + +F#+ provides several helper methods in order to simplify building parsers and parsing like tasks. +*) + +(** +## Parse + +Parse allows you to use `parse` generic method for standard types and types that implement a static Parse method with the correct signature. + + +### Minimal definition + +*) + +(** +```f# +static member Parse (x:'r) :'T +``` +or + +```f# +static member Parse (x:'r, c:CultureInfo) :'T +``` + +*) + +(** +## TryParse + +TryParse allows you to use `tryParse` generic method for standard types and types that implement a static TryParse method with the correct signature. + +### Minimal definition + +In order to use `tryParse` together with a type the type needs to implement a TryParse like static method. + +*) + +(** +You can use F# style TryParse: +```f# +static member TryParse(value:'r) : 'T option +``` +or C# style TryParse: +```f# +static member TryParse (x:'r, [] result: 'T byref) :bool +``` +expressed in C# that would be: +```c# +public static bool TryParse (string x, out T result) +``` + +A neat thing when you have types that implement the above definition is that it's simple to define active patterns: +*) + +let (|Port|_|) : _-> UInt16 option = tryParse +let (|IPAddress|_|) :_->System.Net.IPAddress option = tryParse + +(** +## sscanf, trySscanf and friends + +In F# you have some nice utility functions for creating printf style string writer function. In F#+ we find the inverse: sscanf and trySscanf. + +For instance if you want to parse based on known format of a url: +*) + +let route1 x = trySscanf "/api/resource/%d" x +let parsed : int option = route1 "/api/resource/1" diff --git a/docsrc/content/type-cont.fsx b/docsrc/content/type-cont.fsx index e47e23c14..d0072ec62 100644 --- a/docsrc/content/type-cont.fsx +++ b/docsrc/content/type-cont.fsx @@ -17,7 +17,7 @@ by reading [Real World Functional Programming by Tomas Petricek with Jon Skeet]( Examples -------- -In order to get an idea about this style, let us contrast some of the examples and how they look in when using f#+ or without help. +In order to get an idea about this style, let us contrast some of the examples and how they look in when using F#+ or without help. *) diff --git a/docsrc/tools/Program.fs b/docsrc/tools/Program.fs index 11f6bbee2..6a9a77a0e 100644 --- a/docsrc/tools/Program.fs +++ b/docsrc/tools/Program.fs @@ -178,7 +178,8 @@ let main argv = Target.create "ReleaseDocs" (fun _ -> let tempDocsDir = rootDir @@ "temp/gh-pages" Shell.cleanDir tempDocsDir - Git.Repository.cloneSingleBranch rootDir (gitHome + "/" + gitName + ".git") "gh-pages" tempDocsDir + let repoUrl = Git.Config.remoteOriginUrl rootDir + Git.Repository.cloneSingleBranch rootDir repoUrl "gh-pages" tempDocsDir let docDir = rootDir @@ "docs" Shell.copyRecursive docDir tempDocsDir true |> Trace.tracefn "%A" Git.Staging.stageAll tempDocsDir diff --git a/docsrc/tools/doclib.fs b/docsrc/tools/doclib.fs index 25e4ea613..b12fd0c19 100644 --- a/docsrc/tools/doclib.fs +++ b/docsrc/tools/doclib.fs @@ -1206,7 +1206,17 @@ module Git = sprintf "clone -b %s --single-branch %s %s" branchName repoUrl toPath |> runSimpleGitCommand workingDir |> Trace.trace - + module Config = + /// Get remote origin url + /// ## Parameters + /// + /// - `workingDir` - The working directory. + let remoteOriginUrl workingDir = + let url = + "config --get remote.origin.url" + |> runSimpleGitCommand workingDir + url.Trim([|'\n';'\r';'\t';' '|]) + module DotNet = diff --git a/docsrc/tools/generate.fs b/docsrc/tools/generate.fs index 893880061..0620e8f1e 100644 --- a/docsrc/tools/generate.fs +++ b/docsrc/tools/generate.fs @@ -77,11 +77,6 @@ let evaluationOptions = let compilerOptions = String.concat " " ( Array.toList evaluationOptions) -// PlantUml processing -let abstractions = Path.templates "abstractions.plantuml" -let plantUMLDiag = toUrl (IO.File.ReadAllText abstractions) -let customize (doc:LiterateDocument) = doc.With (paragraphs = (doc.Paragraphs |>> function InlineBlock (x,y) -> InlineBlock ((replace "{plantUMLDiag}" plantUMLDiag x),y) | x -> x)) - let parseFsx path = let doc = @@ -90,7 +85,7 @@ let parseFsx path = compilerOptions = compilerOptions, fsiEvaluator = FSharp.Literate.FsiEvaluator(evaluationOptions)) - let body = FSharp.Literate.Literate.FormatLiterateNodes(doc, OutputKind.Html, "", true, true) |> customize + let body = FSharp.Literate.Literate.FormatLiterateNodes(doc, OutputKind.Html, "", true, true) for err in doc.Errors do Printf.printfn "%A" err body, body.FormattedTips @@ -102,7 +97,7 @@ let parseMd path = path, compilerOptions = compilerOptions, fsiEvaluator = FSharp.Literate.FsiEvaluator(evaluationOptions)) - let body = FSharp.Literate.Literate.FormatLiterateNodes(doc, OutputKind.Html, "", true, true) |> customize + let body = FSharp.Literate.Literate.FormatLiterateNodes(doc, OutputKind.Html, "", true, true) for err in doc.Errors do Printf.printfn "%A" err body, body.FormattedTips @@ -142,5 +137,6 @@ let copyFiles () = let buildDocumentation () = Directory.copyRecursive Path.files Path.output IO.Directory.EnumerateFiles Path.content + |> Seq.filter (fun file-> not <| file.EndsWith(".DS_Store")) |> Seq.iter (processFile Path.output) diff --git a/docsrc/tools/manually_build_docs.sh b/docsrc/tools/manually_build_docs.sh index 3adaabda9..f02c4b849 100755 --- a/docsrc/tools/manually_build_docs.sh +++ b/docsrc/tools/manually_build_docs.sh @@ -6,7 +6,7 @@ dotnet tool restore # Build # Note: some bug means can't build debug -dotnet build -c Release +dotnet build -c Release FSharpPlus.sln # Gen docs #mkdir -p bin/FSharpPlus/netstandard2.0/ diff --git a/docsrc/tools/templates/abstractions.plantuml b/docsrc/tools/templates/abstractions.plantuml index 076cb3831..0f8b4a579 100644 --- a/docsrc/tools/templates/abstractions.plantuml +++ b/docsrc/tools/templates/abstractions.plantuml @@ -3,111 +3,111 @@ skinparam svgDimensionStyle false together { abstract class Semigroup [[http://fsprojects.github.io/FSharpPlus/abstraction-semigroup.html]] { - + (+) x y [[[http://fsprojects.github.io/FSharpPlus/abstraction-semigroup.html#plus {Appends two semigroups} ]]] + + (+) x y } abstract class Comonad [[http://fsprojects.github.io/FSharpPlus/abstraction-comonad.html]] { - + extract x [[[http://fsprojects.github.io/FSharpPlus/abstraction-comonad.html#extract]]] - + (=>>) s g | extend s g [[[http://fsprojects.github.io/FSharpPlus/abstraction-comonad.html#extend]]] + + extract x + + (=>>) s g | extend s g == + duplicate x } } abstract class Monoid [[http://fsprojects.github.io/FSharpPlus/abstraction-monoid.html]] { - + zero [[[http://fsprojects.github.io/FSharpPlus/abstraction-monoid.html#zero {Identity for (+)} ]]] - + (+) x y [[[http://fsprojects.github.io/FSharpPlus/abstraction-monoid.html#plus {Appends both monoids} ]]] + + zero + + (+) x y {Appends both monoids} == - + Seq.sum x [[[http://fsprojects.github.io/FSharpPlus/abstraction-monoid.html#sum {Flatten a sequence of monoids} ]]] + + Seq.sum x } abstract class Functor [[http://fsprojects.github.io/FSharpPlus/abstraction-functor.html]] { - + map f x [[[http://fsprojects.github.io/FSharpPlus/abstraction-functor.html#map {Lift a function into a Functor} ]]] + + map f x == + unzip x } abstract class Contravariant [[http://fsprojects.github.io/FSharpPlus/abstraction-contravariant.html]] { - + contramap f x [[[http://fsprojects.github.io/FSharpPlus/abstraction-contravariant.html#contramap]]] + + contramap f x } abstract class Applicative [[http://fsprojects.github.io/FSharpPlus/abstraction-applicative.html]] { - + return x [[[http://fsprojects.github.io/FSharpPlus/abstraction-monad.html#return {Lift a value into a Functor} ]]] - + (<*>) f x [[[http://fsprojects.github.io/FSharpPlus/abstraction-applicative.html {Apply a lifted argument to a lifted function} ]]] + + return x + + (<*>) f x == - + map f x [[[http://fsprojects.github.io/FSharpPlus/abstraction-functor.html#map {Lift a function into a Functor} ]]] - + lift2 f x y [[[http://fsprojects.github.io/FSharpPlus/abstraction-applicative.html#lift2 {Applies 2 lifted values to a non-lifted function} ]]] + + map f x + + lift2 f x y } abstract class Alternative [[http://fsprojects.github.io/FSharpPlus/abstraction-alternative.html]] { - + empty [[[http://fsprojects.github.io/FSharpPlus/abstraction-alternative.html]]] - + (<|>) f x [[[http://fsprojects.github.io/FSharpPlus/abstraction-alternative.html]]] + + empty + + (<|>) f x == - + mfilter p x [[[http://fsprojects.github.io/FSharpPlus/abstraction-alternative.html#mfilter {Returns all values satisfying the predicate, if the predicate returns false will use the empty value} ]]] + + mfilter p x } abstract class Monad [[http://fsprojects.github.io/FSharpPlus/abstraction-monad.html]] { - + return x [[[http://fsprojects.github.io/FSharpPlus/abstraction-monad.html#return {Lift a value into a Functor} ]]] - + (>>=) x f [[[http://fsprojects.github.io/FSharpPlus/abstraction-monad.html#bind {Takes a monadic value and a function from a plain type to a monadic value, and returns a new monadic value} ]]] + + return x + + (>>=) x f == - + map f x [[[http://fsprojects.github.io/FSharpPlus/abstraction-functor.html#map {Lift a function into a Functor} ]]] - + join x [[[http://fsprojects.github.io/FSharpPlus/abstraction-functor.html#join {Flattens two layers of monadic information into one} ]]] + + map f x + + join x } abstract class Bifunctor [[http://fsprojects.github.io/FSharpPlus/abstraction-bifunctor.html]] { - + {static} bimap f g x [[[http://fsprojects.github.io/FSharpPlus/abstraction-bifunctor.html#bimap]]] - + first f x [[[http://fsprojects.github.io/FSharpPlus/abstraction-bifunctor.html#first]]] - + second f x [[[http://fsprojects.github.io/FSharpPlus/abstraction-bifunctor.html#second]]] + + {static} bimap f g x + + first f x + + second f x } abstract class Foldable [[http://fsprojects.github.io/FSharpPlus/abstraction-foldable.html]] { - {static} + toSeq x [[[http://fsprojects.github.io/FSharpPlus/abstraction-foldable.html#toSeq {Converts to a seq} ]]] + {static} + toSeq x } abstract class Bifoldable [[http://fsprojects.github.io/FSharpPlus/abstraction-bifoldable.html]] { - {static} + bifoldMap f g x [[[http://fsprojects.github.io/FSharpPlus/abstraction-bifoldable.html#bifoldMap]]] - {static} + bifold f g z x [[[http://fsprojects.github.io/FSharpPlus/abstraction-bifoldable.html#bifold]]] - {static} + bifoldBack f g x z [[[http://fsprojects.github.io/FSharpPlus/abstraction-bifoldable.html#bifoldBack]]] - + bisum x [[[http://fsprojects.github.io/FSharpPlus/abstraction-bifoldable.html#bisum]]] + {static} + bifoldMap f g x + {static} + bifold f g z x + {static} + bifoldBack f g x z + + bisum x } abstract class Traversable [[http://fsprojects.github.io/FSharpPlus/abstraction-traversable.html]] { - {static} + traverse f x [[[http://fsprojects.github.io/FSharpPlus/abstraction-traversable.html#traverse {Map each element of a structure to an action, evaluate them left to right and collect result }]]] - + sequence x [[[http://fsprojects.github.io/FSharpPlus/abstraction-traversable.html#sequence {Evaluate each action in the structure left to right and collect the result } ]]] + {static} + traverse f x + + sequence x } abstract class Bitraversable [[http://fsprojects.github.io/FSharpPlus/abstraction-bitraversable.html]] { - {static} + bitraverse f x [[[http://fsprojects.github.io/FSharpPlus/abstraction-bitraversable.html#bitraverse {Evaluates the relevant functions at each element in the structure, running the action, and collect results }]]] - + bisequence x [[[http://fsprojects.github.io/FSharpPlus/abstraction-bitraversable.html#bisequence {Sequences all the actions in a structure, building a new structure with the same shape using the results of the actions } ]]] + {static} + bitraverse f x + + bisequence x } together { abstract class Profunctor [[http://fsprojects.github.io/FSharpPlus/abstraction-profunctor.html]] { - + {static} dimap f g x [[[http://fsprojects.github.io/FSharpPlus/abstraction-profunctor.html#dimap]]] - + lmap f x [[[http://fsprojects.github.io/FSharpPlus/abstraction-profunctor.html#lmap]]] - + rmap f x [[[http://fsprojects.github.io/FSharpPlus/abstraction-profunctor.html#rmap]]] + + {static} dimap f g x + + lmap f x + + rmap f x } abstract class Category [[http://fsprojects.github.io/FSharpPlus/abstraction-category.html]] { - + catId [[[http://fsprojects.github.io/FSharpPlus/abstraction-category.html#catId {The identity morphism. }]]] - + (<<<) f g [[[http://fsprojects.github.io/FSharpPlus/abstraction-category.html#catComp {Right-to-left morphism composition. } ]]] + + catId + + (<<<) f g == - + (>>>) f g [[[http://fsprojects.github.io/FSharpPlus/abstraction-category.html#catComp {Left-to-right morphism composition. } ]]] + + (>>>) f g } } abstract class Arrow [[http://fsprojects.github.io/FSharpPlus/abstraction-arrow.html]] { - + arr f [[[http://fsprojects.github.io/FSharpPlus/abstraction-arrow.html#arr {Lift a function to an arrow. }]]] - + arrFirst f g [[[http://fsprojects.github.io/FSharpPlus/abstraction-arrow.html#arrFirst {Send the 1st component of the input through the argument arrow and copy the rest unchanged to the output. } ]]] + + arr f + + arrFirst f g == - + arrSecond f g [[[http://fsprojects.github.io/FSharpPlus/abstraction-arrow.html#arrSecond {Send the 2nd component of the input through the argument arrow and copy the rest unchanged to the output. } ]]] - + (***) f g [[[http://fsprojects.github.io/FSharpPlus/abstraction-arrow.html#arrCombine {Split the input between the two argument arrows and combine their output. } ]]] - + (&&&) f g [[[http://fsprojects.github.io/FSharpPlus/abstraction-arrow.html#fanout {Send the input to both argument arrows and combine their output. } ]]] + + arrSecond f g + + (***) f g + + (&&&) f g } diff --git a/src/FSharpPlus.Docs/FSharpPlus.Docs.fsproj b/src/FSharpPlus.Docs/FSharpPlus.Docs.fsproj index f89f343ee..e3d8f537d 100644 --- a/src/FSharpPlus.Docs/FSharpPlus.Docs.fsproj +++ b/src/FSharpPlus.Docs/FSharpPlus.Docs.fsproj @@ -38,6 +38,9 @@ Docs\%(FileName)%(Extension) + + Docs\%(FileName)%(Extension) + Docs\Type\%(FileName)%(Extension) diff --git a/src/FSharpPlus/Data/DList.fs b/src/FSharpPlus/Data/DList.fs index c565cc419..1a55e0e9f 100644 --- a/src/FSharpPlus/Data/DList.fs +++ b/src/FSharpPlus/Data/DList.fs @@ -270,7 +270,7 @@ module DList = /// O(n). Returns a seq of the DList elements. let inline toSeq (l: DList<'T>) = l :> seq<'T> - // additions to fit f#+ : + // additions to fit F#+ : let inline map f (x: DList<_>) = DList.foldBack (cons << f ) x empty let concat x = DList.fold append empty x let inline ap f x = concat <| map (fun y -> map ((|>) y) f) x diff --git a/tests/CSharpLib/CSharpLib.csproj b/tests/CSharpLib/CSharpLib.csproj new file mode 100644 index 000000000..a11689f84 --- /dev/null +++ b/tests/CSharpLib/CSharpLib.csproj @@ -0,0 +1,7 @@ + + + + netstandard2.0;net452 + + + diff --git a/tests/CSharpLib/CustomerId.cs b/tests/CSharpLib/CustomerId.cs new file mode 100644 index 000000000..11193afb7 --- /dev/null +++ b/tests/CSharpLib/CustomerId.cs @@ -0,0 +1,26 @@ +using System; + +namespace CSharpLib +{ + public struct CustomerId : IEquatable + { + public long Value { get; } + public CustomerId(long value) => this.Value = value; + public static readonly CustomerId Empty = new CustomerId(); + public bool Equals(CustomerId other) => !ReferenceEquals(null, other) && Equals(Value, other.Value); + public override bool Equals(object obj) => obj is CustomerId id && Equals(id); + public override int GetHashCode() => Value.GetHashCode(); + public override string ToString() => Value.ToString(); + public static bool TryParse(string value,out CustomerId id) + { + var res = value.Split('_'); + if (res.Length == 2 && res[0] == "C" && long.TryParse(res[1],out var val)) + { + id=new CustomerId(val); + return true; + } + id = Empty; + return false; + } + } +} diff --git a/tests/FSharpPlus.Tests/FSharpPlus.Tests.fsproj b/tests/FSharpPlus.Tests/FSharpPlus.Tests.fsproj index cf3d5ed5f..68a2efc4f 100644 --- a/tests/FSharpPlus.Tests/FSharpPlus.Tests.fsproj +++ b/tests/FSharpPlus.Tests/FSharpPlus.Tests.fsproj @@ -29,6 +29,7 @@ True + diff --git a/tests/FSharpPlus.Tests/General.fs b/tests/FSharpPlus.Tests/General.fs index 8b74e7f67..c23e7645f 100644 --- a/tests/FSharpPlus.Tests/General.fs +++ b/tests/FSharpPlus.Tests/General.fs @@ -10,6 +10,7 @@ open FSharpPlus.Control open NUnit.Framework open Helpers open FSharpPlus.Math.Applicative +open CSharpLib type WrappedListA<'s> = WrappedListA of 's list with static member ToSeq (WrappedListA lst) = SideEffects.add "Using WrappedListA's ToSeq"; List.toSeq lst @@ -1715,7 +1716,15 @@ module Splits = Assert.IsTrue((d = Sum 13)) -module Parsing = +module Parsing = + let (|Int32|_|) : _-> Int32 option = tryParse + type ProductId = { Value:int } + with + static member TryParse(value:string) : ProductId option= + match value.Split('_') |> List.ofArray with + | "P" :: Int32 v :: [] -> Some { Value = v } + | _ -> None + [] let parseDateTime () = #if MONO @@ -1749,7 +1758,18 @@ module Parsing = let r123: WrappedListA option = tryParse "[1;2;3]" areStEqual r123 (Some (WrappedListA [1; 2; 3])) - + + [] + let parseCustomType () = + let v1 : CustomerId option = tryParse "C_1" + Assert.IsTrue((v1.Value.Value = 1L)) + let v2 : CustomerId option = tryParse "C_X" + Assert.IsTrue(Option.isNone v2) + let v3 : ProductId option = tryParse "P_1" + Assert.IsTrue((v3.Value.Value = 1)) + let v4 : ProductId option = tryParse "P_X" + Assert.IsTrue(Option.isNone v4) + [] let scanfParsing () = let _ccx: int * uint32 * float * float32 * int * uint32 * float * float32 * int * uint32 * float * float32 * int * uint32 * float * float32 * int = parseArray [|"34"; "24"; "34"; "4"; "5"; "6"; "7"; "8"; "9"; "10"; "11"; "12"; "13"; "14"; "15"; "16"; "17"|]