xyncro / chiron

JSON for F#
https://xyncro.tech/chiron
MIT License
173 stars 41 forks source link

Chiron 7 & .NET Core #81

Closed neoeinstein closed 5 years ago

neoeinstein commented 7 years ago

This is a rather large, breaking rewrite of Chiron 7. The API has been altered and the code optimized.

This is nearly ready to go. It just needs a bit of API cleanup. API documentation should follow shortly after.

What has changed?

Use of struct Result

The use of a new struct Result type was considered. What I found was that operations using a struct Result incurred a performance penalty. While they were nearly allocation free, I found that they could increase the execution time by 100%, resulting in execution times worse than before starting the optimization work. Finding this penalty unacceptable, I chose to use a reference-type Result for Chiron.

Benchmarks

Benchmarks were run on my local machine.


BenchmarkDotNet=v0.10.3.0, OS=Microsoft Windows 6.1.7601
Processor=Intel(R) Core(TM) i7-4800MQ CPU 2.70GHz, ProcessorCount=8
Frequency=2630683 Hz, Resolution=380.1294 ns, Timer=TSC
dotnet cli version=1.0.1
  [Host]    : .NET Core 4.6.25009.03, 64bit RyuJIT
  MediumRun : .NET Core 4.6.25009.03, 64bit RyuJIT

Job=MediumRun  LaunchCount=2  TargetCount=15  
WarmupCount=10  

When parsing:

Method Name Mean StdDev Scaled Scaled-StdDev Gen 0 Gen 1 Gen 2 Allocated
Chiron_Old error 8.1883 us 0.0412 us 1.00 0.00 2.9989 - - 10.57 kB
Chiron_New error 2.6634 us 0.0088 us 0.33 0.00 0.2899 - - 1.2 kB
Chiron_Old fparsec 33.5850 us 0.4901 us 1.00 0.00 7.5765 - - 28.15 kB
Chiron_New fparsec 15.8960 us 0.0748 us 0.47 0.01 1.2044 - - 6.19 kB
Chiron_Old prettyuser 80.4490 us 1.1692 us 1.00 0.00 17.8385 - - 64.96 kB
Chiron_New prettyuser 39.0248 us 0.1181 us 0.49 0.01 3.8818 - - 16.59 kB
Chiron_Old social 199,318.9076 us 822.8354 us 1.00 0.00 30300.0000 2133.3333 212.5000 183.99 MB
Chiron_New social 107,816.0160 us 1,767.2977 us 0.54 0.01 5025.0000 1204.1667 - 30.07 MB
Chiron_Old user 78.1747 us 0.3920 us 1.00 0.00 17.8385 - - 64.96 kB
Chiron_New user 39.0666 us 0.1309 us 0.50 0.00 3.8493 - - 16.59 kB

When formatting:

Method Name Mean StdDev Scaled Scaled-StdDev Gen 0 Gen 1 Allocated
Chiron_New error 723.8419 ns 4.5279 ns 0.62 0.00 0.2764 - 949 B
Chiron_Old error 1,174.1918 ns 4.2226 ns 1.00 0.00 0.5435 - 1.86 kB
Chiron_New fparsec 3,519.4741 ns 21.3731 ns 0.56 0.00 0.5147 - 2.23 kB
Chiron_Old fparsec 6,327.1602 ns 34.3687 ns 1.00 0.00 2.1891 - 8.1 kB
Chiron_New prettyuser 7,513.2868 ns 85.5356 ns 0.53 0.01 0.9603 - 4.24 kB
Chiron_Old prettyuser 14,257.2669 ns 60.9807 ns 1.00 0.00 4.1951 - 15.7 kB
Chiron_New social 17,340,841.3744 ns 453,392.4016 ns 0.62 0.02 383.3333 - 9.6 MB
Chiron_Old social 28,075,461.0672 ns 162,895.2767 ns 1.00 0.00 4245.8333 479.1667 33.53 MB
Chiron_New user 7,464.4183 ns 32.8148 ns 0.53 0.00 0.9928 - 4.24 kB
Chiron_Old user 14,202.8109 ns 47.7378 ns 1.00 0.00 4.2277 - 15.7 kB

When encoding:

