Skip to content

Instantly share code, notes, and snippets.

@mrange
Last active May 22, 2018 09:01
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mrange/18ca0863c45a3c00a670afb09379d4c1 to your computer and use it in GitHub Desktop.
Save mrange/18ca0863c45a3c00a670afb09379d4c1 to your computer and use it in GitHub Desktop.
module AnyTransformer =
type [<AbstractClass>] BadCause () =
class
abstract Describe : string
override x.ToString () = x.Describe
end
type [<Sealed>] MessageBadCause (msg: string) =
class
inherit BadCause ()
let describe = sprintf "Message: %s" msg
override x.Describe = describe
end
type [<Sealed>] NoMemberBadCause (name: string) =
class
inherit BadCause ()
let describe = sprintf "NoMember: %s" name
override x.Describe = describe
end
type [<Sealed>] OutOfRangeBadCause (index: int) =
class
inherit BadCause ()
let describe = sprintf "OutOfRange: %d" index
override x.Describe = describe
end
type [<Sealed>] NotAssociativeBadCause () =
class
inherit BadCause ()
let describe = sprintf "NotAssociative"
override x.Describe = describe
end
type [<Sealed>] NotIndexableBadCause () =
class
inherit BadCause ()
let describe = sprintf "NotIndexable"
override x.Describe = describe
end
type [<Sealed>] ExceptionBadCause (exc : exn) =
class
inherit BadCause ()
let describe = sprintf "Exception: %s" exc.Message
override x.Describe = describe
end
type [<Sealed>] AnyBadCause (any : obj) =
class
inherit BadCause ()
let describe = sprintf "Any: %A" any
override x.Describe = describe
end
[<RequireQualifiedAccess>]
type PathPart =
| Member of string
| Index of int
| Named of string
type Path = PathPart list
[<RequireQualifiedAccess>]
type BadTree =
| Empty
| Leaf of Path*BadCause
| Fork of BadTree*BadTree
| Suppress of BadTree
type TransformContext =
{
Lookup : obj -> string -> Result<obj, BadCause>
Index : obj -> int -> Result<obj, BadCause>
Indexer : obj -> Result<int*(int -> obj), BadCause>
}
static member Default : TransformContext =
let lookup (any : obj) name : Result<obj, BadCause> =
match any with
| :? System.Collections.IDictionary as dic ->
if dic.Contains name then
Ok (dic.[name])
else
Error (upcast NoMemberBadCause name)
| _ ->
Error (upcast NotAssociativeBadCause ())
let index (any : obj) idx : Result<obj, BadCause> =
if idx < 0 then
Error (upcast OutOfRangeBadCause idx)
else
match any with
| :? System.Collections.IList as lis ->
if idx < lis.Count then
Ok (lis.[idx])
else
Error (upcast OutOfRangeBadCause idx)
| :? System.Collections.IEnumerable as enu ->
let e = enu.GetEnumerator ()
let rec loop (e : System.Collections.IEnumerator) i : Result<obj, BadCause> =
if e.MoveNext () then
if i > 0 then
loop e (i - 1)
else
Ok e.Current
else
Error (upcast OutOfRangeBadCause idx)
loop e idx
| _ ->
Error (upcast NotIndexableBadCause ())
let indexer (any : obj) : Result<int*(int -> obj), BadCause> =
match any with
| :? System.Collections.IList as lis ->
let ra = ResizeArray lis.Count
for i = 0 to (lis.Count - 1) do
ra.Add (lis.[i])
Ok (lis.Count, fun idx -> lis.[idx])
| :? System.Collections.IEnumerable as enu ->
let ra = ResizeArray 16
let e = enu.GetEnumerator ()
while e.MoveNext () do
ra.Add e.Current
Ok (ra.Count, fun idx -> ra.[idx])
| _ ->
Error (upcast NotIndexableBadCause ())
{
Lookup = lookup
Index = index
Indexer = indexer
}
type [<Struct>] TransformResult<'T> = TR of 'T*BadTree
type [<Struct>] Transform<'T> = T of (TransformContext -> Path -> obj -> TransformResult<'T>)
[<RequireQualifiedAccess>]
type TransformedValue<'T> =
| IsGood of 'T
| WithWarnings of 'T*struct (string*BadCause) []
| WithErrors of 'T*struct (string*BadCause) []*struct (string*BadCause) []
module Transform =
open FSharp.Core.Printf
open System
module Details =
open System.Text
let defaultSize = 16
let inline adapt (T t) = OptimizedClosures.FSharpFunc<_, _, _, _>.Adapt t
let inline invoke (f : OptimizedClosures.FSharpFunc<_, _, _, _>) a b c = f.Invoke (a, b, c)
let inline btjoin l r =
match (l, r) with
| BadTree.Empty , _ -> r
| _ , BadTree.Empty -> l
| BadTree.Suppress l, BadTree.Suppress r -> BadTree.Suppress (BadTree.Fork (l, r))
| _ , _ -> BadTree.Fork (l, r)
let inline btisgood t =
match t with
| BadTree.Empty
| BadTree.Suppress _ -> true
| _ -> false
let inline btsuppress t =
match t with
| BadTree.Empty
| BadTree.Suppress _ -> t
| _ -> BadTree.Suppress t
let inline btleaf ctx bc = BadTree.Leaf (ctx, bc)
let inline tresult v bt = TR (v, bt)
let inline one () = LanguagePrimitives.GenericOne<'T>
let inline zero () = LanguagePrimitives.GenericZero<'T>
module Loops =
let rec bcpath (sb : StringBuilder) bc =
let inline app t = sb.Append (t : string) |> ignore
match bc with
| [] -> app "root"
| h::t ->
bcpath sb t
match h with
| PathPart.Member m -> app (sprintf ".%s" m)
| PathPart.Index i -> app (sprintf ".[%d]" i)
| PathPart.Named n -> app (sprintf "(%s)" n)
let rec btcollapse (es : ResizeArray<_>) (ws : ResizeArray<_>) s t =
match t with
| BadTree.Empty -> ()
| BadTree.Leaf (cs, bc) ->
let sb = StringBuilder defaultSize
bcpath sb cs
let ra = if s then ws else es
ra.Add <| struct (sb.ToString (), bc)
| BadTree.Fork (l , r) -> btcollapse es ws s l; btcollapse es ws s r
| BadTree.Suppress t -> btcollapse es ws true t
let rec tchoose bv (cs : _ []) ctx path any cbc i =
if i < cs.Length then
let s, t = cs.[i]
let (TR (_, sbc)) = invoke s ctx path any
if btisgood sbc then
invoke t ctx path any
else
tchoose bv cs ctx path any (btjoin cbc sbc) (i + 1)
else
tresult bv cbc
let rec tmany t ctx path indexer c (ra : ResizeArray<_>) cbc i =
if i < c then
let nany = indexer i
let npath = (PathPart.Index i)::path
let (TR (tv, tbc)) = invoke t ctx npath nany
ra.Add tv
tmany t ctx path indexer c ra (btjoin cbc tbc) (i + 1)
else
cbc
open Details
open System.Globalization
let inline tgood v = tresult v BadTree.Empty
let inline tleaf ctx bc = btleaf ctx bc
let inline treturn v =
T <| fun _ _ _ ->
tgood v
let inline tbad bv bc =
T <| fun _ path _ ->
tresult bv (tleaf path bc)
let inline tfailwith bv msg = tbad bv (MessageBadCause msg)
let inline tfailwithf bv fmt = kprintf (tfailwith) fmt
let inline tfailwithz msg = tfailwith (zero ()) msg
let inline tfailwithzf fmt = kprintf tfailwithz fmt
let inline tone () = treturn (one ())
let inline tzero () = treturn (zero ())
// Combinators
let inline tbind t uf =
let t = adapt t
T <| fun ctx path any ->
let (TR (tv, tbc)) = invoke t ctx path any
let u = uf tv
let u = adapt u
let (TR (uv, ubc)) = invoke u ctx path any
tresult uv (btjoin tbc ubc)
// Applicative apply
let inline tapply f t =
let f = adapt f
let t = adapt t
T <| fun ctx path any ->
let (TR (fv, fbc)) = invoke f ctx path any
let (TR (tv, tbc)) = invoke t ctx path any
tresult (fv tv) (btjoin fbc tbc)
// Functor map
let inline tmap m t =
let t = adapt t
T <| fun ctx path any ->
let (TR (tv, tbc)) = invoke t ctx path any
tresult (m tv) tbc
// Combinators
let inline tand l r =
let l = adapt l
let r = adapt r
T <| fun ctx path any ->
let (TR (lv, lbc)) = invoke l ctx path any
let (TR (rv, rbc)) = invoke r ctx path any
tresult (lv, rv) (btjoin lbc rbc)
let inline tor l r =
let l = adapt l
let r = adapt r
T <| fun ctx path any ->
let (TR (lv, lbc)) = invoke l ctx path any
let (TR (rv, rbc)) = invoke r ctx path any
if btisgood lbc then
tresult lv (btjoin lbc (btsuppress rbc))
else
let (TR (rv, rbc)) = invoke r ctx path any
if btisgood rbc then
tresult rv (btjoin (btsuppress lbc) rbc)
else
tresult rv (btjoin lbc rbc)
let inline torElse l r =
let l = adapt l
let r = adapt r
T <| fun ctx path any ->
let (TR (lv, lbc)) = invoke l ctx path any
if btisgood lbc then
tresult lv lbc
else
let (TR (rv, rbc)) = invoke r ctx path any
if btisgood rbc then
tresult rv (btjoin (btsuppress lbc) rbc)
else
tresult rv (btjoin lbc rbc)
let inline tcheck c bv t =
let c = adapt c
let t = adapt t
T <| fun ctx path any ->
let (TR (_, cbc)) = invoke c ctx path any
if btisgood cbc then
let (TR (tv, tbc)) = invoke t ctx path any
tresult tv (btjoin cbc tbc)
else
tresult bv cbc
let inline tcheckz c t = tcheck c (zero ()) t
let inline tchoose bv cs =
let cs = Array.map (fun (s, t) -> adapt s, adapt t) cs
T <| fun ctx path any ->
Loops.tchoose bv cs ctx path any BadTree.Empty 0
let inline tchoosez cs = tchoose (zero ()) cs
let inline tjoin t =
let t = adapt t
T <| fun ctx path any ->
let (TR (tv, tbc)) = invoke t ctx path any
let tv = adapt tv
let (TR (ttv, ubc))= invoke tv ctx path any
tresult ttv (btjoin tbc ubc)
let inline topt t =
let t = adapt t
T <| fun ctx path any ->
let (TR (tv, tbc)) as tr = invoke t ctx path any
if btisgood tbc then
tresult (Some tv) tbc
else
tresult None (BadTree.Suppress tbc)
let inline tsuppress t =
let t = adapt t
T <| fun ctx path any ->
let (TR (tv, tbc)) as tr = invoke t ctx path any
if btisgood tbc then
tr
else
tresult tv (BadTree.Suppress tbc)
let inline tverify (v : 'T -> #BadCause option) t =
let t = adapt t
T <| fun ctx path any ->
let (TR (tv, tbc)) as tr = invoke t ctx path any
match v tv with
| Some bc ->
let bc = bc |> btleaf path
tresult tv (btjoin tbc bc)
| None ->
tr
// Extractors
let tisNull =
T <| fun ctx path any ->
let tv = isNull any
tgood tv
let inline tasDateTime (formats : string []) =
T <| fun ctx path any ->
match any with
| null -> tgood DateTime.MinValue
| :? DateTime as tdt -> tgood tdt
| _ ->
let ts = any.ToString ()
let tb, tdt = DateTime.TryParseExact (ts, formats, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal)
if tb then
tgood tdt
else
tresult DateTime.MinValue (MessageBadCause "Can't interpret value as as a datetime" |> btleaf path)
let tasInt =
T <| fun ctx path any ->
match any with
| null -> tgood 0
| :? int as ti -> tgood ti
| _ ->
let ts = any.ToString ()
let tb, ti = Int32.TryParse ts
if tb then
tgood ti
else
tresult 0 (MessageBadCause "Can't interpret value as a number" |> btleaf path)
let tasString =
T <| fun ctx path any ->
let tv = if isNull any then "" else any.ToString ()
tgood tv
// Navigators
let inline tindex idx bv t =
let t = adapt t
T <| fun ctx path any ->
match ctx.Index any idx with
| Ok nany ->
let npath = (PathPart.Index idx)::path
invoke t ctx npath nany
| Error bc ->
tresult bv (bc |> btleaf path)
let inline tindexz idx t = tindex idx (zero ()) t
let inline tmany t =
let t = adapt t
T <| fun ctx path any ->
match ctx.Indexer any with
| Ok (c, indexer) ->
let ra = ResizeArray 16
let lbc = Loops.tmany t ctx path indexer c ra BadTree.Empty 0
tresult (ra.ToArray ()) lbc
| Error bc ->
tresult [||] (bc |> btleaf path)
let inline tmember name bv t =
let t = adapt t
T <| fun ctx path any ->
match ctx.Lookup any name with
| Ok nany ->
let npath = (PathPart.Member name)::path
invoke t ctx npath nany
| Error bc ->
tresult bv (bc |> btleaf path)
let inline tmemberz name t = tmember name (zero ()) t
// Misc
let inline tdebug nm t =
let t = adapt t
T <| fun ctx path any ->
printfn "BEFORE - %s - %A - %A" nm path any
let tr = invoke t ctx path any
printfn "AFTER - %s - %A - %A - %A" nm path any tr
tr
let trun ctx any t =
let t = adapt t
let (TR (tv, tbc)) = invoke t ctx [] any
let es = ResizeArray defaultSize
let ws = ResizeArray defaultSize
Loops.btcollapse es ws false tbc
if es.Count = 0 && ws.Count = 0 then
TransformedValue.IsGood tv
elif es.Count = 0 then
TransformedValue.WithWarnings (tv, ws.ToArray ())
else
TransformedValue.WithErrors (tv, es.ToArray (), ws.ToArray ())
type Builder () =
member inline x.Bind (t, uf) = tbind t uf
member inline x.Return v = treturn v
member inline x.ReturnFrom t = t : Transform<_>
member inline x.Zero () = tzero
type Transform<'T> with
static member inline (>>=) (t, uf) = Transform.tbind t uf
static member inline (<*>) (f, t) = Transform.tapply f t
static member inline (<&>) (l, r) = Transform.tand l r
static member inline (<|>) (l, r) = Transform.tor l r
static member inline (<||>) (l, r) = Transform.torElse l r
static member inline (|>>) (t, m) = Transform.tmap m t
let transform = Transform.Builder ()
open AnyTransformer
open Newtonsoft.Json.Linq
let jsonContext =
let ctx = TransformContext.Default
let lookup (any : obj) name : Result<obj, BadCause> =
match any with
| :? JObject as jobj ->
let b, v = jobj.TryGetValue name
if b then
Ok (upcast v)
else
Error (upcast NoMemberBadCause name)
| _ ->
ctx.Lookup any name
let index (any : obj) idx : Result<obj, BadCause> =
match any with
| :? JArray as jarr ->
if idx >= 0 && idx < jarr.Count then
Ok (upcast jarr.[idx])
else
Error (upcast OutOfRangeBadCause idx)
| _ ->
ctx.Index any idx
let indexer (any : obj) : Result<int*(int -> obj), BadCause> =
match any with
| :? JArray as jarr ->
Ok (jarr.Count, fun idx -> upcast jarr.[idx])
| _ ->
ctx.Indexer any
{ ctx with Lookup = lookup; Index = index; Indexer = indexer }
module Test =
open System
open System.Collections.Generic
open Newtonsoft.Json.Linq
type Customer =
{
Id : int
FirstName : string
LastName : string
}
static member New id firstName lastName = {Id = id; FirstName = firstName; LastName = lastName}
type OrderLine =
{
LineNo : int
Product : string
Quantity : int
Amount : int
}
static member New lineNo product quantity amount = {LineNo = lineNo; Product = product; Quantity = quantity; Amount = amount}
type Order =
{
Id : int
CustomerId : int
Amount : int
Currency : string
Lines : OrderLine []
}
static member New id customerId amount currency lines = {Id = id; CustomerId = customerId; Amount = amount; Currency = currency; Lines = lines}
[<RequireQualifiedAccess>]
type Request =
| NewCustomer of Customer
| NewOrder of Order
| Unknown
static member Zero = Unknown
type Envelope = Envelope of string*DateTime*Request
open AnyTransformer.Transform
let tintMember nm = tmemberz nm tasInt
let tstrMember nm = tmember nm "" tasString
let tdtMember nm = tmember nm DateTime.Now (tasDateTime [|"yyyy-MM-dd"|])
let tcustomer =
treturn Customer.New
<*> tintMember "id"
<*> tstrMember "firstName"
<*> tstrMember "lastName"
|>> Request.NewCustomer
let torderLine =
treturn OrderLine.New
<*> tintMember "lineNo"
<*> tstrMember "product"
<*> tintMember "quantity"
<*> tintMember "amount"
let torderLines = tmany torderLine
let torder =
treturn Order.New
<*> tintMember "id"
<*> tintMember "customerId"
<*> tintMember "amount"
<*> tstrMember "currency"
<*> tmember "lines" [||] torderLines
|>> Request.NewOrder
let trequest =
let req s t =
let tf = sprintf "@schema expected to be %s" s |> MessageBadCause |> Some
let f v= if v = s then None else tf
let tc = tmember "@schema" "" (tverify f tasString)
tc, tmemberz "request" t
[|
req "customer" tcustomer
req "order" torder
|] |> tchoosez
let tenvelope =
treturn (fun mid ts req -> Envelope (mid, ts, req))
<*> tstrMember "messageId"
<*> tdtMember "timestamp"
<*> trequest
let dict (kvs : _ []) =
let d = Dictionary<_, _> ()
for k, v in kvs do
d.[k] <- v
box d
let customer =
[|
"messageId" , box "uuid0001"
"timestamp" , box DateTime.Now
"@schema" , box "customer"
"request" ,
[|
"id" , box 1001
"firstName" , box "Bill"
"lastName" , box "Gates"
|] |> dict
|] |> dict
let order =
[|
"messageId" , box "uuid0001"
"timestamp" , box DateTime.Now
"@schema" , box "order"
"request" ,
[|
"id" , box 1001
"customerId" , box "1001"
"amount" , box 100
"currency" , box "SEK"
"lines" ,
[|
[|
"lineNo" , box 1
"product" , box "Fussball"
"quantity" , box 20
"amount" , box "100"
|] |> dict
[|
"lineNo" , box 2
"product" , box "Tamagochi"
"quantity" , box 10
"amount" , box 150
|] |> dict
|] |> box
|] |> dict
|] |> dict
let orderJson = """{
"messageId" : "uuid0001"
, "timestamp" : "2017-01-01"
, "@schema" : "order"
, "request" : {
"id" : 1001
, "customerId" : "1001"
, "amount" : 100
, "currency" : "SEK"
, "lines" : [
{
"lineNo" : 1
, "product" : "Fussball"
, "quantity" : 20
, "amount" : "100"
}
, {
"lineNo" : 2
, "product" : "Tamagochi"
, "quantity" : 10
, "amount" : 150
}
]
}
}
"""
let transformDictionaries () =
let ctx = TransformContext.Default
Transform.trun ctx customer tenvelope |> printfn "%A"
Transform.trun ctx order tenvelope |> printfn "%A"
let transformJson () =
let parsedOrder = Newtonsoft.Json.JsonConvert.DeserializeObject<JObject> orderJson
let ctx = TransformContext.Default
Transform.trun ctx parsedOrder tenvelope |> printfn "%A"
Transform.trun jsonContext parsedOrder tenvelope |> printfn "%A"
module Versioned =
open System
open System.Collections.Generic
open Newtonsoft.Json.Linq
open AnyTransformer
let json = """
[
{
"name" : "Cosmo Cramer"
, "password" : "12345"
, "socialNo" : "ABC-98765"
, "comment" : "Original version"
}
, {
"firstName" : "Jerry"
, "lastName" : "Seinfeld"
, "password" : "12345"
, "comment" : "Realized we should split first and last name"
}
, {
"@schema" : "Customer"
, "@version" : 1
, "firstName" : "George"
, "lastName" : "Constanza"
, "hash" : "==<<>>=="
, "socialNo" : "ABC-98765"
, "comment" : "Introduced major version"
}
, {
"@schema" : "Customer"
, "@version" : 2
, "id" : "ID_1001"
, "firstName" : "Elaine"
, "lastName" : "Benes"
, "birthDate" : "1974-01-01"
, "hash" : "==<<>>=="
, "socialNo" : "ABC-98765"
, "comment" : "Major version 2 added ID and birthdate"
}
, {
"@schema" : "Customer"
, "@version" : 3
, "id" : "ID_1001"
, "firstName" : "Elaine"
, "lastName" : "Benes"
, "birthDate" : "1974-01-01"
, "hash" : "==<<>>=="
, "socialNo" : "ABC-98765"
, "comment" : "Major version 3 just to test invalid versions"
}
]"""
type Customer =
{
Version : int
Id : string
Hash : string
FirstName : string
LastName : string
BirthDate : DateTime option
SocialNo : string option
}
static member New v i h f l b s : Customer =
{
Version = v
Id = i
Hash = h
FirstName = f
LastName = l
BirthDate = b
SocialNo = s
}
static member Zero = Customer.New 0 "" "" "" "" None None
open Transform
let tintMember nm = tmemberz nm tasInt
let tstrMember nm = tmember nm "" tasString
let tdtMember nm = tmember nm DateTime.Now (tasDateTime [|"yyyy-MM-dd"|])
module Approach1 =
let tnamePair =
let tfirstName = tstrMember "firstName"
let tlastName = tstrMember "lastName"
let tname = tstrMember "name"
(tfirstName <&> tlastName) <|> (tname |>> (fun fullName ->
match fullName.Split ' ' with
| [||] -> "" , ""
| [|n|] -> "" , n
| ns -> ns.[0] , ns.[1]))
let hashPwd s = s |> hash |> string
let tversion = tintMember "@version" <|> treturn 0
let tid = tstrMember "id" <|> (tnamePair |>> fun (fn, ln) -> sprintf "%s/%s" ln fn)
let thash = tstrMember "hash" <|> (tstrMember "password" |>> hashPwd)
let tfirstName = tnamePair |>> fst
let tlastName = tnamePair |>> snd
let tbirthDate = tdtMember "birthDate" |> topt
let tsocialNo = tstrMember "socialNo" |> topt
let tcustomer =
treturn Customer.New
<*> tversion
<*> tid
<*> thash
<*> tfirstName
<*> tlastName
<*> tbirthDate
<*> tsocialNo
let tcustomers = tmany tcustomer
module Approach2 =
let genId fn ln = sprintf "%s/%s" ln fn
let hashPwd s = s |> hash |> string
let tversion = tintMember "@version" <|> treturn 0
let tid_v1 = (tstrMember "firstName" <&> tstrMember "lastName") |>> fun (fn, ln) -> genId fn ln
let tid = tstrMember "id"
let tfirstName = tstrMember "firstName"
let tlastName = tstrMember "lastName"
let tname = tstrMember "name"
let thash = tstrMember "hash"
let tpassword = tstrMember "password" |>> hashPwd
let tbirthDate = tdtMember "birthDate" |> topt
let tsocialNo = tstrMember "socialNo" |> topt
let tcustomer_b v id fn ln =
treturn Customer.New
<*> treturn v // Version
<*> treturn id // Id
<*> tpassword // Hash
<*> treturn fn // FirstName
<*> treturn ln // LastName
<*> treturn None // BirthDate
<*> tsocialNo // SocialNo
// Transforms a beta1 customer
let tcustomer_b1 =
transform {
let! name = tname
let fn, ln=
match name.Split ' ' with
| [||] -> "" , ""
| [|n|] -> "" , n
| ns -> ns.[0] , ns.[1]
let id = genId fn ln
return! tcustomer_b -1 id fn ln
}
// Transforms a beta2 customer
let tcustomer_b2 =
treturn tcustomer_b
<*> treturn -2
<*> tid_v1
<*> tfirstName
<*> tlastName
|> tjoin
// Transforms a v1 customer
let tcustomer_v1 =
treturn Customer.New
<*> tversion // Version
<*> tid_v1 // Id
<*> thash // Hash
<*> tfirstName // FirstName
<*> tlastName // LastName
<*> treturn None // BirthDate
<*> tsocialNo // SocialNo
// Transforms a v2 customer
let tcustomer_v2 =
treturn Customer.New
<*> tversion // Version
<*> tid // Id
<*> thash // Hash
<*> tfirstName // FirstName
<*> tlastName // LastName
<*> tbirthDate // BirthDate
<*> tsocialNo // SocialNo
let tcustomer =
let version v t =
let tf = sprintf "@version expected to be %d" v |> MessageBadCause |> Some
let f x= if v = x then None else tf
let tc = tmember "@version" 0 (tverify f tasInt) |>> ignore
tc, t
let invalidVersion =
let tc = tmember "@version" "" tasString |>> ignore
tc, tfailwith Customer.Zero "Invalid @version"
[|
version 2 tcustomer_v2 // if @version=2 tag exists
version 1 tcustomer_v1 // if @version=1 tag exists
invalidVersion // Swallows all unrecognized versions
tname |>> ignore, tcustomer_b1 // Detects unversion beta 1 schema
treturn () , tcustomer_b2 // Assumes all other unversioned to be beta 2 schema
|] |> tchoosez
let tcustomers = tmany tcustomer
let transform () =
let parsed = Newtonsoft.Json.JsonConvert.DeserializeObject<JArray> json
Transform.trun jsonContext parsed Approach1.tcustomers |> printfn "%A"
Transform.trun jsonContext parsed Approach2.tcustomers |> printfn "%A"
[<EntryPoint>]
let main argv =
// transformDictionaries ()
// transformJson ()
Versioned.transform ()
0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment