How to read the syntax `Typ{..}` in haskell?

  • A+

While reading library code here I have noticed a really weird looking syntax that I can't make sense of:

momenta     :: (KnownNat m, KnownNat n)     => System m n     -> Config n     -> R n momenta Sys{..} Cfg{..} = tr j #> diag _sysInertia #> j #> cfgVelocities   where     j = _sysJacobian cfgPositions 

The relevant definitions of System includes a record { _sysJacobian :: R n -> L m n }, and { cfgVelocities :: R n } is part of the record declaration of Config so I believe I know what the code does, I think the code is quite readable, props to the author.

The question is: what is this syntax called and how exactly can I use it?

In short: it is an extension of GHC called RecordWildCards.

In Haskell you can use record syntax to define data types. For example:

data Foo = Bar { foo :: Int, bar :: String } | Qux { foo :: Int, qux :: Int } 

We can then pattern match on the data constructor, and match zero or more parameters, for example:

someFunction :: Int -> Foo -> Foo someFunction dd (Bar {foo=x}) = dd + x someFunction dd (Qux {foo=x, qux=y}) = dd + x + y 

But it can happen that we need access to a large amount (or even all) parameters. Like for example:

someOtherFunction :: Foo -> Int someOtherFunction (Bar {foo=foo, bar=bar}) = foo someOtherFunction (Qux {foo=foo, qux=qux}) = foo + qux 

In case the number of parameters is rather large, then this becomes cumbersome. There is an extension RecordWildCards:

{-# LANGUAGE RecordWildCards #-} 

this will implicitly write for every parameter foo, foo=foo if you write {..} when we do record pattern matching.

So we can then write:

someOtherFunction :: Foo -> Int someOtherFunction (Bar {..}) = foo someOtherFunction (Qux {..}) = foo + qux 

So here the compiler implicitly pattern matched all parameters with a variable with the same name, such that we can access those parameters without explicit pattern matching, nor by using getters.

The advantage is thus that we save a lot on large code chunks that have to be written manually. A downside is however the fact that the parameters are no longer explicitly and hence the code is harder to understand. We see the use of parameters for which there exist actually getter counterparts, and thus it can introduce some confusion.

Like @leftroundabout says, probably lenses can do the trick as well, and it will prevent introducing variables that basically shadow getters, etc.

You can also merge the RecordWildCards with pattern matching on parameters, for example:

someOtherFunction :: Foo -> Int someOtherFunction (Bar {bar=[], ..}) = foo someOtherFunction (Bar {..}) = foo + 42 someOtherFunction (Qux {..}) = foo + qux 

So here in case the bar parameter of a Foo instance with a Bar data constructor is the empty string, we return the foo value, otherwise we add 42 to it.


:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: