Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What does (..) mean?

I'm trying to learn Haskell.

I'm reading the code on here[1]. I just copy and past some part of the code from lines:46 and 298-300.

Question: What does (..) mean?

I Hoggled it but I got no result.

module Pos.Core.Types(
-- something here

 SharedSeed (..) -- what does this mean?

) where


newtype SharedSeed = SharedSeed
{ getSharedSeed :: ByteString
} deriving (Show, Eq, Ord, Generic, NFData, Typeable)

[1] https://github.com/input-output-hk/cardano-sl/blob/master/core/Pos/Core/Types.hs

like image 523
user153438 Avatar asked Aug 02 '17 11:08

user153438


2 Answers

The syntax of import/export lists has not much to do with the syntax of Haskell itself. It's just a comma-separated listing of everything you want to export from your module. Now, there's a problem there because Haskell really has two languages with symbols that may have the same name. This is particularly common with newtypes like the one in your example: you have a type-level name SharedSeed :: *, and also a value-level name (data constructor) SharedSeed :: ByteString -> SharedSeed.

This only happens with uppercase names, because lowercase at type level are always local type variables. Thus the convention in export lists that uppercase names refer to types.

But just exporting the type does not allow users to actually construct values of that type. That's often prudent: not all internal-representation values might make legal values of the newtype (see Bartek's example), so then it's better to only export a safe smart constructor instead of the unsafe data constructor.

But other times, you do want to make the data constructor available, certainly for multi-constructor types like Maybe. To that end, export lists have three syntaxes you can use:

module Pos.Core.Types(
  • SharedSeed (SharedSeed) will export the constructor SharedSeed. In this case that's of course the only constructor anyway, but if there were other constructors they would not be exported with this syntax.

  • SharedSeed (..) will export all constructors. Again, in this case that's only SharedSeed, but for e.g. Maybe it would export both Nothing and Just.

  • pattern SharedSeed will export ShareSeed as a standalone pattern (independent of the export of the ShareSeed type). This requires the -XPatternSynonyms extension.

    )
    
like image 172
leftaroundabout Avatar answered Sep 21 '22 14:09

leftaroundabout


It means "export all constructors and record fields for this data type".

When writing a module export list, there's three4 ways you can export a data type:

module ModuleD(
    D,       -- just the type, no constructors
    D(..),   -- the type and all its constructors
    D(DA)    -- the type and the specific constructor
    ) where

data D = DA A | DB B

If you don't export any constructors, the type, well, can't be constructed, at least directly. This is useful if you e.g. want to enforce some runtime invariants on the data type:

module Even (evenInt, toInt) where

newtype EvenInt = EvenInt Int deriving (Show, Eq)

evenInt :: Int -> Maybe EvenInt
evenInt x = if x `mod` 2 == 0 then Just x else Nothing

toInt :: EvenInt -> Int
toInt (EvenInt x) = x

The caller code can now use this type, but only in the allowed manner:

x = evenInt 2
putStrLn $ if isJust x then show . toInt . fromJust $ x else "Not even!"

As a side note, toInt is usually implemented indirectly via the record syntax for convenience:

data EvenInt = EvenInt { toInt :: Int }

4 See @leftaroundabout's answer

like image 40
Bartek Banachewicz Avatar answered Sep 22 '22 14:09

Bartek Banachewicz