What does (..) mean?
Asked Answered
E

2

6

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

Example answered 2/8, 2017 at 11:52 Comment(3)
Relevant answerAnnabellannabella
For anybody else landing on this question via the search engine: .. also has another, unrelated meaning in lists: [1..5]. Here it's syntax sugar for enumFromTo. You can probably find out about this if you search for "list comprehensions".Teressaterete
Not just enumFromTo: that whole family of Enum functions have shorthand notations using .. in different ways.Izaguirre
A
5

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

Annabellannabella answered 2/8, 2017 at 11:53 Comment(6)
Thanks for the answer, what if we don't use it?Example
@Example Then only the type itself is exported, but users can't see the constructors. It's an abstract type.Soluble
@Soluble thanks. Am I right it can still use the constructor?Example
@Example I've added an example, hope it clarifies things a bit.Annabellannabella
export all constructors for this data type ← not only constructors, record fields as well.Hyperparathyroidism
In general, you should either export all the constructors or none of them. The same syntax is used for class methods. It can make sense to (re-)export only some class methods.Brasilin
D
5

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.

    )
    
Dede answered 2/8, 2017 at 12:10 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.