{"id":325151,"date":"2021-06-18T21:00:27","date_gmt":"2021-06-18T21:00:27","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=325151"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=325151","title":{"rendered":"\u0421\u043e\u0437\u0434\u0430\u0435\u043c \u0432\u0435\u0431-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u043d\u0430 Haskell \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c Reflex. \u0427\u0430\u0441\u0442\u044c 4"},"content":{"rendered":"\n<div class=\"post__text post__text-html post__text_v1\" id=\"post-content-body\">\n<p><a href=\"https:\/\/habr.com\/ru\/company\/typeable\/blog\/543998\/\">\u0427\u0430\u0441\u0442\u044c 1<\/a>.<\/p>\n<p>  <\/p>\n<p><a href=\"https:\/\/habr.com\/ru\/company\/typeable\/blog\/549542\/\">\u0427\u0430\u0441\u0442\u044c 2<\/a>.<\/p>\n<p>  <\/p>\n<p><a href=\"https:\/\/habr.com\/ru\/company\/typeable\/blog\/557852\/\">\u0427\u0430\u0441\u0442\u044c 3<\/a>.<\/p>\n<p>  <\/p>\n<p>\u0412\u0441\u0435\u043c \u043f\u0440\u0438\u0432\u0435\u0442! \u0412 \u043d\u043e\u0432\u043e\u0439 \u0447\u0430\u0441\u0442\u0438 \u043c\u044b \u0440\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435 <a href=\"https:\/\/github.com\/ghcjs\/ghcjs\/blob\/master\/doc\/foreign-function-interface.md\">JSFFI<\/a>.<\/p>\n<p>  <\/p>\n<p><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/9w\/m3\/nt\/9wm3ntdpgxeukkjephuvba7tevc.gif\" alt=\"intro\"><\/p>\n<p><a name=\"habracut\"><\/a>  <\/p>\n<h1 id=\"jsffi\">JSFFI<\/h1>\n<p>  <\/p>\n<p>\u0414\u043e\u0431\u0430\u0432\u0438\u043c \u0432 \u043d\u0430\u0448\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0438 \u0434\u0430\u0442\u044b \u0434\u0435\u0434\u043b\u0430\u0439\u043d\u0430. \u0414\u043e\u043f\u0443\u0441\u0442\u0438\u043c, \u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u043d\u0435 \u043f\u0440\u043e\u0441\u0442\u043e \u0442\u0435\u043a\u0441\u0442\u043e\u0432\u044b\u0439 input, \u0430 \u0447\u0442\u043e\u0431\u044b \u044d\u0442\u043e \u0431\u044b\u043b \u0432\u044b\u043f\u0430\u0434\u0430\u044e\u0449\u0438\u0439 datepicker. \u041c\u043e\u0436\u043d\u043e, \u043a\u043e\u043d\u0435\u0447\u043d\u043e, \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u0441\u0432\u043e\u0439 datepicker \u043d\u0430 \u0440\u0435\u0444\u043b\u0435\u043a\u0441\u0435, \u043d\u043e \u0432\u0435\u0434\u044c \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u0435\u0442 \u0431\u043e\u043b\u044c\u0448\u043e\u0435 \u043c\u043d\u043e\u0436\u0435\u0441\u0442\u0432\u043e \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u0445 JS \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a, \u043a\u043e\u0442\u043e\u0440\u044b\u043c\u0438 \u043c\u043e\u0436\u043d\u043e \u0432\u043e\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f. \u041a\u043e\u0433\u0434\u0430 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u0435\u0442 \u0443\u0436\u0435 \u0433\u043e\u0442\u043e\u0432\u044b\u0439 \u043a\u043e\u0434 \u043d\u0430 JS, \u043a\u043e\u0442\u043e\u0440\u044b\u0439, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0441\u043b\u0438\u0448\u043a\u043e\u043c \u0431\u043e\u043b\u044c\u0448\u043e\u0439, \u0447\u0442\u043e\u0431\u044b \u043f\u0435\u0440\u0435\u043f\u0438\u0441\u044b\u0432\u0430\u0442\u044c \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c GHCJS, \u0435\u0441\u0442\u044c \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u0435\u0433\u043e \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e <a href=\"https:\/\/github.com\/ghcjs\/ghcjs\/blob\/master\/doc\/foreign-function-interface.md\">JSFFI (JavaScript Foreign Function Interface)<\/a>. \u0412 \u043d\u0430\u0448\u0435\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c <a href=\"https:\/\/flatpickr.js.org\/\">flatpickr<\/a>.<\/p>\n<p>  <\/p>\n<p>\u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043d\u043e\u0432\u044b\u0439 \u043c\u043e\u0434\u0443\u043b\u044c <code>JSFFI<\/code>, \u0441\u0440\u0430\u0437\u0443 \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u0435\u0433\u043e \u0438\u043c\u043f\u043e\u0440\u0442 \u0432 <code>Main<\/code>. \u0412\u0441\u0442\u0430\u0432\u0438\u043c \u0432 \u0441\u043e\u0437\u0434\u0430\u043d\u043d\u044b\u0439 \u0444\u0430\u0439\u043b \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0439 \u043a\u043e\u0434:<\/p>\n<p>  <\/p>\n<pre><code class=\"haskell\">{-# LANGUAGE MonoLocalBinds #-} module JSFFI where  import Control.Monad.IO.Class import Reflex.Dom  foreign import javascript unsafe   &quot;(function() { \\   \\ flatpickr($1, { \\   \\   enableTime: false, \\   \\   dateFormat: \\&quot;Y-m-d\\&quot; \\   \\  }); \\   \\})()&quot;   addDatePicker_js :: RawInputElement GhcjsDomSpace -&gt; IO ()  addDatePicker :: MonadWidget t m =&gt; InputElement er GhcjsDomSpace t -&gt; m () addDatePicker = liftIO . addDatePicker_js . _inputElement_raw<\/code><\/pre>\n<p>  <\/p>\n<p>\u0422\u0430\u043a \u0436\u0435 \u043d\u0435 \u0437\u0430\u0431\u0443\u0434\u0435\u043c \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0432 \u044d\u043b\u0435\u043c\u0435\u043d\u0442 <code>head<\/code> \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0435 \u0441\u043a\u0440\u0438\u043f\u0442 \u0438 \u0441\u0442\u0438\u043b\u0438:<\/p>\n<p>  <\/p>\n<pre><code class=\"haskell\">  elAttr &quot;link&quot;     (  &quot;rel&quot; =: &quot;stylesheet&quot;     &lt;&gt; &quot;href&quot; =: &quot;https:\/\/cdn.jsdelivr.net\/npm\/flatpickr\/dist\/flatpickr.min.css&quot; )     blank   elAttr &quot;script&quot;     (  &quot;src&quot; =: &quot;https:\/\/cdn.jsdelivr.net\/npm\/flatpickr&quot;)     blank<\/code><\/pre>\n<p>  <\/p>\n<p>\u041f\u0440\u043e\u0431\u0443\u0435\u043c \u0441\u043a\u043e\u043c\u043f\u0438\u043b\u0438\u0440\u043e\u0432\u0430\u0442\u044c, \u0442\u0430\u043a \u0436\u0435 \u043a\u0430\u043a \u0438 \u0440\u0430\u043d\u044c\u0448\u0435, \u0438 \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0443\u044e \u043e\u0448\u0438\u0431\u043a\u0443:<\/p>\n<p>  <\/p>\n<pre><code class=\"plaintext\">src\/JSFFI.hs:(9,1)-(16,60): error:     \u2022 The `javascript' calling convention is unsupported on this platform     \u2022 When checking declaration:         foreign import javascript unsafe &quot;(function() {    flatpickr($1, {      enableTime: false,      dateFormat: \\&quot;Y-m-d\\&quot;    });   })()&quot; addDatePicker_js           :: RawInputElement GhcjsDomSpace -&gt; IO ()   | 9 | foreign import javascript unsafe   |<\/code><\/pre>\n<p>  <\/p>\n<p>\u0414\u0435\u0439\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u043e, \u0441\u0435\u0439\u0447\u0430\u0441 \u043c\u044b \u0441\u043e\u0431\u0438\u0440\u0430\u0435\u043c \u043d\u0430\u0448\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e <code>GHC<\/code>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u043e\u043d\u044f\u0442\u0438\u044f \u043d\u0435 \u0438\u043c\u0435\u0435\u0442, \u0447\u0442\u043e \u0442\u0430\u043a\u043e\u0435 JSFFI. \u041d\u0430\u043f\u043e\u043c\u043d\u0438\u043c, \u0447\u0442\u043e \u0441\u0435\u0439\u0447\u0430\u0441 \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u0442\u0441\u044f \u0441\u0435\u0440\u0432\u0435\u0440, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0432\u0435\u0431\u0441\u043e\u043a\u0435\u0442\u043e\u0432 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u043d\u044b\u0439 <code>DOM<\/code>, \u043a\u043e\u0433\u0434\u0430 \u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f, \u0438 \u043a\u043e\u0434 \u043d\u0430 JavaScript \u0434\u043b\u044f \u043d\u0435\u0433\u043e \u0447\u0443\u0436\u0434. \u0417\u0434\u0435\u0441\u044c \u043d\u0430\u043f\u0440\u0430\u0448\u0438\u0432\u0430\u0435\u0442\u0441\u044f \u0432\u044b\u0432\u043e\u0434, \u0447\u0442\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043d\u0430\u0448 datepicker \u043f\u0440\u0438 \u0441\u0431\u043e\u0440\u043a\u0435 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e <code>GHC<\/code> \u043d\u0435 \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u0441\u044f. \u0422\u0435\u043c \u043d\u0435 \u043c\u0435\u043d\u0435\u0435, \u0432 \u043f\u0440\u043e\u0434\u0430\u043a\u0448\u0435\u043d\u0435 <code>GHC<\/code> \u0434\u043b\u044f \u043a\u043b\u0438\u0435\u043d\u0442\u0430 \u043d\u0435 \u0431\u0443\u0434\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f, \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u043a\u043e\u043c\u043f\u0438\u043b\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0432 JS \u043f\u0440\u0438 \u043f\u043e\u043c\u043e\u0449\u0438 <code>GHCJS<\/code>, \u0438 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u043d\u044b\u0439 JS \u0432\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0442\u044c \u0443\u0436\u0435 \u0432 \u043d\u0430\u0448\u0443 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443. <code>ghcid<\/code> \u043d\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 <code>GHCJS<\/code> \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0441\u043c\u044b\u0441\u043b\u0430 \u0433\u0440\u0443\u0437\u0438\u0442\u044c\u0441\u044f \u0432 nix shell \u043d\u0435\u0442, \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c nix \u0441\u0440\u0430\u0437\u0443 \u0434\u043b\u044f \u0441\u0431\u043e\u0440\u043a\u0438:<\/p>\n<p>  <\/p>\n<pre><code class=\"plaintext\">nix-build . -A ghcjs.todo-client -o todo-client-bin<\/code><\/pre>\n<p>  <\/p>\n<p>\u0412 \u043a\u043e\u0440\u043d\u0435\u0432\u043e\u0439 \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u0438 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043f\u043e\u044f\u0432\u0438\u0442\u0441\u044f \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u044f <code>todo-client-bin<\/code> \u0441\u043e \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0439 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u043e\u0439:<\/p>\n<p>  <\/p>\n<pre><code class=\"plaintext\">todo-client-bin \u2514\u2500\u2500 bin     \u251c\u2500\u2500 todo-client-bin     \u2514\u2500\u2500 todo-client-bin.jsexe         \u251c\u2500\u2500 all.js         \u251c\u2500\u2500 all.js.externs         \u251c\u2500\u2500 index.html         \u251c\u2500\u2500 lib.js         \u251c\u2500\u2500 manifest.webapp         \u251c\u2500\u2500 out.frefs.js         \u251c\u2500\u2500 out.frefs.json         \u251c\u2500\u2500 out.js         \u251c\u2500\u2500 out.stats         \u251c\u2500\u2500 rts.js         \u2514\u2500\u2500 runmain.js<\/code><\/pre>\n<p>  <\/p>\n<p>\u041e\u0442\u043a\u0440\u044b\u0432 <code>index.html<\/code> \u0432 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0435, \u0443\u0432\u0438\u0434\u0438\u043c \u043d\u0430\u0448\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435. \u041c\u044b \u0441\u043e\u0431\u0440\u0430\u043b\u0438 \u043f\u0440\u043e\u0435\u043a\u0442 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e <code>GHCJS<\/code>, \u043d\u043e \u0432\u0435\u0434\u044c \u0434\u043b\u044f \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0432\u0441\u0435 \u0440\u0430\u0432\u043d\u043e \u0443\u0434\u043e\u0431\u043d\u0435\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c <code>GHC<\/code> \u0432\u043c\u0435\u0441\u0442\u0435 \u0441 <code>ghcid<\/code>, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043c\u043e\u0434\u0438\u0444\u0438\u0446\u0438\u0440\u0443\u0435\u043c \u043c\u043e\u0434\u0443\u043b\u044c <code>JSFFI<\/code> \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c:<\/p>\n<p>  <\/p>\n<pre><code class=\"haskell\">{-# LANGUAGE CPP #-} {-# LANGUAGE MonoLocalBinds #-}  module JSFFI where  import Reflex.Dom  #ifdef ghcjs_HOST_OS  import Control.Monad.IO.Class  foreign import javascript unsafe   &quot;(function() {\\     flatpickr($1, {\\       enableTime: false,\\       dateFormat: \\&quot;Y-m-d\\&quot;\\     }); \\   })()&quot;   addDatePicker_js :: RawInputElement GhcjsDomSpace -&gt; IO ()  addDatePicker :: MonadWidget t m =&gt; InputElement er GhcjsDomSpace t -&gt; m () addDatePicker = liftIO . addDatePicker_js . _inputElement_raw  #else  addDatePicker :: MonadWidget t m =&gt; InputElement er GhcjsDomSpace t -&gt; m () addDatePicker _ = pure ()  #endif<\/code><\/pre>\n<p>  <\/p>\n<p>\u041c\u044b \u0434\u043e\u0431\u0430\u0432\u0438\u043b\u0438 \u0443\u0441\u043b\u043e\u0432\u043d\u0443\u044e \u043a\u043e\u043c\u043f\u0438\u043b\u044f\u0446\u0438\u044e: \u0432 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u043e\u0442 \u043f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u044b, \u043b\u0438\u0431\u043e \u0431\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0432\u044b\u0437\u043e\u0432 JS \u0444\u0443\u043d\u043a\u0446\u0438\u0439, \u043b\u0438\u0431\u043e \u0437\u0430\u0433\u043b\u0443\u0448\u043a\u0443.<\/p>\n<p>  <\/p>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u0438\u0437\u043c\u0435\u043d\u0438\u0442\u044c \u0444\u043e\u0440\u043c\u0443 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u043d\u043e\u0432\u043e\u0433\u043e \u0437\u0430\u0434\u0430\u043d\u0438\u044f, \u0434\u043e\u0431\u0430\u0432\u0438\u0432 \u0442\u0443\u0434\u0430 \u043f\u043e\u043b\u0435 \u0432\u044b\u0431\u043e\u0440\u0430 \u0434\u0430\u0442\u044b:<\/p>\n<p>  <\/p>\n<pre><code class=\"haskell\">newTodoForm :: (EventWriter t (Endo Todos) m, MonadWidget t m) =&gt; m () newTodoForm = rowWrapper $ el &quot;form&quot; $ divClass &quot;input-group&quot; $ mdo   iEl &lt;- inputElement $ def     &amp; initialAttributes .~       (  &quot;type&quot; =: &quot;text&quot;       &lt;&gt; &quot;class&quot; =: &quot;form-control&quot;       &lt;&gt; &quot;placeholder&quot; =: &quot;Todo&quot; )     &amp; inputElementConfig_setValue .~ (&quot;&quot; &lt;$ btnEv)   dEl &lt;- inputElement $ def     &amp; initialAttributes .~       (  &quot;type&quot; =: &quot;text&quot;       &lt;&gt; &quot;class&quot; =: &quot;form-control&quot;       &lt;&gt; &quot;placeholder&quot; =: &quot;Deadline&quot;       &lt;&gt; &quot;style&quot; =: &quot;max-width: 150px&quot; )   addDatePicker dEl   let     addNewTodo = \\todo -&gt; Endo $ \\todos -&gt;       insert (nextKey todos) (newTodo todo) todos     newTodoDyn = addNewTodo &lt;$&gt; value iEl     btnAttr = &quot;class&quot; =: &quot;btn btn-outline-secondary&quot;       &lt;&gt; &quot;type&quot; =: &quot;button&quot;   (btnEl, _) &lt;- divClass &quot;input-group-append&quot; $     elAttr' &quot;button&quot; btnAttr $ text &quot;Add new entry&quot;   let btnEv = domEvent Click btnEl   tellEvent $ tagPromptlyDyn newTodoDyn $ domEvent Click btnEl<\/code><\/pre>\n<p>  <\/p>\n<p>\u0421\u043a\u043e\u043c\u043f\u0438\u043b\u0438\u0440\u0443\u0435\u043c \u043d\u0430\u0448\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435, \u043f\u043e\u043f\u0440\u043e\u0431\u0443\u0435\u043c \u0435\u0433\u043e \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c, \u0438 \u043c\u044b \u0432\u0441\u0435 \u0435\u0449\u0435 \u043d\u0438\u0447\u0435\u0433\u043e \u043d\u0435 \u0443\u0432\u0438\u0434\u0438\u043c. \u0415\u0441\u043b\u0438 \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u0432 \u043a\u043e\u043d\u0441\u043e\u043b\u044c \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0430 \u0432 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0435, \u0443\u0432\u0438\u0434\u0438\u043c \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0443\u044e \u043e\u0448\u0438\u0431\u043a\u0443:<\/p>\n<p>  <\/p>\n<pre><code class=\"plaintext\">uncaught exception in Haskell main thread: ReferenceError: flatpickr is not defined rts.js:5902 ReferenceError: flatpickr is not defined     at out.js:43493     at h$$abX (out.js:43495)     at h$runThreadSlice (rts.js:6847)     at h$runThreadSliceCatch (rts.js:6814)     at h$mainLoop (rts.js:6809)     at rts.js:2190     at runIfPresent (rts.js:2204)     at onGlobalMessage (rts.js:2240)<\/code><\/pre>\n<p>  <\/p>\n<p>\u0417\u0430\u043c\u0435\u0447\u0430\u0435\u043c, \u0447\u0442\u043e \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u0430\u044f \u043d\u0430\u043c \u0444\u0443\u043d\u043a\u0446\u0438\u044f \u043d\u0435 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0430. \u0422\u0430\u043a \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u0442\u0441\u044f, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u044d\u043b\u0435\u043c\u0435\u043d\u0442 <code>script<\/code> \u0441\u043e \u0441\u0441\u044b\u043b\u043a\u043e\u0439 \u0441\u043e\u0437\u0434\u0430\u0435\u0442\u0441\u044f \u0434\u0438\u043d\u0430\u043c\u0438\u0447\u0435\u0441\u043a\u0438, \u0440\u0430\u0432\u043d\u043e \u043a\u0430\u043a \u0438 \u0432\u043e\u043e\u0431\u0449\u0435 \u0432\u0441\u0435 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u044b \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b. \u041f\u043e\u044d\u0442\u043e\u043c\u0443, \u043a\u043e\u0433\u0434\u0430 \u043c\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c \u0432\u044b\u0437\u043e\u0432 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 <code>flatpickr<\/code>, \u0441\u043a\u0440\u0438\u043f\u0442, \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0449\u0438\u0439 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0443 \u0441 \u044d\u0442\u043e\u0439 \u0444\u0443\u043d\u043a\u0446\u0438\u0435\u0439 \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0435\u0449\u0435 \u043d\u0435 \u0437\u0430\u0433\u0440\u0443\u0436\u0435\u043d. \u041d\u0430\u0434\u043e \u044f\u0432\u043d\u043e \u0440\u0430\u0441\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u043f\u043e\u0440\u044f\u0434\u043e\u043a \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438.<br \/>  \u0420\u0435\u0448\u0438\u043c \u044d\u0442\u0443 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0443 \u043f\u0440\u0438 \u043f\u043e\u043c\u043e\u0449\u0438 \u043f\u0430\u043a\u0435\u0442\u0430 <code>reflex-dom-contrib<\/code>. \u042d\u0442\u043e\u0442 \u043f\u0430\u043a\u0435\u0442 \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u043c\u043d\u043e\u0433\u043e \u043f\u043e\u043b\u0435\u0437\u043d\u044b\u0445 \u043f\u0440\u0438 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0435 \u0444\u0443\u043d\u043a\u0446\u0438\u0439. \u0415\u0433\u043e \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u043d\u0435\u0442\u0440\u0438\u0432\u0438\u0430\u043b\u044c\u043d\u043e. \u0414\u0435\u043b\u043e \u0432 \u0442\u043e\u043c, \u0447\u0442\u043e \u043d\u0430 <a href=\"https:\/\/hackage.haskell.org\/package\/reflex-dom-contrib\">Hackage<\/a> \u043b\u0435\u0436\u0438\u0442 \u0443\u0441\u0442\u0430\u0440\u0435\u0432\u0448\u0430\u044f \u0432\u0435\u0440\u0441\u0438\u044f \u044d\u0442\u043e\u0433\u043e \u043f\u0430\u043a\u0435\u0442\u0430, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043f\u0440\u0438\u0434\u0435\u0442\u0441\u044f \u0431\u0440\u0430\u0442\u044c \u0435\u0433\u043e \u043d\u0430\u043f\u0440\u044f\u043c\u0443\u044e c <a href=\"https:\/\/github.com\/reflex-frp\/reflex-dom-contrib\">GitHub<\/a>. \u041e\u0431\u043d\u043e\u0432\u0438\u043c <code>default.nix<\/code> \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c.<\/p>\n<p>  <\/p>\n<pre><code class=\"plaintext\">{ reflex-platform ? ((import &lt;nixpkgs&gt; {}).fetchFromGitHub {     owner = &quot;reflex-frp&quot;;     repo = &quot;reflex-platform&quot;;     rev = &quot;efc6d923c633207d18bd4d8cae3e20110a377864&quot;;     sha256 = &quot;121rmnkx8nwiy96ipfyyv6vrgysv0zpr2br46y70zf4d0y1h1lz5&quot;;     }) }: (import reflex-platform {}).project ({ pkgs, ... }: let   reflexDomContribSrc = builtins.fetchGit {     url = &quot;https:\/\/github.com\/reflex-frp\/reflex-dom-contrib.git&quot;;     rev = &quot;11db20865fd275362be9ea099ef88ded425789e7&quot;;   };    override = self: pkg: with pkgs.haskell.lib;   doJailbreak (pkg.overrideAttrs   (old: {     buildInputs = old.buildInputs ++ [ self.doctest self.cabal-doctest ];   }));  in {   useWarp = true;    overrides = self: super: with pkgs.haskell.lib; rec {     reflex-dom-contrib = dontHaddock (override self       (self.callCabal2nix &quot;reflex-dom-contrib&quot; reflexDomContribSrc { }));   };    packages = {     todo-common = .\/todo-common;     todo-server = .\/todo-server;     todo-client = .\/todo-client;   };    shells = {     ghc = [&quot;todo-common&quot; &quot;todo-server&quot; &quot;todo-client&quot;];     ghcjs = [&quot;todo-common&quot; &quot;todo-client&quot;];   }; })<\/code><\/pre>\n<p>  <\/p>\n<p>\u0414\u043e\u0431\u0430\u0432\u0438\u043c \u0438\u043c\u043f\u043e\u0440\u0442 \u043c\u043e\u0434\u0443\u043b\u044f <code>import Reflex.Dom.Contrib.Widgets.ScriptDependent<\/code> \u0438 \u0432\u043d\u0435\u0441\u0435\u043c \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u0432 \u0444\u043e\u0440\u043c\u0443:<\/p>\n<p>  <\/p>\n<pre><code class=\"haskell\">newTodoForm :: MonadWidget t m =&gt; m (Event t (Endo Todos)) newTodoForm = rowWrapper $ el &quot;form&quot; $ divClass &quot;input-group&quot; $ mdo   iEl &lt;- inputElement $ def     &amp; initialAttributes .~       (  &quot;type&quot; =: &quot;text&quot;       &lt;&gt; &quot;class&quot; =: &quot;form-control&quot;       &lt;&gt; &quot;placeholder&quot; =: &quot;Todo&quot; )     &amp; inputElementConfig_setValue .~ (&quot;&quot; &lt;$ btnEv)   dEl &lt;- inputElement $ def     &amp; initialAttributes .~       (  &quot;type&quot; =: &quot;text&quot;       &lt;&gt; &quot;class&quot; =: &quot;form-control&quot;       &lt;&gt; &quot;placeholder&quot; =: &quot;Deadline&quot;       &lt;&gt; &quot;style&quot; =: &quot;max-width: 150px&quot; )   pb &lt;- getPostBuild   widgetHoldUntilDefined &quot;flatpickr&quot;     (pb $&gt; &quot;https:\/\/cdn.jsdelivr.net\/npm\/flatpickr&quot;)     blank     (addDatePicker dEl)   let     addNewTodo = \\todo -&gt; Endo $ \\todos -&gt;       insert (nextKey todos) (newTodo todo) todos     newTodoDyn = addNewTodo &lt;$&gt; value iEl     btnAttr = &quot;class&quot; =: &quot;btn btn-outline-secondary&quot;       &lt;&gt; &quot;type&quot; =: &quot;button&quot;   (btnEl, _) &lt;- divClass &quot;input-group-append&quot; $     elAttr' &quot;button&quot; btnAttr $ text &quot;Add new entry&quot;   let btnEv = domEvent Click btnEl   pure $ tagPromptlyDyn newTodoDyn $ domEvent Click btnEl<\/code><\/pre>\n<p>  <\/p>\n<p>\u041c\u044b \u0432\u043e\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b\u0438\u0441\u044c \u043d\u043e\u0432\u043e\u0439 \u0444\u0443\u043d\u043a\u0446\u0438\u0435\u0439 <code>widgetHoldUntilDefined<\/code>, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u043f\u043e\u0441\u0442\u0440\u043e\u0438\u0442 \u044d\u043b\u0435\u043c\u0435\u043d\u0442, \u043f\u0435\u0440\u0435\u0434\u0430\u043d\u043d\u044b\u0439 \u0435\u0439 \u0432 \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0435\u043c \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0435, \u0442\u043e\u043b\u044c\u043a\u043e \u0432 \u0442\u043e\u0442 \u043c\u043e\u043c\u0435\u043d\u0442, \u043a\u043e\u0433\u0434\u0430 \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u044b\u0439 \u0441\u043a\u0440\u0438\u043f\u0442 \u0431\u0443\u0434\u0435\u0442 \u0437\u0430\u0433\u0440\u0443\u0436\u0435\u043d.<br \/>  \u0422\u0435\u043f\u0435\u0440\u044c, \u0435\u0441\u043b\u0438 \u0437\u0430\u0433\u0440\u0443\u0437\u0438\u043c \u043d\u0430\u0448\u0443 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443, \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u043d\u0443\u044e \u043f\u0440\u0438 \u043f\u043e\u043c\u043e\u0449\u0438 <code>GHCJS<\/code>, \u043c\u044b \u0443\u0432\u0438\u0434\u0438\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u044b\u0439 \u043d\u0430\u043c\u0438 datepicker.<\/p>\n<p>  <\/p>\n<p>\u041d\u043e \u043c\u044b \u043d\u0438\u043a\u0430\u043a \u043d\u0435 \u0437\u0430\u0434\u0435\u0439\u0441\u0442\u0432\u043e\u0432\u0430\u043b\u0438 \u044d\u0442\u043e \u043f\u043e\u043b\u0435. \u0418\u0437\u043c\u0435\u043d\u0438\u043c \u0442\u0438\u043f <code>Todo<\/code>, \u043d\u0435 \u0437\u0430\u0431\u044b\u0432 \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0438\u043c\u043f\u043e\u0440\u0442 <code>Data.Time<\/code>:<\/p>\n<p>  <\/p>\n<pre><code class=\"haskell\">data Todo = Todo   { todoText     :: Text   , todoDeadline :: Day   , todoState    :: TodoState }   deriving (Generic, Eq, Show)  newTodo :: Text -&gt; Day -&gt; Todo newTodo todoText todoDeadline = Todo {todoState = TodoActive False, ..}<\/code><\/pre>\n<p>  <\/p>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u0438\u0437\u043c\u0435\u043d\u0438\u043c \u0444\u0443\u043d\u043a\u0446\u0438\u044e \u0441 \u0444\u043e\u0440\u043c\u043e\u0439 \u0434\u043b\u044f \u043d\u043e\u0432\u043e\u0433\u043e \u0437\u0430\u0434\u0430\u043d\u0438\u044f:<\/p>\n<p>  <\/p>\n<pre><code class=\"haskell\">...   today &lt;- utctDay &lt;$&gt; liftIO getCurrentTime   let     dateStrDyn = value dEl     dateDyn = fromMaybe today . parseTimeM True       defaultTimeLocale &quot;%Y-%m-%d&quot; . unpack &lt;$&gt; dateStrDyn     addNewTodo = \\todo date -&gt; Endo $ \\todos -&gt;       insert (nextKey todos) (newTodo todo date) todos     newTodoDyn = addNewTodo &lt;$&gt; value iEl &lt;*&gt; dateDyn     btnAttr = &quot;class&quot; =: &quot;btn btn-outline-secondary&quot;       &lt;&gt; &quot;type&quot; =: &quot;button&quot; ...<\/code><\/pre>\n<p>  <\/p>\n<p>\u0418 \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435 \u0434\u0430\u0442\u044b \u0432 \u0441\u043f\u0438\u0441\u043a\u0435:<\/p>\n<p>  <\/p>\n<pre><code class=\"haskell\">todoActive   :: (EventWriter t (Endo Todos) m, MonadWidget t m)   =&gt; Int -&gt; Text -&gt; Day -&gt; m () todoActive ix todoText deadline = divClass &quot;d-flex border-bottom&quot; $ do   elClass &quot;p&quot; &quot;p-2 flex-grow-1 my-auto&quot; $ do     text todoText     elClass &quot;span&quot; &quot;badge badge-secondary px-2&quot; $       text $ pack $ formatTime defaultTimeLocale &quot;%F&quot; deadline   divClass &quot;p-2 btn-group&quot; $ do   ...<\/code><\/pre>\n<p>  <\/p>\n<p>\u041f\u043e\u043b\u0443\u0447\u0435\u043d\u043d\u044b\u0439 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442, \u043a\u0430\u043a \u0432\u0441\u0435\u0433\u0434\u0430, \u043c\u043e\u0436\u043d\u043e \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c <a href=\"https:\/\/github.com\/typeable\/blog-posts-ru\/tree\/808ce2750aa9b6022079865bd9500ac498a75c76\/reflex-todo\">\u0432 \u043d\u0430\u0448\u0435\u043c \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0438<\/a>.<\/p>\n<p>  <\/p>\n<p>\u0412 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0439 \u0447\u0430\u0441\u0442\u0438 \u043c\u044b \u0440\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u043a\u0430\u043a \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u0440\u043e\u0443\u0442\u0438\u043d\u0433 \u0432 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0438 \u043d\u0430 Reflex.<\/p>\n<\/div>\n<p> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/company\/typeable\/blog\/563550\/\"> https:\/\/habr.com\/ru\/company\/typeable\/blog\/563550\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"\n<div class=\"post__text post__text-html post__text_v1\" id=\"post-content-body\">\n<p><a href=\"https:\/\/habr.com\/ru\/company\/typeable\/blog\/543998\/\">\u0427\u0430\u0441\u0442\u044c 1<\/a>.<\/p>\n<p>  <\/p>\n<p><a href=\"https:\/\/habr.com\/ru\/company\/typeable\/blog\/549542\/\">\u0427\u0430\u0441\u0442\u044c 2<\/a>.<\/p>\n<p>  <\/p>\n<p><a href=\"https:\/\/habr.com\/ru\/company\/typeable\/blog\/557852\/\">\u0427\u0430\u0441\u0442\u044c 3<\/a>.<\/p>\n<p>  <\/p>\n<p>\u0412\u0441\u0435\u043c \u043f\u0440\u0438\u0432\u0435\u0442! \u0412 \u043d\u043e\u0432\u043e\u0439 \u0447\u0430\u0441\u0442\u0438 \u043c\u044b \u0440\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435 <a href=\"https:\/\/github.com\/ghcjs\/ghcjs\/blob\/master\/doc\/foreign-function-interface.md\">JSFFI<\/a>.<\/p>\n<p>  <\/p>\n<p><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/9w\/m3\/nt\/9wm3ntdpgxeukkjephuvba7tevc.gif\" alt=\"intro\"><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-325151","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/325151","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=325151"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/325151\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=325151"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=325151"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=325151"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}