# New notation for records?

**URL:** <https://discourse.haskell.org/t/new-notation-for-records/9663>\
**Category:** Uncategorized\
**Created:** [June 1, 2024, 8:08pm UTC](https://discourse.haskell.org/t/new-notation-for-records/9663 "2024-06-01T20:08:44Z")\
**Posts on this page:** 20\
**Page:** 2

<div class="post-metadata">

**Author:** ![rhendric](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/rhendric/32/2689_2.png) [@rhendric](https://discourse.haskell.org/u/rhendric)\
**Post date:** [June 3, 2024, 1:13am UTC](https://discourse.haskell.org/t/new-notation-for-records/9663/21 "2024-06-03T01:13:17Z")

</div>

> [@darkxero](#):
>
> My original intention was to see if it could be made into an extension of GHC so that the do’s desugar into plain record notation, so you don’t have to type all those brackets and nested records referring to the previous one. It wouldn’t involve lenses.

- Mom, can we add a dependency on lenses?
- We have lenses at home.

Lenses at home:

```hs
{-# LANGUAGE AllowAmbiguousTypes, BlockArguments, DuplicateRecordFields, OverloadedLabels #-}

import Control.Monad.State (State, execState, modify)
import GHC.OverloadedLabels (IsLabel(..))
import GHC.Records (HasField(..))

setField :: HasField s a b => b -> a -> a
setField = error "wait for https://gitlab.haskell.org/ghc/ghc/-/issues/16232"

instance (HasField s a b, c ~ ()) => IsLabel s (State b () -> State a c) where
  fromLabel inner = modify (setField @s =<< execState inner . getField @s)

newtype Setter a b = Setter { (.=) :: b -> State a () }

instance HasField s a b => IsLabel s (Setter a b) where
  fromLabel = Setter (modify . setField @s)

(&~) :: a -> State a () -> a
(&~) = flip execState

data Country = Country {name :: String, company :: Company} deriving Show
data Company = Company {name :: String, boss :: Employee, car :: Car} deriving Show
data Employee = Employee {name :: String, age :: Integer, empCar :: Car} deriving Show
data Car = Car {name :: String} deriving Show

f :: Integer -> Country -> Country
f i r =
  r &~ do
    #company do
        #boss do
            #age .= i
        #car do
            #name .= "new"
        #name .= "cmp"
    #name .= "ccc"

```

The [deluxe version](https://play.haskell.org/saved/W3ERhyAS) (requires 9.6+) even allows you to write:

```hs
  r &~ do
    #company do
        #"boss.age" .= i
        #"car.name" .= "new"
        #name .= "cmp"
    #name .= "ccc"

```

Of course, there is the minor downside that neither actually works yet, but at least there’s a plan for them to work at some point.

---

<div class="post-metadata">

**Author:** ![AntC2](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/antc2/32/1872_2.png) [@AntC2](https://discourse.haskell.org/u/AntC2)\
**Post date:** [June 3, 2024, 2:05am UTC](https://discourse.haskell.org/t/new-notation-for-records/9663/22 "2024-06-03T02:05:31Z")

</div>

> [@BurningWitness](#):
>
> use dictionaries instead of tables for most of the functionality ([Elm conference video](https://www.youtube.com/watch?v=28OdemxhfbU) on it)

I’m afraid the purpose of pointing to that video was lost on me. The guy drivelled on about Java something. If I wanted a decent record system I wouldn’t be going to Java in the first place. So elm [I got this from the tutorial, not the talk] has [records built over javascript records](https://guide.elm-lang.org/core_language#:~:text=instead%20of%20tuples.-,Records,-A%20record%20can) (same as purescript). elmers appear not to nest records, but I see no reason why they couldn’t. Javanians do tend to nest records, but again I see no reason why they must.

> [@](#):
>
> some dictionary types can be spine-lazy

Again, I didn’t get this from the video, but talk of “dictionary” and “spine” suggests a single indexible attribute. And the video seemed to navigate by a key. This is an even older (and also rejected) structure of database: ‘indexed-sequential’. Rejected because a single indexing path is too restrictive. The Relational Model approach is to store the ‘flat’ tuples separately from any consideration of paths to access it. One application might navigate from `Country` to `Company` to `Employee`, but another might navigate from `Employee` to `Company`. Either way round of nesting (as per o.p.) can’t handle all the likely routes of navigation.

> “Those who cannot remember the past are condemned to repeat it.” – George Santayana,

(OK, I don’t suppose most round here are old enough to remember the [‘database wars’](https://optimizdba.com/the-database-wars-how-sql-came-out-on-top/), but it might occur to you databases are pretty much as old as commercial IT; asking a question from ‘Employee up’ rather than ‘Company down’ pre-dates commercial IT – there were rolodex cards; possibly some people have thought about the challenges.)

---

<div class="post-metadata">

**Author:** ![darkxero](https://avatars.discourse-cdn.com/v4/letter/d/f07891/32.png) [@darkxero](https://discourse.haskell.org/u/darkxero)\
**Post date:** [June 3, 2024, 4:15am UTC](https://discourse.haskell.org/t/new-notation-for-records/9663/23 "2024-06-03T04:15:24Z")

</div>

> [@rhendric](#):
>
> Of course, there is the minor downside that neither actually works yet, but at least there’s a plan for them to work at some point.

That’s amazing.

```haskell
infixl 4 &~

f :: Integer -> Country -> Country
f i r = r &~ #"company.boss.age" .= i
          &~ #"company.car.name" .= "new car"
          &~ #"company.name" .= "new company"
          &~ #"name" .= "new country"

```

Lens like.

---

<div class="post-metadata">

**Author:** ![olf](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/olf/32/3813_2.png) [@olf](https://discourse.haskell.org/u/olf)\
**Post date:** [June 3, 2024, 6:20am UTC](https://discourse.haskell.org/t/new-notation-for-records/9663/24 "2024-06-03T06:20:41Z")

</div>

Very cool. `State s` has the advantage over `Writer (Endo s)` that one can set and view at the same time.

---

<div class="post-metadata">

**Author:** ![danidiaz](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/danidiaz/32/92_2.png) [@danidiaz](https://discourse.haskell.org/u/danidiaz)\
**Post date:** [June 3, 2024, 6:26pm UTC](https://discourse.haskell.org/t/new-notation-for-records/9663/25 "2024-06-03T18:26:49Z")

</div>

Looks nice! Instead of relying on `OverloadedLabels` and parsing the `Symbol`s, an [alternative to the deluxe version](https://gist.github.com/danidiaz/c6fa2278c97c0b6c9928fe2aff259588) could repurpose `OverloadedRecordDot` in combination with two helper values `with` and `the`, to let you write something like

```haskell
f :: Integer -> Country -> Country
f i r =
  r &~ do
    with.company do
      the.boss.age .= i
      the.car.name .= "new"
      the.name .= "cmp"
    the.name .= "ccc"

```

---

<div class="post-metadata">

**Author:** ![mikeplus64](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/mikeplus64/32/3998_2.png) [@mikeplus64](https://discourse.haskell.org/u/mikeplus64)\
**Post date:** [June 4, 2024, 12:54am UTC](https://discourse.haskell.org/t/new-notation-for-records/9663/26 "2024-06-04T00:54:31Z")

</div>

Similar, using lenses, which will work today - [Lenses with OverloadedRecordDot](https://quasimal.com/posts/2022-02-23-overloadedrecorddot-is-evil.html) 🙂 With `optics` you can give every `Optic` a `HasField` fairly easily and avoid having the separate `the` thing, ironically bringing it back to how `lens` code looks (composition with `.`) at least for nested fields only.

---

<div class="post-metadata">

**Author:** ![darkxero](https://avatars.discourse-cdn.com/v4/letter/d/f07891/32.png) [@darkxero](https://discourse.haskell.org/u/darkxero)\
**Post date:** [June 4, 2024, 4:31am UTC](https://discourse.haskell.org/t/new-notation-for-records/9663/27 "2024-06-04T04:31:47Z")

</div>

Trying to write my own implementation of

```haskell
$(set ["countryCompany", "companyBoss", "employeeAge"]) i r

```

in Template Haskell to match

```haskell
g r i = r { countryCompany = (countryCompany r) { companyBoss = (companyBoss (countryCompany r)) { employeeAge = i } } }

```

but I just straight up suck at it. 😢

---

<div class="post-metadata">

**Author:** ![ikervagyok](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/ikervagyok/32/4199_2.png) [@ikervagyok](https://discourse.haskell.org/u/ikervagyok)\
**Post date:** [June 4, 2024, 12:42pm UTC](https://discourse.haskell.org/t/new-notation-for-records/9663/28 "2024-06-04T12:42:25Z")

</div>

Why not use a notation, similar to elm? It is a lot more human readable and is roughly as powerful as our current system. And it probably would still keep the door open for extensible/… Records.

Maybe I am alone on this but I dislike the reliance on lenses, arcane syntax and/or another use of do-notation. Why not a simple `{a | b = c}`, meaning the record a, where the field b has the value c.

(I know, there is a problem around Haskell’s records having a constructor opposed to elm’s, but that is probably a solvable issue)

---

<div class="post-metadata">

**Author:** ![danidiaz](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/danidiaz/32/92_2.png) [@danidiaz](https://discourse.haskell.org/u/danidiaz)\
**Post date:** [June 4, 2024, 6:10pm UTC](https://discourse.haskell.org/t/new-notation-for-records/9663/29 "2024-06-04T18:10:30Z")

</div>

In my opinion, once a typeclass like `SetField` exists, we should repurpose `OverloadedRecordDot` for setting, using techniques similar to the ones seen in this thread. I don’t think overloading existing setting syntax, or adding new baked-in syntax, would be necessary.

---

<div class="post-metadata">

**Author:** ![rhendric](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/rhendric/32/2689_2.png) [@rhendric](https://discourse.haskell.org/u/rhendric)\
**Post date:** [June 4, 2024, 7:02pm UTC](https://discourse.haskell.org/t/new-notation-for-records/9663/30 "2024-06-04T19:02:27Z")

</div>

OP said they were familiar with [`OverloadedRecordUpdate`](https://ghc.gitlab.haskell.org/ghc/doc/users_guide/exts/overloaded_record_update.html), but for everyone else, the simple syntax `r { foo.bar = 1 }` is already reserved and ready to use with an implementation of `setField` (which you can provide manually or [wait for GHC to supply](https://gitlab.haskell.org/ghc/ghc/-/issues/16232)).

---

<div class="post-metadata">

**Author:** ![darkxero](https://avatars.discourse-cdn.com/v4/letter/d/f07891/32.png) [@darkxero](https://discourse.haskell.org/u/darkxero)\
**Post date:** [June 4, 2024, 8:42pm UTC](https://discourse.haskell.org/t/new-notation-for-records/9663/31 "2024-06-04T20:42:54Z")

</div>

> [@ikervagyok](#):
>
> Why not use a notation, similar to elm? It is a lot more human readable and is roughly as powerful as our current system. And it probably would still keep the door open for extensible/… Records.

The notation is already Elm like.

```haskell
r { f = a, g = b }
--- vs
{ r | f = a, g = b }

```

What Elm has that Haskell (98) doesn’t is that it uses maps under the hood for record types and the fields are overloaded.

> [@ikervagyok](#):
>
> Maybe I am alone on this but I dislike the reliance on lenses, arcane syntax and/or another use of do-notation. Why not a simple `{a | b = c}`, meaning the record a, where the field b has the value c.
> 
> (I know, there is a problem around Haskell’s records having a constructor opposed to elm’s, but that is probably a solvable issue)

[Elm has actually the same problem I’m bringing up.](https://discourse.elm-lang.org/t/updating-nested-records-again/1488) And they don’t have typeclasses or Template Elm. If they want to use elm-monocle they have to type the lens wiring by hand. The “official response” is that updating deeply nested records is an antipattern and all your data structures should be flat. Or hidden in modules with all the setter pipeline wiring hidden.

---

<div class="post-metadata">

**Author:** ![AntC2](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/antc2/32/1872_2.png) [@AntC2](https://discourse.haskell.org/u/AntC2)\
**Post date:** [June 4, 2024, 8:55pm UTC](https://discourse.haskell.org/t/new-notation-for-records/9663/32 "2024-06-04T20:55:07Z")

</div>

> [@ikervagyok](#):
>
> Why not use a notation, similar to elm? …  
> … Why not a simple `{a | b = c}`, meaning the record a, where the field b has the value c.

Yes I like elm’s notation. The difficulty introducing it into Haskell is backwards compatibility. Consider:

```haskell
x = foo { a | b = c }
y = Bar { d | e = f }

```

For the first, the compiler seeing `foo { ... }` will think that’s a record update to value `foo`. For the second, the compiler seeing `Bar { ... }` will think that’s constructor `Bar` using record syntax – not `Bar` applied positionally to a single value, that happens to be a record update.

So the choice of notation will have to be module-wide and backwards-incompatible. All those Libraries (including `Base/Prelude`) will never change. So Haskellers will need to read both notations.

---

<div class="post-metadata">

**Author:** ![arybczak](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/arybczak/32/2527_2.png) [@arybczak](https://discourse.haskell.org/u/arybczak)\
**Post date:** [June 5, 2024, 1:19am UTC](https://discourse.haskell.org/t/new-notation-for-records/9663/33 "2024-06-05T01:19:17Z")

</div>

Btw, there is a [dormant GHC proposal for introducing row types](https://github.com/ghc-proposals/ghc-proposals/pull/180), but looks like there are some non-trivial unsolved questions because the author decided to turn it into a PhD thesis.

---

<div class="post-metadata">

**Author:** ![darkxero](https://avatars.discourse-cdn.com/v4/letter/d/f07891/32.png) [@darkxero](https://discourse.haskell.org/u/darkxero)\
**Post date:** [June 5, 2024, 4:05am UTC](https://discourse.haskell.org/t/new-notation-for-records/9663/34 "2024-06-05T04:05:21Z")

</div>

> [@darkxero](#):
>
> but I just straight up suck at it.

I got better.

Setter.hs:

```haskell
{-# LANGUAGE TemplateHaskell #-}

module Setter where

import Language.Haskell.TH

set :: [Name] -> Q Exp
set names = return $ LamE [VarP a, VarP r] (go names (VarE r))
    where
        a = mkName "a"
        r = mkName "r"
        go ns acc = case ns of
            [n] -> RecUpdE acc [(n, VarE a)]
            n : zs -> RecUpdE acc [(n, go zs (AppE (VarE n) acc))]
            _ -> error "no fields?"

```

And the example.

Main.hs:

```haskell
{-# LANGUAGE TemplateHaskell #-}

import Setter

data Country = Country { countryName :: String, countryCompany :: Company } deriving (Show)
data Company = Company { companyName :: String, companyBoss :: Employee, companyCar :: Car } deriving (Show)
data Employee = Employee { employeeName :: String, employeeAge :: Integer, employeeCar :: Car } deriving (Show)
data Car = Car { carName :: String } deriving (Show)

example :: Integer -> Country -> Country
example i = $(set ['countryCompany, 'companyBoss, 'employeeAge]) i
          . $(set ['countryCompany, 'companyCar, 'carName]) "new"
          . $(set ['countryCompany, 'companyName]) "cmp"
          . $(set ['countryName]) "ccc"

main :: IO ()
main = do
    let car1 = Car { carName = "car1" }
    let car2 = Car { carName = "car2" }
    let boss = Employee { employeeName = "Carl", employeeAge = 30, employeeCar = car2 }
    let comp = Company { companyName = "Acme", companyBoss = boss, companyCar = car1 }
    let ctry = Country { countryName = "Scotland", countryCompany = comp }
    print ctry
    print $ example 420 ctry

```

Lenses at home.

---

<div class="post-metadata">

**Author:** ![rhendric](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/rhendric/32/2689_2.png) [@rhendric](https://discourse.haskell.org/u/rhendric)\
**Post date:** [June 6, 2024, 9:20pm UTC](https://discourse.haskell.org/t/new-notation-for-records/9663/35 "2024-06-06T21:20:06Z")

</div>

Out of curiosity, if you’re already eating Template Haskell, what’s stopping you from using actual lenses?

---

<div class="post-metadata">

**Author:** ![darkxero](https://avatars.discourse-cdn.com/v4/letter/d/f07891/32.png) [@darkxero](https://discourse.haskell.org/u/darkxero)\
**Post date:** [June 6, 2024, 9:57pm UTC](https://discourse.haskell.org/t/new-notation-for-records/9663/36 "2024-06-06T21:57:50Z")

</div>

> [@rhendric](#):
>
> if you’re already eating Template Haskell, what’s stopping you from using actual lenses?

Actually? Nothing, really.

Pros of lenses:

- nuclear bomb solution
- versatile, composable

Cons of lenses:

- nuclear bomb solution, do you really need it?
- 30 package dependency
- pollutes the namespace with redundancy on the record accessors with lens counterpart, introduces lens types when initially you just had the record types and the accessors

Pros of my silly script:

- uses the basics, Haskell98 compatible
- you drop it off on a file and that’s it, it just works
- only depends on template-haskell which comes with GHC out of the box, enable the extension and your script is good to go
- doesn’t need AllowAmbiguousTypes, FunctionalDependencies, UndecidableInstances or others

Cons of my silly script:

- generates boilerplate which may or may not be optimized to anything sensible
- you can only set one thing at a time which generates even more boilerplate
- reducing all the boilerplate introduces quasiquotation and you might as well write setters with the full record notation or use lenses if you’re going to bother with a parser
- why not use [vinyl](https://hackage.haskell.org/package/vinyl) or [record](https://hackage.haskell.org/package/record)?

I will keep it on a file of utils, for scripts.

---

<div class="post-metadata">

**Author:** ![arybczak](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/arybczak/32/2527_2.png) [@arybczak](https://discourse.haskell.org/u/arybczak)\
**Post date:** [June 7, 2024, 2:45am UTC](https://discourse.haskell.org/t/new-notation-for-records/9663/37 "2024-06-07T02:45:48Z")

</div>

See [Optics.Generic](https://hackage.haskell.org/package/optics-core-0.4.1.1/docs/Optics-Generic.html) and [Optics.Label](https://hackage.haskell.org/package/optics-core-0.4.1.1/docs/Optics-Label.html). `lens` isn’t the only library with a solution and this gets rid of (most of?) your cons.

---

<div class="post-metadata">

**Author:** ![Ambrose](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/ambrose/32/5672_2.png) [@Ambrose](https://discourse.haskell.org/u/Ambrose)\
**Post date:** [June 7, 2024, 5:12am UTC](https://discourse.haskell.org/t/new-notation-for-records/9663/38 "2024-06-07T05:12:04Z")

</div>

optics has an impressively small dependency graph. i think they all ship with ghc? de facto stdlib stuff

---

<div class="post-metadata">

**Author:** ![darkxero](https://avatars.discourse-cdn.com/v4/letter/d/f07891/32.png) [@darkxero](https://discourse.haskell.org/u/darkxero)\
**Post date:** [June 8, 2024, 2:06am UTC](https://discourse.haskell.org/t/new-notation-for-records/9663/39 "2024-06-08T02:06:30Z")

</div>

It is in fact a pretty nifty alternative to lens.

```haskell
{-# LANGUAGE DuplicateRecordFields, TemplateHaskell, DataKinds, TypeFamilies, UndecidableInstances, OverloadedLabels #-}

import Optics
import Optics.TH

data Country = Country { name :: String, company :: Company } deriving (Show)
data Company = Company { name :: String, boss :: Employee, car :: Car } deriving (Show)
data Employee = Employee { name :: String, age :: Integer, car :: Car } deriving (Show)
data Car = Car { name :: String } deriving (Show)

makeFieldLabelsNoPrefix ''Country
makeFieldLabelsNoPrefix ''Company
makeFieldLabelsNoPrefix ''Employee
makeFieldLabelsNoPrefix ''Car

example :: Integer -> Country -> Country
example i = set (#company % #boss % #age) i
          . set (#company % #car % #name) "Batmobile"
          . set (#company % #name) "Aperture"
          . set (#name) "Canada"

main :: IO ()
main = do
    let car1 = Car { name = "car1" }
    let car2 = Car { name = "car2" }
    let boss = Employee { name = "Carl", age = 30, car = car2 }
    let comp = Company { name = "Acme", boss = boss, car = car1 }
    let ctry = Country { name = "Scotland", company = comp }
    print ctry
    print $ example 420 ctry

```

Makes replacing lenses easy and also quite as handy. You can choose Generics, TH, symbol spaghetti or lens like interface.

---

<div class="post-metadata">

**Author:** ![darkxero](https://avatars.discourse-cdn.com/v4/letter/d/f07891/32.png) [@darkxero](https://discourse.haskell.org/u/darkxero)\
**Post date:** [June 11, 2024, 11:48pm UTC](https://discourse.haskell.org/t/new-notation-for-records/9663/40 "2024-06-11T23:48:27Z")

</div>

> [@rhendric](#):
>
> `setField`

For Template Haskell learning purposes I went and [downloaded this](https://hackage.haskell.org/package/record-hasfield-1.0.1/docs/GHC-Records-Compat.html) and used Template Haskell to provide an overloading function to generate all the boilerplate for a record that wants to use OverloadedRecordUpdate.

Experimental.hs:

```haskell
{-# LANGUAGE AllowAmbiguousTypes, FunctionalDependencies, TemplateHaskell #-}

module Experimental (
    HasField (..),
    getField,
    setField,
    overloadRecords
) where

import Language.Haskell.TH

class HasField x r a | x r -> a where
    -- | Function to get and set a field in a record.
    hasField :: r -> (a -> r, a)

getField :: forall x r a. (HasField x r a) => r -> a
getField = snd . hasField @x

setField :: forall x r a. (HasField x r a) => r -> a -> r
setField = fst . hasField @x

overloadRecords :: [Name] -> Q [Dec]
overloadRecords tNames = do
    foldr (\tName qDecs -> do
        nDecs <- overloadRecord tName
        pDecs <- qDecs
        return (pDecs ++ nDecs)) (return []) tNames

overloadRecord :: Name -> Q [Dec]
overloadRecord tName = do
    tDec <- getTypeDec
    dCons <- getDataCons tDec
    foldr (\a b -> do
        xd <- inst a (getCompleteType tDec) dCons
        xp <- b
        return (xd ++ xp)) (return []) (agroupFs (extractFs dCons) [])
    where
        getTypeDec = do
            i <- reify tName
            case i of
                TyConI d -> return d
                _ -> error $ "unsupported type " ++ show tName
        getDataCons td = case td of
            DataD _ _ _ _ cs _ -> return cs
            _ -> error $ "no cons for " ++ show tName
        getCompleteType td = case td of
            DataD _ t ks _ _ _ -> mf (fmap kf ks) t
            _ -> error $ "no kinds for " ++ show tName
            where
                kf (PlainTV n _) = n
                kf (KindedTV n _ _) = n
                mf ps t = case ps of
                    [x] -> appT (conT t) (varT x)
                    x : xs -> appT (mf xs t) (varT x)
                    _ -> conT t
        extractFs rs = case rs of
            r : xs -> z r ++ extractFs xs
            _ -> []
            where
                z (RecC c fs) = fmap (\(n, _, t) -> (n, t, c)) fs
                z _ = []
        agroupFs xs acc = case xs of
            [] -> acc
            (n, t, c) : ys -> inc (\(n2, t2, c2) -> (n2, t2, c : c2)) (\(n2, _, _) -> n2 == n) (n, t, [c]) (agroupFs ys acc)
            where
                inc z h x zs = case span (not . h) zs of
                    (_, []) -> x : zs
                    (a, b : bs) -> a ++ (z b : bs)
        inst (fieldName, fieldType, dataCons) fullT dCons =
            [d|
                instance HasField $(litT (strTyLit fieldStr)) $(fullT) $(return fieldType) where
                    hasField r = $(caseE [| r |] matches)
              |]
            where
                fieldStr = reverse . takeWhile (/= '.') . reverse . show $ fieldName
                o = "v"
                matches = fmap (\dcn -> let vats = fmap varP (mt dCons dcn)
                                            pats = conP dcn vats
                                            body = normalB [| (\x -> $(vt dCons dcn [| x |]), $(varE (mkName o))) |]
                                        in match pats body []) dataCons ++ [fin]
                    where
                        err = "No match in record selector " ++ fieldStr
                        fin = match wildP (normalB [| error $(litE (stringL err)) |]) []
                mt rs consName = case rs of
                    (RecC c fs) : xs -> if consName == c then p fs 0 else mt xs consName
                    _ -> []
                    where
                        p ((n, _, _) : js) num = (if n == fieldName then mkName o else mkName (o ++ show num)) : p js (num + 1)
                        p _ _ = []
                vt rs consName xq = case rs of
                    (RecC c fs) : xs -> if consName == c then p (prep fs) else vt xs consName xq
                    _ -> conE consName
                    where
                        prep = reverse . zip [0..] . fmap (\(n, _, _) -> n)
                        p [] = conE consName
                        p ((i, x) : xs) = appE (p xs) (if x == fieldName then xq else varE (mkName (o ++ show i)))

{-
instance "name" Country String where
    hasField r = case r of
        Country v v1 -> (\x -> Country x v1, v)
        AlienCountry v v1 -> (\x -> Country x v1, v)
        _ -> error "no match"
-}

```

Main.hs:

```haskell
{-# LANGUAGE DuplicateRecordFields, OverloadedRecordDot, OverloadedRecordUpdate, RebindableSyntax #-}
{-# LANGUAGE DataKinds, TemplateHaskell #-}

import Prelude
import Experimental

data Country a = Country { name :: String, company :: Company } | AlienCountry { name :: String, poly :: a } deriving (Show)
data Company = Company { name :: String, boss :: Employee, car :: Car } deriving (Show)
data Employee = Employee { name :: String, age :: Integer, car :: Car } deriving (Show)
data Car = Car { name :: String } deriving (Show)

overloadRecords [''Country, ''Company, ''Employee, ''Car]

main :: IO ()
main = do
    let car1 = Car { name = "car1" }
    let car2 = Car { name = "car2" }
    let boss = Employee { name = "Carl", age = 30, car = car2 }
    let comp = Company { name = "Acme", boss = boss, car = car1 }
    let ctry = Country { name = "Scotland", company = comp }
    print (ctry :: Country String)
    print $ ctry.company.boss.age
    let uc = ctry { company.boss.age = 420, company.boss.car.name = "new car" }
    print (uc :: Country String)
    let alien = AlienCountry { name = "Canada", poly = Company { name = "Alien Corp", boss = boss, car = car2 }}
    print alien
    print $ alien.poly.name
    let ac = alien { name = "Brazil", poly.boss.age = 999 }
    print ac

```

Only to realize I was using the old proposal and not the new one and I can’t just flip the types to finally see the “typechecked only” example work (because of ambiguous errors).

The good thing is that my example just works, like how original records do. Which is good… and bad. Good because it works, with the simple ergonomics that implies. Bad because there’s no type checking that the member you are calling is effectively part of the value (e.g asking for the company of AlienCountry is valid in old Haskell, valid here, and gives a runtime error if you do so).

Now I’m wondering how it would look like with optics.

[Previous page](https://discourse.haskell.org/t/new-notation-for-records/9663.md?page=1)

[Next page](https://discourse.haskell.org/t/new-notation-for-records/9663.md?page=3)
