-
Notifications
You must be signed in to change notification settings - Fork 48
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Exposing internal functionality (Internal modules)
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
Showing
2 changed files
with
72 additions
and
68 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
'<' -> "<" | ||
'>' -> ">" | ||
'&' -> "&" | ||
'"' -> """ | ||
'\'' -> "'" | ||
_ -> [c] | ||
in | ||
concat . map escapeChar |