The module Type.hs
defines the homonym newtype
and exports only its type constructor, but not the value constructor, to avoid exposing the detail; it also provides a constructor function makeType
to balance the lack of value ctor. Why do I need to wrap a String
in a new type? Because I want it to be more than a String
; in my specific case, Type
is actually called Line
, and what corresponds to makeType
enforces that it contains only one \n
, as the last character. A newtype
seemed the most obvious choice to me. If this is not the case, forgive me: I'm learning.
module Type (Type, makeType) where
newtype Type = Type String
makeType :: String -> Type
makeType = Type
In order to show a value of type Type
the way I like (for instance, given my actual usecase of Line
, I might want to represent \n
with a nice unicode character, or with the sequence <NL>
, or whatever), I created another module TypeShow.hs
, which I later (in the attempt of doing what I'm describing) I edited by adding some pragmas. Why another module? Because I guess the way something works inside and the way I show it to screen are two separete aspects. Am I wrong?
{-# LANGUAGE FlexibleInstances #-}
module TypeShow () where
import Type
instance Show Type where
show = const "Type"
-- the following instance came later, see below why
instance {-# OVERLAPS #-} Show (Maybe Type) where
show (Just t) = show t
show _ = ""
Beside this pair of modules (which describe the core of Type
, and how it should be shown), I created other similar pairs Type1
/Type1Show
, Type2
/Type2Show
, which all wrap a String
to represent and show other String
-like entities.
For other reasons, I also needed another type which wraps an optional value, which can be of type Type
, Type1
, or any other type, so I wrote this module
module Wrapper (Wrapper, makeWrapper, getInside) where
newtype Wrapper a = Wrapper { getInside :: Maybe a }
makeWrapper :: a -> Wrapper a
makeWrapper = Wrapper . Just
(In reality Wrapper
actually wraps more than one Type
value, but I'll avoid putting more details then necessary; if the following is stupid exactly because I'm wrapping only one Type
value in Wrapper
, then please consider it's wrapping more than one, in reality.) Again, here I tried to hide the details of Wrapper
while providing makeWrapper
to make one, and getInside
to have a "controlled" access to its inside.
I also wanted to show this on screen, so I created a corresponding WrapperShow.hs
module, so that Wrapper
's show
method relies on the content's show
method.
module WrapperShow () where
import Wrapper
instance Show a => Show (Wrapper a) where
show = show . getInside
At this point, however, when the type a
is a Maybe Type
, I wanted to show the content of the Wrapper
printing an empty string instead of Nothing
, or the content of the Just
; therefore I wrote the instance Show (Maybe Type)
that I commented above.
Given this, Type "hello"
and Just $ Type "hello"
are both correcly shown as Type
, but Wrapper $ Just $ Type "hello"
is displayed as Just Type
, just like it's using Maybe
's original instance of Show
, irrespective of the fact that for this specific type inside the Maybe
(the Type
) I've customized the Show
instance.