diff --git a/ConsumePlugin/Catamorphism.fs b/ConsumePlugin/Catamorphism.fs index b794a06..04cd1f0 100644 --- a/ConsumePlugin/Catamorphism.fs +++ b/ConsumePlugin/Catamorphism.fs @@ -11,12 +11,12 @@ type PairOpKind = | ThenDoSeq [] -type Tree<'a> = - | Const of Const<'a> - | Pair of Tree<'a> * Tree<'a> * PairOpKind - | Sequential of Tree<'a> list - | Builder of Tree<'a> * TreeBuilder<'a> +type Tree<'a, 'b> = + | Const of Const<'a> * 'b + | Pair of Tree<'a, 'b> * Tree<'a, 'b> * PairOpKind + | Sequential of Tree<'a, 'b> list + | Builder of Tree<'a, 'b> * TreeBuilder<'b, 'a> -and TreeBuilder<'a> = - | Child of TreeBuilder<'a> - | Parent of Tree<'a> +and TreeBuilder<'b, 'a> = + | Child of TreeBuilder<'b, 'a> + | Parent of Tree<'a, 'b> diff --git a/ConsumePlugin/GeneratedCatamorphism.fs b/ConsumePlugin/GeneratedCatamorphism.fs index 86ac217..192a327 100644 --- a/ConsumePlugin/GeneratedCatamorphism.fs +++ b/ConsumePlugin/GeneratedCatamorphism.fs @@ -12,16 +12,16 @@ namespace ConsumePlugin open WoofWare.Myriad.Plugins /// Description of how to combine cases during a fold -type TreeBuilderCataCase<'a, 'TreeBuilder, 'Tree> = +type TreeBuilderCataCase<'b, 'a, 'TreeBuilder, 'Tree> = /// How to operate on the Child case abstract Child : 'TreeBuilder -> 'TreeBuilder /// How to operate on the Parent case abstract Parent : 'Tree -> 'TreeBuilder /// Description of how to combine cases during a fold -type TreeCataCase<'a, 'TreeBuilder, 'Tree> = +type TreeCataCase<'a, 'b, 'TreeBuilder, 'Tree> = /// How to operate on the Const case - abstract Const : Const<'a> -> 'Tree + abstract Const : Const<'a> -> 'b -> 'Tree /// How to operate on the Pair case abstract Pair : 'Tree -> 'Tree -> PairOpKind -> 'Tree /// How to operate on the Sequential case @@ -30,30 +30,30 @@ type TreeCataCase<'a, 'TreeBuilder, 'Tree> = abstract Builder : 'Tree -> 'TreeBuilder -> 'Tree /// Specifies how to perform a fold (catamorphism) over the type Tree and its friends. -type TreeCata<'a, 'a, 'TreeBuilder, 'Tree> = +type TreeCata<'b, 'a, 'TreeBuilder, 'Tree> = { /// How to perform a fold (catamorphism) over the type TreeBuilder - TreeBuilder : TreeBuilderCataCase<'a, 'TreeBuilder, 'Tree> + TreeBuilder : TreeBuilderCataCase<'b, 'a, 'TreeBuilder, 'Tree> /// How to perform a fold (catamorphism) over the type Tree - Tree : TreeCataCase<'a, 'TreeBuilder, 'Tree> + Tree : TreeCataCase<'a, 'b, 'TreeBuilder, 'Tree> } /// Methods to perform a catamorphism over the type Tree [] module TreeCata = [] - type private Instruction<'a, 'a> = - | Process__TreeBuilder of TreeBuilder<'a> - | Process__Tree of Tree<'a> + type private Instruction<'b, 'a> = + | Process__TreeBuilder of TreeBuilder<'b, 'a> + | Process__Tree of Tree<'a, 'b> | TreeBuilder_Child | TreeBuilder_Parent | Tree_Pair of PairOpKind | Tree_Sequential of int | Tree_Builder - let private loop (cata : TreeCata<_, _, _, _>) (instructions : ResizeArray>) = - let treeStack = ResizeArray () - let treeBuilderStack = ResizeArray () + let private loop (cata : TreeCata<'b, 'a, 'TreeBuilder, 'Tree>) (instructions : ResizeArray>) = + let treeStack = ResizeArray<'Tree> () + let treeBuilderStack = ResizeArray<'TreeBuilder> () while instructions.Count > 0 do let currentInstruction = instructions.[instructions.Count - 1] @@ -70,7 +70,7 @@ module TreeCata = instructions.Add (Instruction.Process__Tree arg0_0) | Instruction.Process__Tree x -> match x with - | Tree.Const (arg0_0) -> cata.Tree.Const arg0_0 |> treeStack.Add + | Tree.Const (arg0_0, arg1_0) -> cata.Tree.Const arg0_0 arg1_0 |> treeStack.Add | Tree.Pair (arg0_0, arg1_0, arg2_0) -> instructions.Add (Instruction.Tree_Pair (arg2_0)) instructions.Add (Instruction.Process__Tree arg0_0) @@ -121,8 +121,8 @@ module TreeCata = /// Execute the catamorphism. let runTreeBuilder - (cata : TreeCata<'a, 'a, 'TreeBuilderRet, 'TreeRet>) - (x : TreeBuilder<'a, 'a>) + (cata : TreeCata<'b, 'a, 'TreeBuilderRet, 'TreeRet>) + (x : TreeBuilder<'b, 'a>) : 'TreeBuilderRet = let instructions = ResizeArray () @@ -131,7 +131,7 @@ module TreeCata = Seq.exactlyOne treeBuilderRetStack /// Execute the catamorphism. - let runTree (cata : TreeCata<'a, 'a, 'TreeBuilderRet, 'TreeRet>) (x : Tree<'a, 'a>) : 'TreeRet = + let runTree (cata : TreeCata<'b, 'a, 'TreeBuilderRet, 'TreeRet>) (x : Tree<'a, 'b>) : 'TreeRet = let instructions = ResizeArray () instructions.Add (Instruction.Process__Tree x) let treeBuilderRetStack, treeRetStack = loop cata instructions diff --git a/ConsumePlugin/GeneratedFileSystem.fs b/ConsumePlugin/GeneratedFileSystem.fs index 7a472c2..4021d70 100644 --- a/ConsumePlugin/GeneratedFileSystem.fs +++ b/ConsumePlugin/GeneratedFileSystem.fs @@ -33,8 +33,8 @@ module FileSystemItemCata = | Process__FileSystemItem of FileSystemItem | FileSystemItem_Directory of string * int * int - let private loop (cata : FileSystemCata<_>) (instructions : ResizeArray) = - let fileSystemItemStack = ResizeArray () + let private loop (cata : FileSystemCata<'FileSystemItem>) (instructions : ResizeArray) = + let fileSystemItemStack = ResizeArray<'FileSystemItem> () while instructions.Count > 0 do let currentInstruction = instructions.[instructions.Count - 1] @@ -108,8 +108,8 @@ module GiftCata = | Gift_Boxed | Gift_WithACard of string - let private loop (cata : GiftCata<_>) (instructions : ResizeArray) = - let giftStack = ResizeArray () + let private loop (cata : GiftCata<'Gift>) (instructions : ResizeArray) = + let giftStack = ResizeArray<'Gift> () while instructions.Count > 0 do let currentInstruction = instructions.[instructions.Count - 1] diff --git a/ConsumePlugin/ListCata.fs b/ConsumePlugin/ListCata.fs index b0c7ca1..5c75d8e 100644 --- a/ConsumePlugin/ListCata.fs +++ b/ConsumePlugin/ListCata.fs @@ -33,8 +33,8 @@ module MyListCata = | Process__MyList of MyList<'a> | MyList_Cons of 'a - let private loop (cata : MyListCata<_, _>) (instructions : ResizeArray>) = - let myListStack = ResizeArray () + let private loop (cata : MyListCata<'a, 'MyList>) (instructions : ResizeArray>) = + let myListStack = ResizeArray<'MyList> () while instructions.Count > 0 do let currentInstruction = instructions.[instructions.Count - 1] @@ -89,8 +89,8 @@ module MyList2Cata = | Process__MyList2 of MyList2<'a> | MyList2_Cons of 'a - let private loop (cata : MyList2Cata<_, _>) (instructions : ResizeArray>) = - let myList2Stack = ResizeArray () + let private loop (cata : MyList2Cata<'a, 'MyList2>) (instructions : ResizeArray>) = + let myList2Stack = ResizeArray<'MyList2> () while instructions.Count > 0 do let currentInstruction = instructions.[instructions.Count - 1] diff --git a/README.md b/README.md index e598b47..f6845c8 100644 --- a/README.md +++ b/README.md @@ -332,7 +332,7 @@ thereby allowing the programmer to use F#'s record-update syntax. Takes a collection of mutually recursive discriminated unions: ```fsharp -[] +[] type Expr = | Const of Const | Pair of Expr * Expr * PairOpKind @@ -356,7 +356,7 @@ type ExprBuilderCata<'Expr, 'ExprBuilder> = abstract Child : 'ExprBuilder -> 'ExprBuilder abstract Parent : 'Expr -> 'ExprBuilder -type Cata<'Expr, 'ExprBuilder> = +type MyCata<'Expr, 'ExprBuilder> = { Expr : ExprCata<'Expr, 'ExprBuilder> ExprBuilder : ExprBuilderCata<'Expr, 'ExprBuilder> @@ -364,10 +364,10 @@ type Cata<'Expr, 'ExprBuilder> = [] module ExprCata = - let runExpr (cata : Cata<'ExprRet, 'ExprBuilderRet>) (x : Expr) : 'ExprRet = + let runExpr (cata : MyCata<'ExprRet, 'ExprBuilderRet>) (x : Expr) : 'ExprRet = failwith "this is implemented" - let runExprBuilder (cata : Cata<'ExprRet, 'ExprBuilderRet>) (x : ExprBuilder) : 'ExprBuilderRet = + let runExprBuilder (cata : MyCata<'ExprRet, 'ExprBuilderRet>) (x : ExprBuilder) : 'ExprBuilderRet = failwith "this is implemented" ``` @@ -381,6 +381,10 @@ and then each time you only plug in what you want to do. * Mutually recursive DUs are supported (as in the example above). Every DU in a recursive `type Foo... and Bar...` knot will be given an appropriate cata, as long as any one of those DUs has the `[]` attribute. * There is *limited* support for records and for lists. +* There is *extremely brittle* support for generics in the DUs you are cata'ing over. + It is based on the names of the generic parameters, so you must ensure that generic parameters with the same name have the same meaning across the various cases in your recursive knot of DUs. + (If you overstep the bounds of what this generator can do, you will get compile-time errors, e.g. with generics being constrained to each other's values.) + See the [List tests](./WoofWare.Myriad.Plugins.Test/TestCataGenerator/TestMyList2.fs) for an example, where we re-implement `FSharpList<'a>`. ### Limitations diff --git a/WoofWare.Myriad.Plugins.Test/TestCataGenerator/TestCataGenerator.fs b/WoofWare.Myriad.Plugins.Test/TestCataGenerator/TestCataGenerator.fs index 8516b68..1d53a6e 100644 --- a/WoofWare.Myriad.Plugins.Test/TestCataGenerator/TestCataGenerator.fs +++ b/WoofWare.Myriad.Plugins.Test/TestCataGenerator/TestCataGenerator.fs @@ -8,17 +8,17 @@ open FsCheck [] module TestCataGenerator = - let idCata : TreeCata<_, _> = + let idCata<'a, 'b> : TreeCata<'a, 'b, _, _> = { Tree = - { new TreeCataCase<_, _> with - member _.Const x = Const x + { new TreeCataCase<_, _, _, _> with + member _.Const x y = Const (x, y) member _.Pair x y z = Pair (x, y, z) member _.Sequential xs = Sequential xs member _.Builder x b = Builder (x, b) } TreeBuilder = - { new TreeBuilderCataCase<_, _> with + { new TreeBuilderCataCase<_, _, _, _> with member _.Child x = Child x member _.Parent x = Parent x } @@ -27,7 +27,7 @@ module TestCataGenerator = [] let ``Example`` () = let x = - Tree.Pair (Tree.Const (Const.Int 0), Tree.Const (Const.String ""), PairOpKind.ThenDoSeq) + Tree.Pair (Tree.Const (Const.Verbatim 0, "hi"), Tree.Const (Const.String "", "bye"), PairOpKind.ThenDoSeq) TreeCata.runTree idCata x |> shouldEqual x @@ -36,7 +36,7 @@ module TestCataGenerator = let ``Cata works`` () = let builderCases = ref 0 - let property (x : Tree) = + let property (x : Tree) = match x with | Tree.Builder _ -> Interlocked.Increment builderCases |> ignore | _ -> () diff --git a/WoofWare.Myriad.Plugins.Test/TestCataGenerator/TestMyList.fs b/WoofWare.Myriad.Plugins.Test/TestCataGenerator/TestMyList.fs index 35dfed8..f39ef0a 100644 --- a/WoofWare.Myriad.Plugins.Test/TestCataGenerator/TestMyList.fs +++ b/WoofWare.Myriad.Plugins.Test/TestCataGenerator/TestMyList.fs @@ -21,7 +21,6 @@ module TestMyList = Tail = tail } } - } [] diff --git a/WoofWare.Myriad.Plugins.Test/TestCataGenerator/TestMyList2.fs b/WoofWare.Myriad.Plugins.Test/TestCataGenerator/TestMyList2.fs index 7c65b9b..1660b5b 100644 --- a/WoofWare.Myriad.Plugins.Test/TestCataGenerator/TestMyList2.fs +++ b/WoofWare.Myriad.Plugins.Test/TestCataGenerator/TestMyList2.fs @@ -14,9 +14,8 @@ module TestMyList2 = { new MyList2CataCase<'a, _> with member _.Nil = MyList2.Nil - member _.Cons head tail = MyList2.Cons (head, tail) + member _.Cons (head : 'a) (tail : MyList2<'a>) = MyList2.Cons (head, tail) } - } [] diff --git a/WoofWare.Myriad.Plugins/AstHelper.fs b/WoofWare.Myriad.Plugins/AstHelper.fs index 50c0ac0..21ad941 100644 --- a/WoofWare.Myriad.Plugins/AstHelper.fs +++ b/WoofWare.Myriad.Plugins/AstHelper.fs @@ -76,6 +76,9 @@ type internal AdtNode = { Type : SynType Name : Ident option + /// An ordered list, so you can look up any given generic within `this.Type` + /// to discover what its index is in the parent DU which defined it. + GenericsOfParent : SynTyparDecl list } /// A DU is a sum of products (e.g. `type Thing = Foo of a * b`); @@ -85,6 +88,10 @@ type internal AdtProduct = { Name : SynIdent Fields : AdtNode list + /// This AdtProduct represents a product in which there might be + /// some bound type parameters. This field lists the bound + /// type parameters in the order they appeared on the parent type. + Generics : SynTyparDecl list } [] @@ -435,15 +442,30 @@ module internal AstHelper = { Type = ty Name = id + GenericsOfParent = typars } ) + Generics = typars } ) cases, typars, access | _ -> failwithf "Failed to get union cases for type that was: %+A" repr - let getRecordFields (SynTypeDefn.SynTypeDefn (_, repr, _, _, _, _)) : AdtNode list = + let getRecordFields (SynTypeDefn.SynTypeDefn (typeInfo, repr, _, _, _, _)) : AdtNode list = + let (SynComponentInfo.SynComponentInfo (typeParams = typars)) = typeInfo + + let typars = + match typars with + | None -> [] + | Some (SynTyparDecls.PrefixList (decls, _)) -> decls + | Some (SynTyparDecls.SinglePrefix (l, _)) -> [ l ] + | Some (SynTyparDecls.PostfixList (decls, constraints, _)) -> + if not constraints.IsEmpty then + failwith "Constrained type parameters not currently supported" + + decls + match repr with | SynTypeDefnRepr.Simple (SynTypeDefnSimpleRepr.Record (_, fields, _), _) -> fields @@ -451,6 +473,7 @@ module internal AstHelper = { Name = ident Type = ty + GenericsOfParent = typars } ) | _ -> failwithf "Failed to get record elements for type that was: %+A" repr diff --git a/WoofWare.Myriad.Plugins/CataGenerator.fs b/WoofWare.Myriad.Plugins/CataGenerator.fs index 3b54076..b19f653 100644 --- a/WoofWare.Myriad.Plugins/CataGenerator.fs +++ b/WoofWare.Myriad.Plugins/CataGenerator.fs @@ -121,12 +121,10 @@ module internal CataGenerator = (userProvidedTypars : SynTyparDecl list) (allArtificialTypars : SynType list) (relevantTypar : SynType) - (unionType : SynTypeDefn) + (analysis : UnionAnalysis) : SynBinding = - let relevantTypeName = - match unionType with - | SynTypeDefn.SynTypeDefn (SynComponentInfo.SynComponentInfo (longId = id), _, _, _, _, _) -> List.last id + let relevantTypeName = analysis.ParentTypeName let allArtificialTyparNames = allArtificialTypars @@ -136,7 +134,11 @@ module internal CataGenerator = | _ -> failwith "logic error in generator" ) - let userProvidedTypars = + let userProvidedTyparsForCase = + analysis.Typars + |> List.map (fun (SynTyparDecl.SynTyparDecl (_, ty)) -> SynType.Var (ty, range0)) + + let userProvidedTyparsForCata = userProvidedTypars |> List.map (fun (SynTyparDecl.SynTyparDecl (_, ty)) -> SynType.Var (ty, range0)) @@ -147,7 +149,7 @@ module internal CataGenerator = let inputObjectType = let baseType = - SynType.CreateLongIdent (SynLongIdent.CreateFromLongIdent [ relevantTypeName ]) + SynType.CreateLongIdent (SynLongIdent.CreateFromLongIdent relevantTypeName) if userProvidedTypars.Length = 0 then baseType @@ -155,7 +157,7 @@ module internal CataGenerator = SynType.App ( baseType, Some range0, - userProvidedTypars, + userProvidedTyparsForCase, List.replicate (userProvidedTypars.Length - 1) range0, Some range0, false, @@ -172,7 +174,7 @@ module internal CataGenerator = SynType.App ( SynType.CreateLongIdent (SynLongIdent.CreateFromLongIdent [ cataName ]), Some range0, - userProvidedTypars @ allArtificialTypars, + userProvidedTyparsForCata @ allArtificialTypars, List.replicate (userProvidedTypars.Length + allArtificialTypars.Length - 1) range0, Some range0, false, @@ -196,8 +198,8 @@ module internal CataGenerator = None ), SynPat.CreateLongIdent ( - SynLongIdent.CreateString ("run" + relevantTypeName.idText), - [ SynPat.CreateParen (cataObject) ; SynPat.CreateParen inputObject ] + SynLongIdent.CreateString ("run" + List.last(relevantTypeName).idText), + [ SynPat.CreateParen cataObject ; SynPat.CreateParen inputObject ] ), Some (SynBindingReturnInfo.Create relevantTypar), SynExpr.CreateTyped ( @@ -221,10 +223,7 @@ module internal CataGenerator = SynExpr.CreateLongIdent (SynLongIdent.Create [ "instructions" ; "Add" ]), SynExpr.CreateParen ( SynExpr.CreateApp ( - SynExpr.CreateLongIdent ( - SynLongIdent.Create - [ "Instruction" ; "Process__" + relevantTypeName.idText ] - ), + SynExpr.CreateLongIdent analysis.AssociatedProcessInstruction, SynExpr.CreateLongIdent (SynLongIdent.CreateString "x") ) ) @@ -477,6 +476,8 @@ module internal CataGenerator = { Name = name |> Option.map Ident.lowerFirstLetter Type = ty + // TODO this is definitely wrong + GenericsOfParent = [] } ) @@ -519,6 +520,7 @@ module internal CataGenerator = false, range0 ) + GenericsOfParent = union.Typars } |> List.singleton } @@ -531,12 +533,28 @@ module internal CataGenerator = /// Build the DU which defines the states our state machine can be in. let createInstructionType (analysis : UnionAnalysis list) : SynTypeDefn = + let parentGenerics = + analysis + |> List.collect _.Typars + |> List.map (fun (SynTyparDecl.SynTyparDecl (_, SynTypar.SynTypar (ident, _, _))) -> ident.idText) + |> List.distinct + |> List.map (fun i -> + SynTyparDecl.SynTyparDecl ([], SynTypar.SynTypar (Ident.Create i, TyparStaticReq.None, false)) + ) + // One union case for each union type, and then // a union case for each union case which contains a recursive reference. let casesFromProcess : SynUnionCase list = baseCases analysis |> List.map (fun unionCase -> - SynUnionCase.Create (unionCase.Name, unionCase.Fields |> List.map (fun f -> SynField.Create f.Type)) + let fields = + unionCase.Fields + |> List.map (fun field -> + // TODO: adjust type parameters + SynField.Create field.Type + ) + + SynUnionCase.Create (unionCase.Name, fields) ) let casesFromCases = @@ -548,14 +566,22 @@ module internal CataGenerator = let cases = casesFromProcess @ casesFromCases let typars = - // TODO: deduplicate names where we have the same generic across multiple DUs - analysis - |> List.collect _.Typars - |> fun x -> - if x.IsEmpty then - None - else - Some (SynTyparDecls.PostfixList (x, [], range0)) + let count = analysis |> List.map (fun x -> List.length x.Typars) |> List.max + + if analysis |> List.forall (fun x -> x.Typars.IsEmpty) then + None + else + + let typars = + analysis + |> List.collect _.Typars + |> List.map (fun (SynTyparDecl.SynTyparDecl (_, SynTypar.SynTypar (ident, _, _))) -> ident.idText) + |> List.distinct + |> List.map (fun i -> + SynTyparDecl.SynTyparDecl ([], SynTypar.SynTypar (Ident.Create i, TyparStaticReq.None, false)) + ) + + Some (SynTyparDecls.PostfixList (typars, [], range0)) SynTypeDefn.SynTypeDefn ( SynComponentInfo.SynComponentInfo ( @@ -759,7 +785,11 @@ module internal CataGenerator = let userInputGenerics = analysis.Typars - |> List.map (fun (SynTyparDecl.SynTyparDecl (_, typar)) -> SynType.Var (typar, range0)) + |> List.map (fun (SynTyparDecl.SynTyparDecl (_, SynTypar.SynTypar (ident, _, _))) -> ident.idText) + |> List.distinct + |> List.map (fun i -> + SynType.Var (SynTypar.SynTypar (Ident.Create i, TyparStaticReq.None, false), range0) + ) let ty = SynType.App ( @@ -790,9 +820,11 @@ module internal CataGenerator = // A "real" generic for each generic in the user-provided type let genericsFromUserInput = analysis - |> List.collect (fun analysis -> - // TODO: deduplicate generics with the same name from different cases - analysis.Typars + |> List.collect _.Typars + |> List.map (fun (SynTyparDecl.SynTyparDecl (_, SynTypar.SynTypar (ident, _, _))) -> ident.idText) + |> List.distinct + |> List.map (fun i -> + SynTyparDecl.SynTyparDecl ([], SynTypar.SynTypar (Ident.Create i, TyparStaticReq.None, false)) ) let genericsFromCata = @@ -1346,16 +1378,20 @@ module internal CataGenerator = None ) - // A generic for each DU case, and a generic for each generic in the DU - let genericCount = analysis.Length + (analysis |> List.sumBy _.Typars.Length) + let userSuppliedGenerics = + analysis + |> List.collect _.Typars + |> List.map (fun (SynTyparDecl.SynTyparDecl (_, SynTypar.SynTypar (ident, _, _))) -> ident.idText) + |> List.distinct + |> List.map (fun i -> SynTypar.SynTypar (Ident.Create i, TyparStaticReq.None, false)) let instructionsArrType = - if genericCount > analysis.Length then + if not userSuppliedGenerics.IsEmpty then SynType.App ( SynType.CreateLongIdent "Instruction", Some range0, - List.replicate (genericCount - analysis.Length) (SynType.Anon range0), - List.replicate (genericCount - analysis.Length - 1) range0, + userSuppliedGenerics |> List.map (fun x -> SynType.Var (x, range0)), + List.replicate (userSuppliedGenerics.Length - 1) range0, Some range0, false, range0 @@ -1363,6 +1399,14 @@ module internal CataGenerator = else SynType.CreateLongIdent "Instruction" + let cataGenerics = + [ + for generic in userSuppliedGenerics do + yield SynType.Var (generic, range0) + for case in analysis do + yield SynType.Var (SynTypar.SynTypar (case.GenericName, TyparStaticReq.None, false), range0) + ] + let headPat = SynPat.LongIdent ( SynLongIdent.CreateString "loop", @@ -1376,8 +1420,8 @@ module internal CataGenerator = SynType.App ( SynType.CreateLongIdent (SynLongIdent.CreateFromLongIdent [ cataTypeName ]), Some range0, - List.replicate genericCount (SynType.Anon range0), - List.replicate (genericCount - 1) range0, + cataGenerics, + List.replicate (cataGenerics.Length - 1) range0, Some range0, false, range0 @@ -1492,7 +1536,20 @@ module internal CataGenerator = SynPat.Named (SynIdent.SynIdent (unionCase.StackName, None), false, None, range0), None, SynExpr.CreateApp ( - SynExpr.CreateLongIdent (SynLongIdent.CreateString "ResizeArray"), + SynExpr.TypeApp ( + SynExpr.CreateIdent (Ident.Create "ResizeArray"), + range0, + [ + SynType.Var ( + SynTypar.SynTypar (unionCase.GenericName, TyparStaticReq.None, false), + range0 + ) + ], + [], + Some range0, + range0, + range0 + ), SynExpr.CreateConst SynConst.Unit ), range0, @@ -1562,14 +1619,20 @@ module internal CataGenerator = |> fun x -> SynType.Var (x, range0) ) - let userProvidedGenerics = analysis |> List.collect (fun x -> x.Typars) - - let runFunctions = - List.zip allUnionTypes allTypars - |> List.map (fun (unionType, relevantTypar) -> - createRunFunction cataName userProvidedGenerics allTypars relevantTypar unionType + let userProvidedGenerics = + analysis + |> List.collect _.Typars + |> List.map (fun (SynTyparDecl (_, SynTypar.SynTypar (ident, _, _))) -> ident.idText) + |> List.distinct + |> List.map (fun x -> + SynTyparDecl.SynTyparDecl ([], SynTypar.SynTypar (Ident.Create x, TyparStaticReq.None, false)) ) + let runFunctions = + List.zip analysis allTypars + |> List.map (fun (analysis, relevantTypar) -> + createRunFunction cataName userProvidedGenerics allTypars relevantTypar analysis + ) let cataStructures = createCataStructure analysis