Skip to content

Commit

Permalink
Remove old DOM API, WeakDynamic and Widgets.{Button, Input, RadioGrou…
Browse files Browse the repository at this point in the history
…p, Select) (#97)

* Remove old DOM API and Widgets.{Button, Input, RadioGroup, Select)

* Fix benchmark

* Remove WeakDynamic

* Format code

* Revert package.json changes

* Fix warnings

* Fix formatting
  • Loading branch information
zyla committed Apr 26, 2024
1 parent 44c06fa commit 3afc978
Show file tree
Hide file tree
Showing 42 changed files with 356 additions and 1,858 deletions.
142 changes: 85 additions & 57 deletions bench/Bench/Builder.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,43 +11,63 @@

// replicateM_Widget_ :: Int -> Widget Unit -> Widget Unit
// replicateM_, optimized for Widget.
exports.replicateM_Widget_ = function(n) {
return function(widget) {
return function(env) {
for(var i = 0; i < n; i++) {
export function replicateM_Widget_(n) {
return function (widget) {
return function (env) {
for (var i = 0; i < n; i++) {
widget(env);
}
};
};
};
}

////////////////////////////////////////////////////////////////////////////////
// staticJS :: forall e. Int -> Eff e Unit
//
// This is what we're aiming for in terms of performance: imperative JS code
// that just creates and appends the nodes.
exports.staticJS = function(n) {
return function() {
var parent = document.createElement('div');
for(var i = 0; i < n; i++) {
elAttr('div', {'class':'foo'}, function(parent) {
elAttr('div', {'class':'bar'}, function(parent) {
text('foo', parent);
}, parent);
elAttr('div', {'class':'baz'}, function(parent) {
text('foo', parent);
}, parent);
elAttr('div', {'class':'thud'}, function(parent) {
text('foo', parent);
}, parent);
}, parent);
export function staticJS(n) {
return function () {
var parent = document.createElement("div");
for (var i = 0; i < n; i++) {
elAttr(
"div",
{ class: "foo" },
function (parent) {
elAttr(
"div",
{ class: "bar" },
function (parent) {
text("foo", parent);
},
parent
);
elAttr(
"div",
{ class: "baz" },
function (parent) {
text("foo", parent);
},
parent
);
elAttr(
"div",
{ class: "thud" },
function (parent) {
text("foo", parent);
},
parent
);
},
parent
);
}
};
};
}

function elAttr(tag, attrs, content, parent) {
var el = document.createElement(tag);
for(var k in attrs) {
for (var k in attrs) {
el.setAttribute(k, attrs[k]);
}
content(el);
Expand All @@ -65,31 +85,31 @@ function text(t, parent) {
// Like staticJS, but all functions are curried. This is more similar to what
// PureScript emits, but still uses imperative sequencing instead of `bindE`.

exports.staticJS_c = function(n) {
return function() {
var parent = document.createElement('div');
for(var i = 0; i < n; i++) {
elAttr_c('div')({'class':'foo'})(function(parent) {
elAttr_c('div')({'class':'bar'})(function(parent) {
text_c('foo')(parent);
export function staticJS_c(n) {
return function () {
var parent = document.createElement("div");
for (var i = 0; i < n; i++) {
elAttr_c("div")({ class: "foo" })(function (parent) {
elAttr_c("div")({ class: "bar" })(function (parent) {
text_c("foo")(parent);
})(parent);
elAttr_c('div')({'class':'baz'})(function(parent) {
text_c('foo')(parent);
elAttr_c("div")({ class: "baz" })(function (parent) {
text_c("foo")(parent);
})(parent);
elAttr_c('div')({'class':'thud'})(function(parent) {
text_c('foo')(parent);
elAttr_c("div")({ class: "thud" })(function (parent) {
text_c("foo")(parent);
})(parent);
})(parent);
}
};
};
}

function elAttr_c(tag) {
return function(attrs) {
return function(content) {
return function(parent) {
return function (attrs) {
return function (content) {
return function (parent) {
var el = document.createElement(tag);
for(var k in attrs) {
for (var k in attrs) {
el.setAttribute(k, attrs[k]);
}
content(el);
Expand All @@ -100,7 +120,7 @@ function elAttr_c(tag) {
}

function text_c(t) {
return function(parent) {
return function (parent) {
var node = document.createTextNode(t);
parent.appendChild(node);
};
Expand All @@ -112,33 +132,41 @@ function text_c(t) {
// Functions are curried, and sequencing is done using monadic `bind`, though
// specialized to `RIO` monad - "Reader + IO".

exports.staticJS_m = function(n) {
return function() {
var parent = document.createElement('div');
replicateM_RIO(n,
elAttr_c('div')({'class':'foo'})(
bind_RIO( elAttr_c('div')({'class':'bar'})(text_c('foo')), function(_) {
return bind_RIO( elAttr_c('div')({'class':'baz'})(text_c('foo')), function(_) {
return elAttr_c('div')({'class':'thud'})(text_c('foo'));
});
})
))(parent);
export function staticJS_m(n) {
return function () {
var parent = document.createElement("div");
replicateM_RIO(
n,
elAttr_c("div")({ class: "foo" })(
bind_RIO(
elAttr_c("div")({ class: "bar" })(text_c("foo")),
function (_) {
return bind_RIO(
elAttr_c("div")({ class: "baz" })(text_c("foo")),
function (_) {
return elAttr_c("div")({ class: "thud" })(text_c("foo"));
}
);
}
)
)
)(parent);
};
};
}

function bind_RIO(m, k) {
return function(env) {
return function (env) {
return k(m(env))(env);
};
};
}

// Generic implementation of `replicateM` in terms of `bind_RIO`.
function replicateM_RIO(n, x) {
if(n == 0) {
return function() {};
if (n == 0) {
return function () {};
} else {
return bind_RIO(x, function(_) {
return replicateM_RIO(n-1, x);
return bind_RIO(x, function (_) {
return replicateM_RIO(n - 1, x);
});
}
}
80 changes: 12 additions & 68 deletions bench/Bench/Builder.purs
Original file line number Diff line number Diff line change
Expand Up @@ -5,88 +5,35 @@ module Bench.Builder
import Prelude

import Bench.Types (Tests)
import Control.Monad.Reader (runReaderT)
import Data.List.Lazy (replicateM)
import Data.Tuple (Tuple(Tuple))
import Effect (Effect)
import Specular.Dom.Browser (Node)
import Specular.Dom.Builder.Class (elAttr, elDynAttr, text)
import Specular.Dom.Browser (Node, createElement, (:=))
import Specular.Dom.Element as E
import Specular.Dom.Node.Class (createElement, (:=))
import Specular.Dom.Widget (class MonadWidget, Widget, runWidgetInNode)
import Specular.Dom.Widget (Widget, runWidgetInNode)
import Test.Utils.Dom (T3(T3))

-- | The widget we're rendering.
staticWidget :: forall m. MonadWidget m => Int -> m Unit
staticWidget n =
void $ replicateM n $
elAttr "div" ("class" := "foo") $ do
elAttr "div" ("class" := "bar") $ do
text "foo"
elAttr "div" ("class" := "baz") $ do
text "foo"
elAttr "div" ("class" := "thud") $ do
text "foo"

-- | The same widget, but monomorphic. Can be used to test the effect of
-- | polymorphism to some degree.
staticWidgetMono :: Int -> Widget Unit
staticWidgetMono n =
void $ replicateM n $
elAttr "div" ("class" := "foo") $ do
elAttr "div" ("class" := "bar") $ do
text "foo"
elAttr "div" ("class" := "baz") $ do
text "foo"
elAttr "div" ("class" := "thud") $ do
text "foo"

-- | The same widget, but monomorphic. Can be used to test the effect of
-- | polymorphism to some degree.
staticWidgetMonoOptReplicate :: Int -> Widget Unit
staticWidgetMonoOptReplicate n =
replicateM_Widget_ n $
elAttr "div" ("class" := "foo") $ do
elAttr "div" ("class" := "bar") $ do
text "foo"
elAttr "div" ("class" := "baz") $ do
text "foo"
elAttr "div" ("class" := "thud") $ do
text "foo"

staticWidgetMonoOptReplicateD :: Int -> Widget Unit
staticWidgetMonoOptReplicateD n =
replicateM_Widget_ n $
elDynAttr "div" (pure $ "class" := "foo") do
elDynAttr "div" (pure $ "class" := "bar") do
text "foo"
elDynAttr "div" (pure $ "class" := "baz") do
text "foo"
elDynAttr "div" (pure $ "class" := "thud") do
text "foo"

foreign import replicateM_Widget_ :: Int -> Widget Unit -> Widget Unit

staticWidgetNewApi :: Int -> Widget Unit
staticWidgetNewApi n =
replicateM_Widget_ n $
E.el "div" [E.attrs ("class" := "foo")] do
E.el "div" [E.attrs ("class" := "bar")] do
E.el "div" [ E.attrs ("class" := "foo") ] do
E.el "div" [ E.attrs ("class" := "bar") ] do
E.text "foo"
E.el "div" [E.attrs ("class" := "baz")] do
E.el "div" [ E.attrs ("class" := "baz") ] do
E.text "foo"
E.el "div" [E.attrs ("class" := "thud")] do
E.el "div" [ E.attrs ("class" := "thud") ] do
E.text "foo"

staticWidgetNewApiD :: Int -> Widget Unit
staticWidgetNewApiD n =
replicateM_Widget_ n $
E.el "div" [E.attrsD (pure ("class" := "foo"))] do
E.el "div" [E.attrsD (pure ("class" := "bar"))] do
E.el "div" [ E.attrsD (pure ("class" := "foo")) ] do
E.el "div" [ E.attrsD (pure ("class" := "bar")) ] do
E.text "foo"
E.el "div" [E.attrsD (pure ("class" := "baz"))] do
E.el "div" [ E.attrsD (pure ("class" := "baz")) ] do
E.text "foo"
E.el "div" [E.attrsD (pure ("class" := "thud"))] do
E.el "div" [ E.attrsD (pure ("class" := "thud")) ] do
E.text "foo"

-- See comments in the FFI module.
Expand All @@ -99,15 +46,12 @@ builderTests =
[ Tuple "js " (pure $ delay \_ -> staticJS 10)
, Tuple "js curried " (pure $ delay \_ -> staticJS_c 10)
, Tuple "js monad " (pure $ delay \_ -> staticJS_m 10)
, Tuple "Widget + elAttr " (pure $ delay \_ -> runWidget $ staticWidgetMonoOptReplicate 10)
, Tuple "Widget + elDynAttr " (pure $ delay \_ -> runWidget $ staticWidgetMonoOptReplicateD 10)
, Tuple "Widget + attr " (pure $ delay \_ -> runWidget $ staticWidgetNewApi 10)
, Tuple "Widget + attrD " (pure $ delay \_ -> runWidget $ staticWidgetNewApiD 10)
, Tuple "MonadWidget + ReaderT " (pure $ delay \_ -> runWidget $ runReaderT (staticWidget 10) unit)
, Tuple "MonadWidget + 2x ReaderT " (pure $ delay \_ -> runWidget $ flip runReaderT unit $ flip runReaderT unit $ staticWidget 10)
]

where delay x = pure unit >>= x
where
delay x = pure unit >>= x

-- mechanics

Expand Down

0 comments on commit 3afc978

Please sign in to comment.