Search code examples
haskellnotation

What do bracketed double dots mean in Haskell?


I understand that .. can be used in ranges--i.e., [1..3] == [1,2,3], and [10..] is an infinite list starting at 10.

However, recently I've started seeing these double dots inside brackets too. Either as (..) or {..}.

For example, an import statement can read import Colog (HasLog (..))

My first intuition here is to think that this means that HasLog has several components and we are explicitly importing all of them. But how does that differ from simply importing HasLog, without the (..)?

Further, how does (..) differ from {..}?


Solution

  • (..) and {..} are both ways to bring names associated with data types in scope, but they're very different and used in different contexts.

    • The (..) syntax is specifically used for import and export lists. It's part of the syntax for declaring which constructors and/or record fields of a data type you want to import/export. For example, if I have a module

      module FooM where
      data Foo = F0 | F1 | F2
      data Bar = Bar { b0 :: Int, b1 :: Char }
      

      then this compiles

      import FooM ( Foo(F1,F2), Bar(b1) )
      fu = F1
      ba = b1
      

      but this doesn't

      import FooM ( Foo(F1,F2) )
      fu = F0
      

      because I've only imported the F1 and F2 constructors, not F0. If I write

      import FooM ( Foo(..), Bar(..) )
      fu = F0
      ba = b0
      

      it also works, because this imports all constructors and record labels. By contrast, with

      import FooM ( Foo )
      fu = F0
      

      you don't import any constructors or record fields at all, but only Foo as an opaque type, so here fu = F0 also won't compile. (This often exploited in export lists, if you want the inner structure of a data type to be “private” and only manipulable with utility functions, smart constructors etc..)

    • {..} is part of the RecordWildCards extension. In a nutshell, it turns the names of all record labels that would apply to a parameter into locally-scoped variables.
      I would advice against using this, it's one of those extensions that tried to address the shortcomings of Haskell's record system – with IMO not much success. Better to either use records in the traditional way, or auto-generate some lenses and use these to completely avoid the record issues.