4 ms·
Oh this looks fun getStructureString and render can be defined as record selectors newtype Structure = Structure { getStructureString :: String } newtype H
by Iceland_jack 4y ago
Oh this looks fun
getStructureString and render can be defined as record selectors
newtype Structure = Structure { getStructureString :: String }
newtype Html = Html { render :: String }
BlockArguments let you write expressions like myhtml
myhtml :: Html
myhtml =
html_
"My title"
( append_
(h1_ "Heading")
( append_
(p_ "Paragraph #1")
(p_ "Paragraph #2")
)
)
in a more domain-specific style. De gustibus, but some may prefer it to the parentheses.
{-# Language BlockArguments #-}
myhtml :: Html
myhtml =
html_ "My title" do
append_
do h1_ "Heading"
do append_
do p_ "Paragraph #1"
do p_ "Paragraph #2"
or using the associativity of append_ = (<>)
myhtml =
html_ "My title" do
h1_ "Heading" <> p_ "Paragraph #1" <> p_ "Paragraph #2"
myhtml =
html_ "My title" do mconcat
[ h1_ "Heading"
, p_ "Paragraph #1"
, p_ "Paragraph #2"
]
All examples of 'concat . map' can be replaced with.. concatMap :Ð
- Iceland_jack 4y agoInstead of defining append_ for structure newtype Structure = Structure String append_ :: Structure -> Structure -> Structure append_ (Structure c1) (Structure c2) = Structure (c1 <> c2) empty_ :: Structure empty_ = Structure "" the <> operator (from Semigroup) can be reused by deriving it via the underlying String type (edit: I see this is suggested later in the tutorial). {-# Language DerivingStrategies #-} {-# Language GeneralizedNewtypeDeriving #-} newtype Structure = Structure String deriving newtype (Semigroup, Monoid) append_ :: Structure -> Structure -> Structure append_ = (<>) empty_ :: Structure empty_ = mempty Semigroup and Monoid let us use a lot of standard vocabulary concatStructure :: [Structure] -> Structure concatStructure list = case list of [] -> empty_ x : xs -> x <> concatStructure xs becomes concatStructure :: [Structure] -> Structure concatStructure = mconcat or concatStructure = fold Also in the detour about kinds they are written as * while the ecosystem is moving towards a more uniform Type name. {-# Language StandaloneKindSignatures #-} import Data.Kind (Type) type Tuple :: Type -> Type -> Type data Tuple a b = Tuple a b type Either :: Type -> Type -> Type data Either a b = Left a | Right b
- gilmi 4y agoRight but the point of the book is to ease learners into the language (and teach core concepts), and not throw them into the deep end of it. Also, I'll update the text about kinds when GHC will use Type instead of * everywhere: $ ghci GHCi, version 9.2.2: https://www.haskell.org/ghc/ https://www.haskell.org/ghc/ :? for help λ> :k (,) (,) :: * -> * -> *
- Iceland_jack 4y agoNot critiquing, I was giving a stream of consciousness while reading. I enjoyed it Until we get Type by default the best we can do is enable NoStarIsType
- gilmi 4y agoThank you. I'm glad you're enjoying it. Sorry if I sounded a bit aggressive here. I think one of the cool things about Haskell is that there's quite a high ceiling in terms of solutions you can reach for. On many occasions when one get annoyed by something and thinks "there must be a better way", there is one. You show how one with more knowledge and command of the language can make it do a lot of things for free, and that is very cool! But I can also see how these solutions can look a bit intimidating for people with less experience, and it's important to take this into account as well. This is kind of a double edged sword. Gotta find the right balance.
- Iceland_jack 4y agoI didn't take it as aggressive. I hope people who are curious get something out of my comment but without intimidating others, perhaps I write it as a bonus that people can ignore if it doesn't help but you are right that some people could be put off by it.
- gilmi 4y agoSounds like a generally reasonable approach to me.