Skip to content

Commit

Permalink
Exposing internal functionality (Internal modules)
Browse files Browse the repository at this point in the history
When a user runs into trouble with using a library the best course of
actions usually are to open an issue in the repository or submit a pull
request, but sometimes the user need things to work *now*.

We admit that we are not perfect and can't think of all use cases for our
library. Sometimes the restrictions we add are too great and may limit
the usage of advanced users that know how things work under the hood and
need certain functionality to present in our library.

For that we can use internal modules to provide some flexibility for
advanced users.

For that we use internal modules, which are not a language concept but
rather a (fairly common) design pattern in Haskell.

Internal modules are named `Internal` and are considered risky to use
by convention. These modules will export all of the functionality in a
module, and we will add another module without the `Internal` suffix
which will import from our internal module and only export the api
we'd like to export.

Internal modules should be considered unstable and risky to use. If
you end up using one, make sure to open a ticket in the library after
the storm has passed!
  • Loading branch information
soupi committed Nov 7, 2020
1 parent aeee32c commit 379fbb0
Show file tree
Hide file tree
Showing 2 changed files with 72 additions and 68 deletions.
69 changes: 1 addition & 68 deletions Html.hs
Original file line number Diff line number Diff line change
Expand Up @@ -10,71 +10,4 @@ module Html
)
where

-----------
-- Types --
-----------

newtype Html
= Html String

type HtmlTitle
= String

type HtmlBody
= [HtmlBodyContent]

newtype HtmlBodyContent
= HtmlBodyContent String

----------
-- EDSL --
----------

html_ :: HtmlTitle -> HtmlBody -> Html
html_ title content =
Html
( el "html"
( concat
[ el "head" (el "title" (escape title))
, el "body" (concat (map getBodyContentString content))
]
)
)

p_ :: String -> HtmlBodyContent
p_ = HtmlBodyContent . el "p" . escape

h1_ :: String -> HtmlBodyContent
h1_ = HtmlBodyContent . el "h1" . escape

------------
-- Render --
------------

render :: Html -> String
render (Html str) = str

-----------
-- Utils --
-----------

el :: String -> String -> String
el tag content =
"<" <> tag <> ">" <> content <> "</" <> tag <> ">"

getBodyContentString :: HtmlBodyContent -> String
getBodyContentString (HtmlBodyContent str) = str

escape :: String -> String
escape =
let
escapeChar c =
case c of
'<' -> "&lt;"
'>' -> "&gt;"
'&' -> "&amp;"
'"' -> "&quot;"
'\'' -> "&#39;"
_ -> [c]
in
concat . map escapeChar
import Html.Internal
71 changes: 71 additions & 0 deletions Html/Internal.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
-- Warning: Internal module - usage at your own risk!
module Html.Internal where

-----------
-- Types --
-----------

newtype Html
= Html String

type HtmlTitle
= String

type HtmlBody
= [HtmlBodyContent]

newtype HtmlBodyContent
= HtmlBodyContent String

----------
-- EDSL --
----------

html_ :: HtmlTitle -> HtmlBody -> Html
html_ title content =
Html
( el "html"
( concat
[ el "head" (el "title" (escape title))
, el "body" (concat (map getBodyContentString content))
]
)
)

p_ :: String -> HtmlBodyContent
p_ = HtmlBodyContent . el "p" . escape

h1_ :: String -> HtmlBodyContent
h1_ = HtmlBodyContent . el "h1" . escape

------------
-- Render --
------------

render :: Html -> String
render (Html str) = str

-----------
-- Utils --
-----------

el :: String -> String -> String
el tag content =
"<" <> tag <> ">" <> content <> "</" <> tag <> ">"

getBodyContentString :: HtmlBodyContent -> String
getBodyContentString (HtmlBodyContent str) = str

escape :: String -> String
escape =
let
escapeChar c =
case c of
'<' -> "&lt;"
'>' -> "&gt;"
'&' -> "&amp;"
'"' -> "&quot;"
'\'' -> "&#39;"
_ -> [c]
in
concat . map escapeChar

0 comments on commit 379fbb0

Please sign in to comment.