{"id":16663444,"url":"https://github.com/ndmitchell/record-dot-preprocessor","last_synced_at":"2025-04-05T18:09:58.771Z","repository":{"id":43974794,"uuid":"132173766","full_name":"ndmitchell/record-dot-preprocessor","owner":"ndmitchell","description":"A preprocessor for a Haskell record syntax using dot","archived":false,"fork":false,"pushed_at":"2024-01-15T17:56:08.000Z","size":326,"stargazers_count":129,"open_issues_count":10,"forks_count":20,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-03-29T17:09:46.102Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Haskell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ndmitchell.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGES.txt","contributing":null,"funding":null,"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}},"created_at":"2018-05-04T18:10:22.000Z","updated_at":"2024-12-12T08:07:17.000Z","dependencies_parsed_at":"2024-01-15T19:41:00.939Z","dependency_job_id":"9ca9aec7-09e9-486a-9a5c-11cbd01f7314","html_url":"https://github.com/ndmitchell/record-dot-preprocessor","commit_stats":{"total_commits":350,"total_committers":11,"mean_commits":"31.818181818181817","dds":"0.18285714285714283","last_synced_commit":"18a33826d0653dc295635c751fb616ff850189f3"},"previous_names":[],"tags_count":21,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ndmitchell%2Frecord-dot-preprocessor","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ndmitchell%2Frecord-dot-preprocessor/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ndmitchell%2Frecord-dot-preprocessor/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ndmitchell%2Frecord-dot-preprocessor/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ndmitchell","download_url":"https://codeload.github.com/ndmitchell/record-dot-preprocessor/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247378149,"owners_count":20929297,"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":[],"created_at":"2024-10-12T10:40:59.869Z","updated_at":"2025-04-05T18:09:58.721Z","avatar_url":"https://github.com/ndmitchell.png","language":"Haskell","funding_links":[],"categories":[],"sub_categories":[],"readme":"# record-dot-preprocessor [![Hackage version](https://img.shields.io/hackage/v/record-dot-preprocessor.svg?label=Hackage)](https://hackage.haskell.org/package/record-dot-preprocessor) [![Stackage version](https://www.stackage.org/package/record-dot-preprocessor/badge/nightly?label=Stackage)](https://www.stackage.org/package/record-dot-preprocessor) [![Build status](https://img.shields.io/github/actions/workflow/status/ndmitchell/record-dot-preprocessor/ci.yml?branch=master)](https://github.com/ndmitchell/record-dot-preprocessor/actions)\n\nIn almost every programming language `a.b` will get the `b` field from the `a` data type, and many different data types can have a `b` field. The reason this feature is ubiquitous is because it's _useful_. The `record-dot-preprocessor` brings this feature to modern GHC versions. This feature has been [proposed for Haskell](https://github.com/ghc-proposals/ghc-proposals/pull/282) as `RecordDotSyntax`. Since GHC 9.2 the [`OverloadedRecordDot`](https://downloads.haskell.org/~ghc/9.2.3/docs/html/users_guide/exts/overloaded_record_dot.html#extension-OverloadedRecordDot) and [`OverloadedRecordUpdate`](https://downloads.haskell.org/~ghc/9.2.3/docs/html/users_guide/exts/overloaded_record_update.html) extensions implement much the same functionality. Some examples:\n\n```haskell\ndata Company = Company {name :: String, owner :: Person}\ndata Person = Person {name :: String, age :: Int}\n\ndisplay :: Company -\u003e String\ndisplay c = c.name ++ \" is run by \" ++ c.owner.name\n\nnameAfterOwner :: Company -\u003e Company\nnameAfterOwner c = c{name = c.owner.name ++ \"'s Company\"}\n```\n\nHere we declare two records both with `name` as a field, then write `c.name` and `c.owner.name` to get those fields. We can also write `c{name = x}` as a record update, which still works even though `name` is no longer unique.\n\n## How do I use this magic?\n\nFirst install `record-dot-preprocessor` with either `stack install record-dot-preprocessor` or `cabal update \u0026\u0026 cabal install record-dot-preprocessor`. Then at the top of the file add:\n\n* Either: `{-# OPTIONS_GHC -F -pgmF=record-dot-preprocessor #-}` for the preprocessor.\n* Or: `{-# OPTIONS_GHC -fplugin=RecordDotPreprocessor #-}` and `{-# LANGUAGE DuplicateRecordFields, TypeApplications, FlexibleContexts, DataKinds, MultiParamTypeClasses, TypeSynonymInstances, FlexibleInstances, UndecidableInstances, GADTs #-}` for the GHC plugin.\n\nThe GHC plugin only runs on GHC 8.6 or higher, [has some issues on Windows](https://gitlab.haskell.org/ghc/ghc/issues/16405) and has much better error messages. In contrast, the preprocessor runs everywhere and has more features.\n\nYou must make sure that the `OPTIONS_GHC` is applied both to the file _where your records are defined_, and _where the record syntax is used_. The resulting program will require the [`record-hasfield` library](https://hackage.haskell.org/package/record-hasfield).\n\n## What magic is available, precisely?\n\nUsing the preprocessor or the GHC plugin you can write:\n\n* `expr.lbl` is equivalent to `getField @\"lbl\" expr` (the `.` cannot have whitespace on either side).\n* `expr{lbl = val}` is equivalent to `setField @\"lbl\" expr val` (the `{` cannot have whitespace before it).\n* `(.lbl)` is equivalent to `(\\x -\u003e x.lbl)` (the `.` cannot have whitespace after).\n\nUsing the preprocessor, but _not_ the GHC plugin:\n\n* `expr{lbl1.lbl2 = val}` is equivalent to `expr{lbl1 = (expr.lbl1){lbl2 = val}}`, performing a nested update.\n* `expr{lbl * val}` is equivalent to `expr{lbl = expr.lbl * val}`, where `*` can be any operator.\n* `expr{lbl1.lbl2}` is equivalent to `expr{lbl1.lbl2 = lbl2}`.\n\nThese forms combine to offer the identities:\n\n* `expr.lbl1.lbl2` is equivalent to `(expr.lbl1).lbl2`.\n* `(.lbl1.lbl2)` is equivalent to `(\\x -\u003e x.lbl1.lbl2)`.\n* `expr.lbl1{lbl2 = val}` is equivalent to `(expr.lbl1){lbl2 = val}`.\n* `expr{lbl1 = val}.lbl2` is equivalent to `(expr{lbl1 = val}).lbl2`.\n* `expr{lbl1.lbl2 * val}` is equivalent to `expr{lbl1.lbl2 = expr.lbl1.lbl2 * val}`.\n* `expr{lbl1 = val1, lbl2 = val2}` is equivalent to `(expr{lbl1 = val1}){lbl2 = val2}`.\n\n## How does this magic compare to other magic?\n\nRecords in Haskell are well known to be [pretty lousy](https://www.yesodweb.com/blog/2011/09/limitations-of-haskell). There are [many proposals](https://wiki.haskell.org/Extensible_record) that aim to make Haskell records more powerful using dark arts taken from type systems and category theory. This preprocessor aims for simplicity - combining existing elements into a coherent story. The aim is to do no worse than Java, not achieve perfection.\n\n## Any advice for using this magic?\n\nThe most important consideration is that all records used by `a.b` or `a{b=c}` syntax _must_ have `HasField` instances, which requires either running the preprocessor/plugin over the module defining them, or writing orphan instances by hand. To use records which don't have such instances use normal selector functions (e.g. `b a`) and insert a space before the `{` (e.g. `a {b=c}`).\n\n## Limitations\n\n* The preprocessor doesn't deal with anti-quoted expressions inside `QuasiQuotes`, e.g. `[D.pgSQL|$ SELECT ${dummy.x} :: text|]`.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fndmitchell%2Frecord-dot-preprocessor","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fndmitchell%2Frecord-dot-preprocessor","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fndmitchell%2Frecord-dot-preprocessor/lists"}