2

In Haskell I try to call the following two newtypes that have I have declared.

Why does this work:

newtype CharList = CharList { get2CharList :: [Char] } deriving (Eq, Show)   

ghci> CharList "some"    
CharList {get2CharList = "some"}

When this does not work:

newtype Pair a b = Pair { createPair :: (a, b) } deriving (Eq, Show)

ghci> Pair 2 4
<interactive>:13:1: error:
    * Couldn't match expected type: t0 -> t
                  with actual type: Pair a0 b0
    * The function `Pair' is applied to two value arguments,
        but its type `(a0, b0) -> Pair a0 b0' has only one
      In the expression: Pair 2 4
      In an equation for `it': it = Pair 2 4
    * Relevant bindings include it :: t (bound at <interactive>:13:1)

Does it require a Monad to work as seen here: The Writer monad and its type declaration And will I necessarily then have to constraint the type when calling the newtype

Furthermore, how can I go about it if I want a newtype to take even more arguments such as 3?

Piskator
  • 605
  • 1
  • 9
  • You can always create a pattern synonym, `pattern ThePair a b = Pair (a, b)` – Iceland_jack Nov 13 '22 at 16:03
  • I am not sure I understand. What will that do exactly? – Piskator Nov 13 '22 at 16:34
  • With [PatternSynonyms](https://ghc.gitlab.haskell.org/ghc/doc/users_guide/exts/pattern_synonyms.html?highlight=patternsynonyms#extension-PatternSynonyms) you can abstract over data constructors. You can still use a newtype with an uncurried constructor `Pair :: (a, b) -> Pair a b` but you can define a curried variety of it through the pattern synonym above: `ThePair :: a -> b -> Pair a b`. That way you can leverage `GeneralizedNewtypeDeriving` while having a more ergonomic constructor. – Iceland_jack Nov 13 '22 at 17:23
  • The link you posted seems to be broken. But what you are suggesting is that I can create a patternsynosym as specified and it it willl allow me to call (e.g.) `ThePair 2 4` and `Pair 2 4` will be returned. – Piskator Nov 14 '22 at 05:46
  • The link works for me, https://ghc.gitlab.haskell.org/ghc/doc/users_guide/exts/pattern_synonyms.html. Using the code you wrote you can not write `Pair 2 4`, you must write a tuple `Pair (2, 4)`. The my suggestion you can write `ThePair 2 4` and it translates it into `Pair (2, 4)`. – Iceland_jack Nov 14 '22 at 10:46
  • 1
    A newtype only wraps a single type. If there is no need to use a newtype then just write: `data Pair a b = MkPair a b`. That allows you to write the constructor like you wanted `MkPair 2 4`. Or you can define a pair of 3 values `data Pair3 a b c = MkPair3 a b c` and you can construct it directly: `MkPair3 True True False`. – Iceland_jack Nov 14 '22 at 10:49
  • 1
    Ok, it seems I have understood it correctly then. Thank you. NB: Weird the new link you have posted works for me how as well (both versions). – Piskator Nov 14 '22 at 10:49

1 Answers1

5

The Pair newtype is declared as containing (or wrapping) a tuple of values, so that's what you must supply to create a value:

ghci> Pair (2, 4)
Pair {createPair = (2,4)}

You can extract the tuple from a Pair value with createPair:

ghci> createPair $ Pair (2, 4)
(2,4)

While the type is given as Pair a b (without brackets or comma), the data constructor requires a tuple. The type is declared to the left of the equal sign, and the data constructor is to the right.

The syntax Pair (2, 4) is just shorthand for the full data constructor, which is also possible:

ghci> Pair { createPair = (2, 4) }
Pair {createPair = (2,4)}

You can create wrappers with more type arguments in the same way:

newtype Triple a b c = T { getTriple :: (a, b, c) } deriving (Eq, Show)

This example also showcases something that may help understanding the above. The name of the type doesn't have to be the same as the name of the data constructor. Here, the name of the type is Triple, while the name of the data constructor is T.

You create a value of Triple using the data constructor T:

ghci> T (1, "foo", True)
T {getTriple = (1,"foo",True)}

The type of that expression is:

ghci> :t T (1, "foo", True)
T (1, "foo", True) :: Num a => Triple a String Bool
Mark Seemann
  • 225,310
  • 48
  • 427
  • 736