diff --git a/backends/pardiff/Shpadoinkle/Backend/ParDiff.hs b/backends/pardiff/Shpadoinkle/Backend/ParDiff.hs index afe73c49e79ec86049748a8e2006d122e79cb1b3..23ddcc526a7dd7e46ca464f0405b88426f3603a6 100644 --- a/backends/pardiff/Shpadoinkle/Backend/ParDiff.hs +++ b/backends/pardiff/Shpadoinkle/Backend/ParDiff.hs @@ -15,6 +15,7 @@ {-# LANGUAGE TypeOperators #-} {-# LANGUAGE UndecidableInstances #-} {-# OPTIONS_GHC -fno-warn-type-defaults #-} +{-# OPTIONS_GHC -fno-warn-orphans #-} #ifndef ghcjs_HOST_OS {-# LANGUAGE StandaloneDeriving #-} @@ -45,7 +46,7 @@ module Shpadoinkle.Backend.ParDiff import Control.Applicative (Alternative) -import Control.Compactable (Compactable (traverseMaybe)) +import Control.Compactable (Compactable (..)) import Control.Lens ((^.)) import Control.Monad.Base (MonadBase (..), liftBaseDefault) import Control.Monad.Catch (MonadCatch, MonadThrow) @@ -56,11 +57,13 @@ import Control.Monad.Trans.Control (ComposeSt, MonadBaseControl (..), MonadTransControl, defaultLiftBaseWith, defaultRestoreM) -import Data.Align (Semialign (align)) +import Data.Align (Semialign (..)) +import Data.FMList as FM import Data.Foldable (traverse_) import Data.Kind (Type) import Data.Map (Map) import qualified Data.Map as M +import Data.Maybe (fromJust, isJust) import Data.Monoid ((<>)) import Data.Once (Once, newOnce, runOnce) import Data.Text (Text, intercalate) @@ -138,8 +141,20 @@ runParDiff :: TVar model -> ParDiffT model m ~> m runParDiff t (ParDiffT r) = runReaderT r t +instance Compactable FMList where + compact = fmap fromJust . FM.filter isJust + +instance Semialign FMList where + align xs ys | FM.null ys = This <$> xs + align xs ys | FM.null xs = That <$> ys + align xs ys = let x = FM.head xs + y = FM.head ys + in FM.cons (These x y) (align (FM.tail xs) (FM.tail ys)) + zip = FM.zip + + data ParVNode :: Type -> Type where - ParNode :: Once JSM RawNode -> Text -> Map Text (ParVProp a) -> [ParVNode a] -> ParVNode a + ParNode :: Once JSM RawNode -> Text -> Map Text (ParVProp a) -> FMList (ParVNode a) -> ParVNode a ParPotato :: Once JSM RawNode -> ParVNode a ParTextNode :: Once JSM RawNode -> Text -> ParVNode a @@ -326,7 +341,7 @@ patchChildren #endif => Show a => NFData a - => RawNode -> [ParVNode a] -> [ParVNode a] -> ParDiffT a m [ParVNode a] + => RawNode -> FMList (ParVNode a) -> FMList (ParVNode a) -> ParDiffT a m (FMList (ParVNode a)) patchChildren parent@(RawNode p) old new'' = traverseMaybe (\case diff --git a/backends/pardiff/package.yaml b/backends/pardiff/package.yaml index 218a3172a0adec90cbf58dd94d237a461ed476fa..94751bf8791194556f1e5d065e3b0a931903a141 100644 --- a/backends/pardiff/package.yaml +++ b/backends/pardiff/package.yaml @@ -53,6 +53,7 @@ library: - semialign >= 1 && < 1.2 - these >= 1 && < 1.2 - transformers-base >= 0.4.5 && < 0.5 + - fmlist - Shpadoinkle diff --git a/backends/snabbdom/Shpadoinkle/Backend/Snabbdom.hs b/backends/snabbdom/Shpadoinkle/Backend/Snabbdom.hs index be5ef61f517b4daf8b033e71bf5a3a9d05d71f4b..e36d8ef772d1e9d2edf43ce825eea548ea69c829 100644 --- a/backends/snabbdom/Shpadoinkle/Backend/Snabbdom.hs +++ b/backends/snabbdom/Shpadoinkle/Backend/Snabbdom.hs @@ -40,6 +40,7 @@ import Control.Monad.Trans.Control (ComposeSt, MonadBaseControl (..), MonadTransControl, defaultLiftBaseWith, defaultRestoreM) +import Data.FMList as FM import Data.FileEmbed (embedStringFile) import Data.Text (Text, split) import Data.Traversable (for) @@ -184,7 +185,7 @@ instance (MonadJSM m, NFData a) => Backend (SnabbdomT a) m a where cs <- sequence children i <- ask; liftJSM $ do o <- props toJSM i $ fromProps ps - jsg3 "vnode" name o cs >>= fromJSValUnchecked) + jsg3 "vnode" name o (FM.toList cs) >>= fromJSValUnchecked) (\mrn -> liftJSM $ do o <- create diff --git a/backends/snabbdom/package.yaml b/backends/snabbdom/package.yaml index 22680ef280a3c971e35d47797e1ae3a82692861a..0e9c269e034a0b26a9968616efab0da95f27c07d 100644 --- a/backends/snabbdom/package.yaml +++ b/backends/snabbdom/package.yaml @@ -44,6 +44,7 @@ library: - exceptions - transformers-base - monad-control + - fmlist - mtl >= 2.2.2 && < 2.3 - unliftio >= 0.2.12 && < 0.3 diff --git a/backends/static/Shpadoinkle/Backend/Static.hs b/backends/static/Shpadoinkle/Backend/Static.hs index 04083b659d92950751f936306045d5b8946c5449..bebee22a5c8cce260f19bd221348ec6fe31b8058 100644 --- a/backends/static/Shpadoinkle/Backend/Static.hs +++ b/backends/static/Shpadoinkle/Backend/Static.hs @@ -11,7 +11,9 @@ module Shpadoinkle.Backend.Static ( renderStatic ) where import Control.Compactable (Compactable (fmapMaybe)) -import Data.Monoid (mconcat, (<>)) +import Data.FMList as FM +import Data.Foldable (fold) +import Data.Monoid ((<>)) import Data.Text (Text, null, unwords) import Shpadoinkle (Html, Prop (PText), cataH, cataProp) @@ -22,7 +24,7 @@ renderStatic :: Html m a -> Text renderStatic = cataH renderTag (const mempty) id -renderTag :: Text -> [(Text, Prop m a)] -> [Text] -> Text +renderTag :: Text -> [(Text, Prop m a)] -> FMList Text -> Text renderTag tag props cs | isSelfClosing tag = renderSelfClosing tag props | otherwise = renderWrapping tag props cs @@ -38,12 +40,12 @@ innerHTML :: Text innerHTML = "innerHTML" -renderWrapping :: Text -> [(Text, Prop m a)] -> [Text] -> Text +renderWrapping :: Text -> [(Text, Prop m a)] -> FMList Text -> Text renderWrapping tag props cs = renderOpening tag props <> ">" <> (case innerHTML `lookup` props of Just (PText html) -> html - _ -> mconcat cs) + _ -> fold cs) <> " tag <> ">" diff --git a/backends/static/package.yaml b/backends/static/package.yaml index 2efafe13d4ee9af53b4d80430b1a9b59001f8bd1..023fe4f6353120dbe64fddcac32a01a1d216527f 100644 --- a/backends/static/package.yaml +++ b/backends/static/package.yaml @@ -38,5 +38,6 @@ library: dependencies: - compactable >= 0.1.2 && < 0.2 - text >= 1.2.3 && < 1.3 + - fmlist - Shpadoinkle diff --git a/core/Shpadoinkle/Core.hs b/core/Shpadoinkle/Core.hs index f77f6e7b83e92d798caf75fb1977af79165630c7..e79bf0992e051852afca93f7325861352f1815bf 100644 --- a/core/Shpadoinkle/Core.hs +++ b/core/Shpadoinkle/Core.hs @@ -59,6 +59,7 @@ import Control.PseudoInverseCategory (EndoIso (..), PIArrow (piendo, piiso), PseudoInverseCategory (piinverse), ToHask (piapply)) +import Data.FMList as FM import Data.Kind (Type) import Data.Map as M (Map, singleton, toList, unionWithKey) @@ -87,7 +88,7 @@ import Shpadoinkle.Continuation (Continuation, Continuous (..), -- This is Church encoded for performance reasons. newtype Html m a = Html { unHtml - :: forall r. (Text -> Props m a -> [r] -> r) + :: forall r. (Text -> Props m a -> FMList r-> r) -> (JSM RawNode -> r) -> (Text -> r) -> r @@ -134,7 +135,7 @@ newtype Props m a = Props { getProps :: Map Text (Prop m a) } toProps :: Applicative m => [(Text, Prop m a)] -> Props m a -toProps = foldMap $ Props . uncurry singleton +toProps = foldMap $ Props . uncurry M.singleton fromProps :: Props m a -> [(Text, Prop m a)] @@ -311,7 +312,7 @@ cataProp d t f l p = \case -- | Construct an HTML element JSX-style. h :: Applicative m => Text -> [(Text, Prop m a)] -> [Html m a] -> Html m a -h t ps cs = Html $ \a b c -> a t (toProps ps) ((\(Html h') -> h' a b c) <$> cs) +h t ps cs = Html $ \a b c -> a t (toProps ps) ((\(Html h') -> h' a b c) <$> FM.fromList cs) {-# INLINE h #-} @@ -334,7 +335,7 @@ eitherH = eitherC -- | Fold an HTML element, i.e. transform an h-algebra into an h-catamorphism. -cataH :: (Text -> [(Text, Prop m a)] -> [b] -> b) +cataH :: (Text -> [(Text, Prop m a)] -> FMList b -> b) -> (JSM RawNode -> b) -> (Text -> b) -> Html m a -> b diff --git a/core/package.yaml b/core/package.yaml index 853515d5db3f1d759ae54899cbe8dab466953ebb..bd6488cb00edffa740502656a15dcdca7b5ac499 100644 --- a/core/package.yaml +++ b/core/package.yaml @@ -31,6 +31,7 @@ dependencies: - base >= 4.12.0 && < 4.16 - transformers - containers + - fmlist when: