{"id":27679040,"url":"https://github.com/MoiraeSoftware/myriad","last_synced_at":"2025-04-25T03:06:59.056Z","repository":{"id":34786836,"uuid":"181503824","full_name":"MoiraeSoftware/Myriad","owner":"MoiraeSoftware","description":"Myriad is a code generator for F#","archived":false,"fork":false,"pushed_at":"2025-03-06T18:25:37.000Z","size":7156,"stargazers_count":357,"open_issues_count":7,"forks_count":44,"subscribers_count":18,"default_branch":"master","last_synced_at":"2025-04-21T05:54:54.548Z","etag":null,"topics":["codegenerator","fsharp"],"latest_commit_sha":null,"homepage":"https://moiraesoftware.github.io/Myriad/","language":"F#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/MoiraeSoftware.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"Contributing.md","funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":null,"patreon":null,"open_collective":null,"ko_fi":"7sharp9","tidelift":null,"community_bridge":null,"liberapay":null,"issuehunt":null,"otechie":null,"custom":null}},"created_at":"2019-04-15T14:27:11.000Z","updated_at":"2025-04-17T08:36:47.000Z","dependencies_parsed_at":"2024-01-06T23:54:54.729Z","dependency_job_id":"c0399c99-b854-4257-a414-9530fbe90257","html_url":"https://github.com/MoiraeSoftware/Myriad","commit_stats":{"total_commits":640,"total_committers":22,"mean_commits":29.09090909090909,"dds":0.334375,"last_synced_commit":"5124a9e9d98cb2f4d5fb2926955d0195cc620399"},"previous_names":[],"tags_count":43,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MoiraeSoftware%2FMyriad","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MoiraeSoftware%2FMyriad/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MoiraeSoftware%2FMyriad/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MoiraeSoftware%2FMyriad/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/MoiraeSoftware","download_url":"https://codeload.github.com/MoiraeSoftware/Myriad/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250007258,"owners_count":21359746,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["codegenerator","fsharp"],"created_at":"2025-04-25T03:01:47.302Z","updated_at":"2025-04-25T03:06:59.046Z","avatar_url":"https://github.com/MoiraeSoftware.png","language":"F#","funding_links":["https://ko-fi.com/7sharp9","https://ko-fi.com/K3K115UYS"],"categories":["Code Generation"],"sub_categories":[],"readme":"# Myriad\n\nMyriad is a code generator. It takes input from files or data sources and the library provides different mechanisms to allow F# code to be produced in response to the file, whether that file be an F# source file or a simple text file.  \n\nMyriad can be used from either an MSBuild extension or from its CLI tool.\n\nThe idea behind Myriad is to un-complicate, as far as possible, the ability to do meta-programming in F#. By meta-programming in F# I mean generating idiomatic F# code using F# native types like discriminated unions and records, this is something that is not possible with F# Type Providers etc. which just output basic .NET classes.\n\nMyriad is an evolution of the ideas I developed while working with F#'s type providers and other meta-programming functionality like quotations and AST manipulation. Myriad aims to make it easy to extend the compiler via Myriad plugins. Myriad provides an approach to compiler extension that isn't modifying or adjusting Type Providers or waiting a long time for other F# language improvements. You write a Myriad plugin that works on a fragment of AST input, and the plugin supplies AST output with the final form being source code that is built into your project. This lets the compiler optimise generated output in addition to allowing tooling to operate effectively.\n\nIf you want to help and contribute code than thats great check out the issues and make a PR.  \n\nIf you enjoy this repo and wanted to shown your appriciation etc then I do have Ko-fi:  \n  \n[![ko-fi](https://www.ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/K3K115UYS)  \n\n## Usage via MSBuild\n\nTo use Myriad via its MSBuild support you add the `Myriad.Core` and `Myriad.Sdk` package references:\n```xml\n    \u003cItemGroup\u003e\n      \u003cPackageReference Include=\"Myriad.Core\" Version=\"0.5.0\" /\u003e\n      \u003cPackageReference Include=\"Myriad.Sdk\" Version=\"0.5.0\" /\u003e\n    \u003c/ItemGroup\u003e\n```\n\nAn input file is specified by using the usual `Compile` element:\n```xml\n\u003cCompile Include=\"Library.fs\"/\u003e\n\u003cCompile Include=\"Generated.fs\"\u003e\n    \u003cMyriadFile\u003eLibrary.fs\u003c/MyriadFile\u003e\n\u003c/Compile\u003e\n```\nThis is configuring Myriad so that a file called `Generated.fs` will be included in the build using `Library.fs` as input to the Myriad.  \n\nIt is also possible to append the generated content to the input file.\n\n```xml\n\u003cCompile Include=\"Library.fs\"\u003e\n    \u003cMyriadInlineGeneration\u003etrue\u003c/MyriadInlineGeneration\u003e\n\u003c/Compile\u003e\n```\n\nMyriad works by using plugins to generate code.  A plugin called fields is included with Myriad which takes inspiration from OCaml's [ppx_fields_conv](https://github.com/janestreet/ppx_fields_conv) plugin of the same name.\n\nThe input file in this example `Library.fs` looks like this:\n```fsharp\nnamespace Example\nopen Myriad.Plugins\n\n[\u003cGenerator.Fields \"fields\"\u003e]\ntype Test1 = { one: int; two: string; three: float; four: float32 }\ntype Test2 = { one: Test1; two: string }\n```\n\nAttribute's are use so that the code generator knows which parts of the input AST are to be processed by the plugin. If you had several records and you only want the fields plugin to operate on `Test1` then the attribute would be used like in the example to only apply `Generator.Fields` to `Test1`. Note, if you wanted a plugin that just needs the whole input AST then there is no need to provide an input. Myriad aims to be a library rather than a full framework that ties you to the mechanism used to input and generate code.  The parameter passed to the attribute \"fields\" specifies the configuration section that is used for the plugin in the `myriad.toml` file.   In this instance fields is used and the `myriad.toml` file is as follows:\n\n```toml\n[fields]\nnamespace = \"TestFields\"\n```\n\nThis specifies the namespace that is used for the plugin, which in this case is \"TestFields\".  \n\nThe fields plugin in this example will generate the following code at pre-build time and compile the code into your assembly:\n```fsharp\n//------------------------------------------------------------------------------\n//        This code was generated by myriad.\n//        Changes to this file will be lost when the code is regenerated.\n//------------------------------------------------------------------------------\nnamespace rec TestFields\n\nmodule Test1 =\n    open Example\n\n    let one (x : Test1) = x.one\n    let two (x : Test1) = x.two\n    let three (x : Test1) = x.three\n    let four (x : Test1) = x.four\n\n    let create (one : int) (two : string) (three : float) (four : float32) : Test1 =\n        { one = one\n          two = two\n          three = three\n          four = four }\n\n    let map (mapone : int -\u003e int) (maptwo : string -\u003e string) (mapthree : float -\u003e float) (mapfour : float32 -\u003e float32) (record': Test1) =\n      { record' with\n          one = mapone record'.one\n          two = maptwo record'.two\n          three = mapthree record'.three\n          four = mapfour record'.four }\n```\n\nThe fields plugin generates a `map` for each field in the input record, a `create` function taking each field, and a `map` function that takes one function per field in the input record.\n\nThe map functions for each field are useful in situations where you just want to use a single field from a record in a lambda like a list of records:\n```fsharp\nlet records = [{one = \"a\"; two = \"aa\"; three = 42.0; four = 172.0f}\n               {one = \"b\"; two = \"bb\"; three = 42.0; four = 172.0f}]\n records |\u003e List.sortBy Test1.one\n```\n\n### Lens plugin\n\nMyriad can also generate [lenses](https://fsprojects.github.io/FSharpPlus/tutorial.html#Lens) for records and single-case discriminated unions.\nLens is a _pair_ of a `getter` and a `setter` for one property of the type. Given the object `Lens` allows you to get the value of the property or to update it, creating a new object. The advantage of _lenses_ is an ability to combine them to read or update nested fields of the object.\n\nTo create lenses for your type, first annotate the type for which you want lenses to be generated with `Generator.Lenses` attribute:\n\n```fsharp\n[\u003cGenerator.Lenses(\"lens\")\u003e]\ntype Record =\n    { one: int\n      two: string }\n```\n\nMyriad will generate the following code:\n\n```fsharp\nmodule RecordLenses =\n    let one = (fun (x: Test1) -\u003e x.one), (fun (x: Test1) (value: int) -\u003e { x with one = value })\n    let two = (fun (x: Test1) -\u003e x.two), (fun (x: Test1) (value: string) -\u003e { x with two = value })\n```\n\nOften lenses are defined as a single-case union around a pair of getter and setter. Myriad is also capable of adding the invocation of such DU's constructor.\n\nTo achieve this, decorate your type with the `Lens` attribute, specifying the name of the DU constructor: `[\u003cGenerator.Lenses(\"Lens\")\u003e]`, and Myriad will generate this code:\n\n```fsharp\nmodule RecordLenses =\n    let one = Lens((fun (x: Test1) -\u003e x.one), (fun (x: Test1) (value: int) -\u003e { x with one = value }))\n    let two = Lens((fun (x: Test1) -\u003e x.two), (fun (x: Test1) (value: string) -\u003e { x with two = value }))\n```\n\nYou can provide the name of DU constructor in several ways:\n- As a string: `[\u003cGenerator.Lenses(\"lens\", \"Lens\")\u003e]`;\n- Or as a type: `[\u003cGenerator.Lenses(\"lens\", typedefof\u003cLens\u003c_, _\u003e\u003e)\u003e]` or `[\u003cGenerator.Lenses(typeof\u003cLens\u003c_, _\u003e\u003e)\u003e]`.\n\nIf the `Lens` type is in different namespace/module than the type decorated with the attribute, provide the full name of the `Lens` constructor: `[\u003cGenerator.Lenses(\"Namespace.And.Module.Of.Lens\")\u003e]`.\n\n---\n\nThe full fsproj is detail below:\n```xml\n\u003cProject Sdk=\"Microsoft.NET.Sdk\"\u003e\n    \u003cPropertyGroup\u003e\n        \u003cTargetFramework\u003enet9.0\u003c/TargetFramework\u003e\n    \u003c/PropertyGroup\u003e\n    \u003cItemGroup\u003e\n        \u003cCompile Include=\"Library.fs\" /\u003e\n        \u003cCompile Include=\"Generated.fs\"\u003e\n            \u003cMyriadFile\u003eLibrary.fs\u003c/MyriadFile\u003e\n        \u003c/Compile\u003e\n    \u003c/ItemGroup\u003e\n    \u003cItemGroup\u003e\n      \u003cPackageReference Include=\"Myriad.Core\" Version=\"0.5.0\" /\u003e\n      \u003cPackageReference Include=\"Myriad.Sdk\" Version=\"0.5.0\" /\u003e\n    \u003c/ItemGroup\u003e\n\u003c/Project\u003e\n```\n\n## Plugins\n\nPlugins for Myriad are supplied by including the nuget package in your project. The nuget infrastructure supplies the necessary MSBuild props and targets so that the plugin is used by Myriad automatically. Following the source for the fields plugin can be used as reference until more details about authoring plugins is created.\n\n### Naming of plugins\n\nIf you make a plugin the an informal naming convention is to use is:\n{{OwnerNamespace}}.Myriad.Plugin\n\n### Using external Plugins\n\nTo consume external plugins that aren't included in the `Myriad.Plugins` package, you must register them with Myriad. If you are using the CLI tool then the way to do this is by passing in the `--plugin \u003cpath to dll\u003e` command-line argument. If you are using MSBuild then this can be done by adding to the `MyriadSdkGenerator` property to your project file:\n\n```xml\n\u003cItemGroup\u003e\n    \u003cMyriadSdkGenerator Include=\"\u003cpath to plugin dll\u003e\" /\u003e\n\u003c/ItemGroup\u003e\n```\n\nFor example, if you had a project layout like this:\n\n```\n\\src\n-\\GeneratorLib\n - Generator.fs\n - Generator.fsproj\n-\\GeneratorTests\n - Tests.fs\n - GeneratorTests.fsproj\n```\n\nYou would add the following to Generator.fsproj:\n```xml\n  \u003cItemGroup\u003e\n    \u003cContent Include=\"build\\Generator.props\"\u003e\n      \u003cPack\u003etrue\u003c/Pack\u003e\n      \u003cPackagePath\u003e%(Identity)\u003c/PackagePath\u003e\n      \u003cVisible\u003etrue\u003c/Visible\u003e\n    \u003c/Content\u003e\n  \u003c/ItemGroup\u003e\n```\n\nThen add a new folder `build` with the `Generator.props` file within:\n```xml\n\u003cProject\u003e\n    \u003cItemGroup\u003e\n        \u003cMyriadSdkGenerator Include=\"$(MSBuildThisFileDirectory)/../lib/netstandard2.1/Generator.dll\" /\u003e\n    \u003c/ItemGroup\u003e\n\u003c/Project\u003e\n```\n\nOften an additional props file (In this sample the file would be `Generator.InTest.props`) is used to make testing easier.  The matching element for the tests `.fsproj` would be something like this:\n\n```xml\n\u003cProject\u003e\n    \u003cItemGroup\u003e\n        \u003cMyriadSdkGenerator Include=\"$(MSBuildThisFileDirectory)/../bin/$(Configuration)/netstandard2.1/Generator.dll\" /\u003e\n    \u003c/ItemGroup\u003e\n\u003c/Project\u003e\n```\n\nNotice the Include path is pointing locally rather than within the packaged nuget folder structure.\n\nIn your testing `fsproj` you would add the following to allow the plugin to be used locally rather that having to consume a nuget package:\n\n```xml\n\u003c!-- include plugin --\u003e\n\u003cImport Project=\"\u003cPath to Generator plugin location\u003e\\build\\Myriad.Plugins.InTest.props\" /\u003e\n```\n\n## Debugging\n\nTo debug Myriad, you can use the following two command line options:\n\n* `--verbose` — write diagnostic logs out to standard out\n* `--wait-for-debugger` — causes Myriad to wait for a debugger to attach to the Myriad process\n\nThese can be triggered from MSBuild by the `\u003cMyriadSdkVerboseOutput\u003etrue\u003c/MyriadSdkVerboseOutput\u003e` and `\u003cMyriadSdkWaitForDebugger\u003etrue\u003c/MyriadSdkWaitForDebugger\u003e` properties, respectively.\n\n## Nuget\nThe nuget package for Myriad can be found here:\n[Nuget package](https://www.nuget.org/packages/myriad/).\n\n## Dotnet template\nA dotnet template for a Myriad plugin/generator is available here:\n```\n#install dotnet template\ndotnet new -i Myriad.Templates\n\n#create myriad generator from the template\ndotnet new myriadgenerator -n myMyriadPlugin\n```\n\n## How to build and test\n\n1. Make sure you have .Net Core SDK installed - check required version in [global.json](global.json)\n2. Run `dotnet tool restore`\n3. Run `dotnet build -c Release -t:Build`\n\n## How to release new version\n\n1. Update [CHANGELOG.md](./CHANGELOG.md) by adding new entry (`## [0.X.X]`) and commit it.\n2. Create version tag (`git tag v0.X.X`)\n3. Update the `VersionPrefix` in `Directory.Build.props` to match the tag above.\n4. Run `dotnet build -t:Pack` to create the nuget package and test/examine it locally.\n5. Push the tag to the repo `git push origin v0.X.X` - this will start CI process that will create GitHub release and put generated NuGet packages in it\n6. Upload generated packages into NuGet.org\n\n### Also see\n* [Applied Metaprogramming with Myriad And Falanx](https://7sharp9.dev/programming/2019-04-24-applied-metaprogramming-with-myriad/)\n* [Myriad Intro](https://7sharp9.dev/programming/2019-11-06-myriad-intro/)\n\n### External plugins\n\nHere is a list of external plugins that have been built\n\n[SqlHyra](https://github.com/JordanMarr/SqlHydra)\n[JsonWrapper](https://github.com/BinaryDefense/JsonWrapper)\n[TypeSafeInternals](https://github.com/TheAngryByrd/TypeSafeInternals)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FMoiraeSoftware%2Fmyriad","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FMoiraeSoftware%2Fmyriad","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FMoiraeSoftware%2Fmyriad/lists"}