Method Mean StdDev Scaled Scaled-StdDev Gen 0 Allocated
Inline_Explicit 120.0114 ns 2.4677 ns 0.06 0.00 0.0876 295 B
InModule_Explicit 119.0491 ns 0.7453 ns 0.06 0.00 0.0877 295 B
Inline_Inferred 114.5410 ns 0.6669 ns 0.06 0.00 0.0798 271 B
InModule_Inferred 112.0574 ns 0.8763 ns 0.05 0.00 0.0801 271 B
Version6_ComputationExpression 2,051.8482 ns 23.7067 ns 1.00 0.00 0.8143 2.86 kB
Version6_Operators 2,102.9870 ns 8.4327 ns 1.03 0.01 0.9384 3.25 kB

And when decoding:

Method Mean StdDev Scaled Scaled-StdDev Gen 0 Allocated
Inline_Explicit_ComputationExpression 425.6357 ns 16.5782 ns 0.45 0.02 0.1993 702 B
InModule_Explicit_ComputationExpression 397.6754 ns 3.0964 ns 0.42 0.01 0.1759 630 B
Inline_Inferred_ComputationExpression 1,138.4921 ns 6.4098 ns 1.21 0.02 0.2106 822 B
InModule_Inferred_ComputationExpression 1,115.1637 ns 8.1718 ns 1.18 0.02 0.1897 750 B
Inline_Explicit_Operators 356.4605 ns 3.0266 ns 0.38 0.01 0.1757 590 B
InModule_Explicit_Operators 328.4038 ns 1.0029 ns 0.35 0.00 0.1291 447 B
Inline_Inferred_Operators 1,094.8538 ns 5.3998 ns 1.16 0.01 0.1849 734 B
InModule_Inferred_Operators 1,038.3364 ns 7.5264 ns 1.10 0.01 0.1076 495 B
Version6_ComputationExpression 942.9645 ns 10.9486 ns 1.00 0.00 0.9374 3.1 kB
Version6_Operators 950.2550 ns 5.0865 ns 1.01 0.01 0.9811 3.24 kB

I have also tested the new version by implementing a JSON schema parser, and then verifying the amount of time required for each stage: parse, decode, encode, format, parse and decode, encode and format, and a full round-trip. The sample target used was the Swagger 2.0 JSON schema, measuring in at about 39 kiB.

Method Mean StdDev Gen 0 Gen 1 Allocated
Parse 1,117.3922 us 6.4157 us 28.9063 - 397.4 kB
Decode 1,999.4045 us 14.0837 us 223.9583 20.8333 1.09 MB
ParseAndDecode 3,232.3001 us 49.3304 us 103.1250 - 1.48 MB
Format 216.1864 us 0.8882 us 26.2370 - 115.24 kB
Encode 379.0652 us 1.5211 us 89.7135 14.5182 360.3 kB
EncodeAndFormat 567.4594 us 3.1673 us 127.2135 - 463.12 kB
RoundTrip 3,908.8632 us 78.9472 us 228.1250 - 1.95 MB
neoeinstein commented 7 years ago

An alpha with these changes has been posted to NuGet as "7.0.0-alpha-170410"

dangets commented 7 years ago

I realize that this is an alpha release, but was wondering if you could help me w/ an issue when I attempt to use this:

Binding session to 'c:\Users\dannygeorge\code\fsharp\extendedevents\ExtendedEventsLib\../build\FParsec.dll'...
System.TypeInitializationException: The type initializer for '<StartupCode$Chiron>.$Chiron' threw an exception. ---> System.IO.FileLoadException: Could not loa
d file or assembly 'FParsec, Version=1.0.0.0, Culture=neutral, PublicKeyToken=40ccfc0a09edbb5d' or one of its dependencies. A strongly-named assembly is requir
ed. (Exception from HRESULT: 0x80131044) ---> System.IO.FileLoadException: A strongly-named assembly is required. (Exception from HRESULT: 0x80131044)
   --- End of inner exception stack trace ---
   at <StartupCode$Chiron>.$Chiron..cctor()
   --- End of inner exception stack trace ---
   at Chiron.JsonObjectModule.get_empty()
   at FSI_0002.encoder(XEvent x) in c:\Users\dannygeorge\code\fsharp\extendedevents\ExtendedEventsLib\Script2.fsx:line 98
   at <StartupCode$FSI_0002>.$FSI_0002.main@() in c:\Users\dannygeorge\code\fsharp\extendedevents\ExtendedEventsLib\Script2.fsx:line 112

The versions that paket is using are: Chiron 7.0.0-alpha-170410 and FParsec 1.0.3-alpha-170404

aggieben commented 7 years ago

@neoeinstein I'm reading through the tests now trying to grasp how to actually use the thing now. Any chance you've got something written about it?

