Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

syntax

haskell

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
    --  ^^^^^^^^^^^^^^^ the syntax in question
  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?

like image 220
WorldSEnder Avatar asked May 10 '18 08:05

WorldSEnder


2 Answers

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.

like image 157
Willem Van Onsem Avatar answered Oct 19 '22 17:10

Willem Van Onsem


It's the RecordWildCards syntax extension. From the docs:

For records with many fields, it can be tiresome to write out each field individually in a record pattern ... Record wildcard syntax permits a ".." in a record pattern, where each elided field f is replaced by the pattern f = f ... The expansion is purely syntactic, so the record wildcard expression refers to the nearest enclosing variables that are spelled the same as the omitted field names.

Basically it brings the fields of a record into scope.

It is particularly useful when writing encoders/decoders (e.g. Aeson), but should be used sparingly in the interest of code clarity.

like image 22
Jordan Mackie Avatar answered Oct 19 '22 16:10

Jordan Mackie