thinkbeforecoding commented 7 years ago

This looks quite promising, but is there any plan to move forward ? Or is it currently blocked by some issue ?

neoeinstein commented 7 years ago

Main thing is to get FParsec onto a stable release. Once that is done, I will push the new Chiron as a stable release. If you have any suggestions for changes or improvements to the API, please let me know.

czifro commented 7 years ago

I'm working on a converting the freya example to dotnet core as a learning exercise. So I am using the alpha version of Chiron and am using Aether (v8.2). It seems that the operators API clash: Chiron and Aether.

The example code has this:

let todo_ id =
      todos_
  >-> Map.value_ id

which is fine for pre-netCore. however, for netCore workaround:

let todo_ id =
       todos_
  >--> Map.value_ id

which the operator >--> is marked as obsolete. I'm not sure how common it is to use both Aether and Chiron because I'm just starting, but if it is quite common, this clash might be a little annoying and if Aether drops that operator, projects would break upon upgrade. I just thought I'd point it out since it was something I noticed.

sjalowoi commented 7 years ago

Hi all,

I currently use Chiron, dotnet core alpha version, in one of our projects. I encounter what it seems to me to be a bug. I have the following JSON with two optional fields like this:

{"id":"0e3563cb-FFFB-488f-8b99-ea06cd17ef98", "companyName": "AGILITIC SAS", "civility":"mr", "lastname":"JALOWOI", "firstname":"Sylvain", "phone":"02334345654", "mobile_phone":"", "email":"sja@gm.com", "notes":"", "billing_address":{ "address":"6 rue des peupliers", "city":"La combe-sur-mer", "zipcode":"63150"}, "intervention_address": { "address":"6 rue des peupliers", "city":"La combe-sur-mer", "zipcode":"63150"}}

Actually, one field is missing, "owner", the second optional field was "CompanyName".

On the backend server side (Suave), I have the following code to interpret the JSON:

` type CommonClientInformationForm =
{ id : ClientId civility : string lastname : string firstname : string phone : string mobilePhone : string email : string notes : string billingAddr : Address interventionAddr : Address }

type CreateClientForm = 
    | RegularClientForm of bool * CommonClientInformationForm
    | CompanyClientForm of string * CommonClientInformationForm
    | InvalidClientForm

    static member FromJson (_ : CreateClientForm) = jsonDecoder {
                let! cid = DI.required "id"
                let! e   = DI.required "email"
                let! f   = DI.required "firstname"
                let! l   = DI.required "lastname"
                let! c   = DI.required "civility"
                let! pn  = DI.required "phone"
                let! mpn = DI.required "mobile_phone"
                let! n   = DI.required "notes"
                let! baddr = DI.required "billing_address"
                let! iaddr = DI.required "intervention_address"

                let! cname = DI.optional "companyName"
                let! owner = DI.optional "owner"

                let commonInfo = {  id          = cid
                                    civility    = c
                                    lastname    = l
                                    firstname   = f
                                    phone       = pn
                                    mobilePhone = mpn
                                    email       = e
                                    notes       = n
                                    billingAddr      = baddr
                                    interventionAddr = iaddr
                                }

                let res = match cname with
                            | None ->
                                   match owner with
                                        | None -> InvalidClientForm
                                        | Some o ->  RegularClientForm(o, commonInfo)
                            | Some compName -> CompanyClientForm(compName, commonInfo)
                return res
    }`

At runtime, if there is one missing of the two optional fields, the program stops with a StackOverflow exception. If I remove the code dealing with the two optional fields, the code runs correctly. The behavior I expect when a field is missing in a JSON is that the D.optional "key" function returns None, not an exception. Am I wrong ?

Thanks for the code, the new version is very nice to use

neoeinstein commented 6 years ago

StackOverflow is a very particular exception. Do you have a stack trace? Generally a stack overflow indicates some recursion or deep call stack, so knowing what that call stack was would be helpful in understanding the issue you are observing.

Better yet, if you can create a PR against the chiron-7 branch with an added test that fails, I can work to make that test pass.

sjalowoi commented 6 years ago

Hi Marcus,

thanks for you answer. I cannot reproduce the StackTrace, i think it is a problem into my code. Sorry for the disturbing. However i encounter a problem with using of Optional.

Actually, i have a Json like this :

{ "action": { "interventionCreated": { "clientId": "340078fc-e585-402d-b2e1-96df3b2a2f03", "interventionStatus": { "value": "toschedule" }, "userId": "0e45fedd-2aaf-41c4-b7ea-3554ecebf145", "interventionDeadline": null, "interventionType": { "value": "maintenance" }, "interventionId": "ba56b826-2e7e-49f6-aea4-24019bd5f8f0", "interventionDescr": " " } }, "version": "1.0" }

A field called interventionDeadline has a Null value. So i suppose i need to use DI.optional "interventionDeadline" for getting the field and i will get a None value.

Instead of None value, i get the error message:

Unhandled Exception: System.Exception: Impossible de deserialiser un événement: Found 9 errors: (Choice #1): Failed to find expected property: email (Choice #2): Failed to find expected property: login (Choice #3): Failed to find expected property: userAdded (Choice #4): Failed to find expected property: regularClient (Choice #5): Failed to find expected property: companyClient (Choice #6).interventionCreated.interventionDeadline: Expected to find a string, but instead found null (Choice #7): Failed to find expected property: appointmentAdded (Choice #8): Failed to find expected property: appointmentCancelled (Choice #9): Failed to find expected property: appointmentCompleted du stream: account:041c49f0-eb59-4982-a602-0046d72a6564 - Numéro d'event: 4

How can i read a field like "interventionDeadline" with Chiron 7 that is supposed to be a String field with eventually a Null value ? Thanks for your time, Sylvain

PS: I am in vacation, it is not easy for me to do a PullRequest.

neoeinstein commented 6 years ago

For that line, you can use D.required (D.optionWith D.string) "interventionDeadline". That will require that "interventionDeadline" exists and is either null or a string. If you want "interventionDeadline" to be optional and also handle null, then use D.optional (D.optionWith D.string) "interventionDeadline".

Part of the API change was to ensure that all three of the optional cases were covered (accept missing, accept null, accept null or missing).

sjalowoi commented 6 years ago

Thanks for your answer, it resolve my problems and it is more clear for me. Best regards Sylvain

lanarchyste commented 6 years ago

Hello,

I have the same problem, but when I use the syntax: D.optional (D.option D.string) "value"

I get the following error: error FS0001: This expression was expected to have type "Decoder<Json,'a>" but here has type "JsonResult"

I have the same error using the other proposed syntax: D.required (D.option D.string) "value"

In this context, D = Chiron.Serialization.Json.Decode

Do you confirm that this is the correct syntax?

Best regards. Julien.

Risord commented 6 years ago

Decoding objects can be done with a new computation expression: jsonReader or by using the provided operators.

Is there any example about decoding with operators?

neoeinstein commented 6 years ago

@lanarchyste Having a fuller example, including the type you are attempting to deserialize would help me to see if there is anything off in the syntax. I went looking for a test that uses D.optional (D.option _) and didn't see one, so I will get one added to ensure it is working properly.

@Risord, Take a look at the tests in the PR here: https://github.com/neoeinstein/chiron/blob/chiron-7/tests/Chiron.Tests/Chiron.Tests.fs#L38.

neoeinstein commented 6 years ago

@lanarchyste Well, after just having responded to Risord, that same section of code gives the answer. It's not D.optional (D.option _), it's D.optional (D.optionWith _). I'll correct my earlier comments.

jonathanfishbein1 commented 6 years ago

Hello, I am attempting to update and I am having more success than my last attempt a few months ago. I have the following type

type CommentDatabaseModel =
  { 
    commentId : string
    userId : string
    message : string
  }

and CommentMultiTreeDatabaseModel = 
  CommentDatabaseModelNode of CommentDatabaseModel * CommentMultiTreeDatabaseModel list

and the following decoders

let commentDatabaseModelDecode =
  let inner = 
    (fun 
      commentId 
      userId 
      message -> 
      { commentId = commentId
        userId = userId
        message = message
      })
    <!> required "commentId"
    <*> required "userId"
    <*> required "message"
  Json.Decode.jsonObject >=> inner

let rec commentMultiTreeDatabaseModelDecode =
  let inner = 
    (fun 
      commentDatabaseModel
      forest ->
        CommentDatabaseModelNode (commentDatabaseModel, forest)
    )
    <!> D.required commentDatabaseModelDecode "commentDatabaseModel"
    <*> D.required (D.listWith (D.lazily commentMultiTreeDatabaseModelDecode)) "forest"
  Json.Decode.jsonObject >=> inner

I am attempting to copy Elm decoder

commentTreeDecoder : Decoder (Tree CommentModel)
commentTreeDecoder =
    decode MultiwayTree.Tree
        |> required "commentModel" commentModelDecoder
        |> required "forest" (list (lazy (\_ -> commentTreeDecoder)))

What am I missing? Any help or advice is much appreciated. Thank you

UPDATE

Got it! Use D.delay

Risord commented 6 years ago

I found a bug. Chiron seems to encode decimals with ',' instead of '.'

Chiron.Inference.Json.encode 1.5
|> Chiron.Formatting.Json.format // returns "1,5" expected: "1.5"

Windows 10 with culture fi-FI

Risord commented 6 years ago

I think I cannot create pull request for this because development doesn't exists in any traditional github branch(?). Anyway you should be able to reproduce issue by setting: CultureInfo.CurrentCulture <- new CultureInfo("fi-FI")

I am currently watching newest commit of this pull request (0b1a12c) and I suggest that all primitive .ToString([potential-format]) calls should be changed to: .ToString([potential-format], CultureInfo.InvariantCulture) (Chiron.fs: 996 - 1046)

Culture effects to parsing too so CultureInfo.InvariantCulture must be passed to .Parse functions too. (Chiron.fs: 1527 - 1572) For example this code fails with fi-FI culture:

let number : float = 
    Chiron.Inference.Json.deserialize "1.5"
    |> function
        | JPass n -> n
        | _ -> failwith "No can do"

For now workaround is to change current culture at program start: CultureInfo.CurrentCulture <- CultureInfo.InvariantCulture

neoeinstein commented 6 years ago

@Risord Thanks for the report. I'll make sure to explicitly set the invariant culture in each of these spots. The PR is coming from my own fork of the repo at the moment, so that's likely what is making it difficult to see where to make a PR.

neoeinstein commented 6 years ago

Ok. A new Chiron 7 alpha has been released. The underlying library (FParsec) is now on a stable release, so I am about ready to release Chiron 7 as a stable release. Let me know if there are any additional things that should be tweaked in the API. Thanks for all who have been trialing the new API.

lust4life commented 6 years ago

@neoeinstein https://www.nuget.org/packages/Chiron/ seems no new version has been released ?

MaxDeg commented 6 years ago

Hello,

I'm working with the library to deserialize json but I have an issue.

Considering the following types:

type Info =
  { title           : string
    description     : string option
    termsOfService  : string option
    contact         : Contact option
    license         : License option
    version         : string }

and Contact =
  { name  : string option
    url   : string option
    email : string option }

and License =
  { name  : string
    url   : string option }

If I create FromJson methods using the builder jsonDecoder it compile and the type of the methods is 'a -> Decoder<Json, 'a>.

But if I use the operators to define FromJson methods it doesn't compile and I get the following error: 'Type mismatch. Expecting a 'Decoder<Json,Contact>'
but given a 'Decoder<JsonObject,Contact>'
The type 'Json' does not match the type 'JsonObject''

What's the difference between Json and JsonObject ? I don't get why when using operators signature of FromJson is 'a -> Decoder<JsonObject, 'a> and when I use the builder it's 'a -> Decoder<Json, 'a>. Did I miss something? Thanks

open Chiron
open Chiron.Inference

type Contact with
  static member FromJson (_ : Contact) =
    jsonDecoder {
      let! name = Json.Decode.optional "name"
      let! url = Json.Decode.optional "url"
      let! email = Json.Decode.optional "email"

      return  { name  = name
                url   = url
                email = email }
    }

type License with
  static member FromJson (_ : License) =
    jsonDecoder {
      let! name = Json.Decode.required "name"
      let! url = Json.Decode.optional "url"

      return { name  = name
               url   = url }
    }

type Info with
  static member FromJson (_ : Info) =
    jsonDecoder {
      let! title = Json.Decode.required "title"
      let! desc = Json.Decode.optional "description"
      let! tos = Json.Decode.optional "termsOfService"
      let! contact = Json.Decode.optional "contact"
      let! license = Json.Decode.optional "license"
      let! version = Json.Decode.required "version"

      return { title           = title
               description     = desc
               termsOfService  = tos
               contact         = contact
               license         = license
               version         = version }
    }
type Contact with
  static member FromJson (_ : Contact) =
        fun name url email ->
          { name  = name
            url   = url
            email = email }
    <!> Json.Decode.optional "name"
    <*> Json.Decode.optional "url"
    <*> Json.Decode.optional "email"

type License with
  static member FromJson (_ : License) =
        fun name url ->
          { name  = name
            url   = url }
    <!> Json.Decode.required "name"
    <*> Json.Decode.optional "url"

type Info with
  static member FromJson (_ : Info) =
        fun title desc tos contact license version ->
          { title           = title
            description     = desc
            termsOfService  = tos
            contact         = contact
            license         = license
            version         = version }
    <!> Json.Decode.required "title"
    <*> Json.Decode.optional "description"
    <*> Json.Decode.optional "termsOfService"
    <*> Json.Decode.optional "contact"
    <*> Json.Decode.optional "license"
    <*> Json.Decode.required "version"
neoeinstein commented 6 years ago

Apologies to all. I had published the new version to MyGet. I've corrected that error and have now published a beta version—making sure that I push to NuGet this time. Please review and let me know if there are any final changes that we should make to the API.

neoeinstein commented 6 years ago

Ok, I've pushed up a new version which contains one more feature that I really wanted. Dealing with recursive structures was a pain with the new encoder/decoders. You would need to write your own delay functions, etc.

Now, you can use a method very similar to FParsec, wherein you request a ref from Chiron. You get back a ref and a usable (en/de)coder. Then, you can use a static initializer (a module do block) to lazily initialize the ref.

I've added tests for the new functionality. I want to modify the Schema Def example to use the new mechanism.

Still to consider: would aggressive inlining hints benefit performance here?

There will be some minor clean up, dead code comment deletion, and I will try to get some source documentation added. After that, this new version should be ready to go stable.

neoeinstein commented 6 years ago

@MaxDeg A Json is a discriminated union of all the possible types JSON can be, that is, a number, a string, a boolean, a null, an array, an object. A JsonObject is the type used to hold the properties of an object. If you have a Json, you may be able to decode it to a JsonObject (if the JSON actually is an object). If you have a JsonObject, you can always turn it into a Json by wrapping it in the Json.Object case.

In your case you may want to use Decode.jsonObject >=> jsonDecoder { … }. That will try to decode the Json as a JsonObject before passing it along to your decoder.

This difference is important as it allows you to create mixins, where you can decode several types out of the same object without needing to unwrap the Json for each mixin. You do it once with Decode.jsonObject, and then you can use your Decoder<JsonObject,'a> to extract your 'a from there.

nojaf commented 6 years ago

@neoeinstein I've made a little script to try out the new beta.

#r "./packages/json/FParsec/lib/net40-client/FParsec.dll"
#r "./packages/json/FParsec/lib/net40-client/FParsecCS.dll"
#r "./packages/json/Chiron/lib/net45/Chiron.dll"

open Chiron

module E = Json.Encode

type Person = {
    Name:string
    Age:int
    City:string
} 

type Person with
  static member ToJson (x:Person) (jObj) = 
    jObj
    |> E.required E.string "name" x.Name
    |> E.required E.int "age" x.Age
    |> E.required E.string "city" x.City

{ Name = "Barry"; Age = 22; City = "New York"}
|> E.jsonObjectWith Person.ToJson
|> Json.formatWith JsonFormattingOptions.Pretty

Is this the correct approach to serialize?

wallymathieu commented 5 years ago

I've heard that people are using chiron 7 in production. Perhaps it's time to merge this pull request? Ir there anything left on it?

ninjarobot commented 5 years ago

@neoeinstein is there anything I can do to help move this forward?

neoeinstein commented 5 years ago

My congratulations to Dave (@ninjarobot). I've given you publish rights to the NuGet feed as well as admin collaborator rights on this repository. I'm happy to help out from time to time, but I think that Chiron 7 is in a pretty good state. Dave, feel free to pull it out of alpha and release it to the world as stable.

wallymathieu commented 5 years ago

I hope we see additional improvement of this library! Good luck on maintaining it @ninjarobot ! 😀

robkuz commented 4 years ago

I am just moving to the latest available version and I have some questions about performance or rather about the terminology used in the description above.

What are the differences between
Inline_Explicit_ComputationExpression and Inline_Inferred_ComputationExpression
or Inline_Inferred_ComputationExpression and Inline_Inferred_Operators . Are there any code examples for this available? My biggest isse with Chiron is indeed performance atm. I have large object graphs that I need to deserialize (and many of those in one go) and it is not uncommon for a end user initiated action to take 5-10 seconds or even 20s. which is much to much. So I need to get the most out of Chiron. Thx

et1975 commented 3 years ago

Would it be possible to have an upgrade path from 6 -> 7 that doesn't break the user's code and let one easy into 7 gently?