Building with Parenscript and Preact: Difference between revisions

no edit summary
No edit summary
Tags: Mobile edit Mobile web edit
No edit summary
 
(4 intermediate revisions by the same user not shown)
Line 7: Line 7:
Damn... I guess I gotta try this out.
Damn... I guess I gotta try this out.


I have a thing for tools that are off the beaten path but are super powerful. I call them "secret weapons" since everyone sees "Lisp" or "Pascal" or whatever and immediately turns their brain off while you're doubling your productivity. I figured it might be fun to put something together with Parenscript to see what the fuss is all about. I saw an article from Holger Sindbaek about how he's making 10k a month off a Solitaire game, so I figured I might try to get in on some of that action; plus I have fond memories as a kid of opening FreeCell and watching the king's head turn left and right to follow the mouse.
I have a thing for tools that are off the beaten path but are super powerful. I call them "secret weapons" since everyone sees "Lisp" or "Pascal" or whatever and immediately turns their brain off while you're doubling your productivity. I figured it might be fun to put something together with Parenscript to see what the fuss is all about. I saw an [https://www.indiehackers.com/post/how-i-grew-a-simple-solitaire-game-to-10k-mrr-28e352c308 article from Holger Sindbaek] about how he's making 10k a month off a Solitaire game, so I figured I might try to get in on some of that action; plus I have fond memories as a kid of opening FreeCell and watching the king's head turn left and right to follow the mouse.


[[File:King2.apng]]
[[File:King2.apng]]
Line 17: Line 17:
Since I want to side-step the modern frontend stack a bit, I found Preact as an alternative implementation of React in 3kb, and it [https://dev.to/jovidecroock/prefresh-fast-refresh-for-preact-26kg has a blog post] describing how to hook into its hot updating system. I quickly whipped up a macro which implements a JSX-like syntax, and a <code>defcomponent</code> macro for creating React components, so I can write something like this:
Since I want to side-step the modern frontend stack a bit, I found Preact as an alternative implementation of React in 3kb, and it [https://dev.to/jovidecroock/prefresh-fast-refresh-for-preact-26kg has a blog post] describing how to hook into its hot updating system. I quickly whipped up a macro which implements a JSX-like syntax, and a <code>defcomponent</code> macro for creating React components, so I can write something like this:


<syntaxhighlight lang="lisp">(defcomponent -ui-button () (props)
<syntaxhighlight lang="lisp">
  (with-defaults (on-click (disabled false) children) props
(defcomponent -counter (((counter set-counter) (use-state 0))) ()
    (psx
  (psx
    (:div ((on-click on-click)
  (:div ()
            (class-name (class-names "ui-button" (when disabled "disabled")))
        "The button has been clicked "
            (style (create "color"
        (:span ((style (create "color" "red")))
                          (if disabled
                (chain counter (to-string)))
                            "grey"
        " times."
                            "black"))))
        (:button ((onclick (lambda () (set-counter (1+ counter)))))
          children))))</syntaxhighlight>
                  "Increment"))))
</syntaxhighlight>
 
[[File:Clicked.png]]


and have it compile into:
and have it compile into:
Line 32: Line 35:
<syntaxhighlight lang="javascript">
<syntaxhighlight lang="javascript">
var s = $RefreshSig$();
var s = $RefreshSig$();
function UiButton(props) {
function Counter() {
     s();
     s();
     var disabled304 = 'undefined' === typeof props.disabled ? false : props.disabled;
     var _db1107 = preactHooks.useState(0);
    var counter = _db1107[0];
    var setCounter = _db1107[1];
     __PS_MV_REG = [];
     __PS_MV_REG = [];
     return [preact.h('div', Object.assign({ onClick : props.onClick,
     return [preact.h('div', Object.assign({ }), ['The button has been clicked '], [preact.h('span', Object.assign({ style : { 'color' : 'red' } }), [counter.toString()])], [' times.'], [preact.h('button', Object.assign({ onclick : function () {
                                            className : classNames('ui-button', disabled304 ? 'disabled' : null),
        __PS_MV_REG = [];
                                            style : { 'color' : disabled304 ? 'grey' : 'black' }
        return setCounter(counter + 1);
                                          }), [props.children])];
    } }), ['Increment'])])];
};
};
s(UiButton, '', false, function () {
s(Counter, 'useState{[counter, setCounter](0)}', false, function () {
     return [];
     return [];
});
});
$RefreshReg$(UiButton, 'UiButton');
$RefreshReg$(Counter, 'Counter');
flushUpdates();
flushUpdates();
</syntaxhighlight>
</syntaxhighlight>
Line 52: Line 57:
[[File:Lisp-preact.mp4|1000px]]
[[File:Lisp-preact.mp4|1000px]]


And it's true, it only took about 50 lines of macro code to implement. Combine that with trident-mode to push the snippets to the frontend for evaluation, and you have a full fledged interactive hot reloading environment! I don't want to get all Reddit about it, but:
And it's true, it only took about [https://github.com/SuperDisk/cardgames/blob/dbf74fc105fde68cb4962a8b7ff7dcfb231db34f/preact.lisp#L53 50 lines of macro code to implement.] Combine that with trident-mode to push the snippets to the frontend for evaluation, and you have a full fledged interactive hot reloading environment! I don't want to get all Reddit about it, but:


[[File:8q5nqk.jpg]]
[[File:8q5nqk.jpg]]
Line 115: Line 120:
</blockquote>
</blockquote>


Parenscript is fundamentally flawed. It's a super-lightweight papering-over of JavaScript, and you become aware of that harsh reality immediately. It makes an attempt to look like the surface syntax of Common Lisp while maintaining none of its semantics, so you kind of have to juggle in your mind the JavaScript you want to get, the Parenscript you have to write, and the Common Lisp that would do something else if executed as CL. It's especially odd since JS has no cons cells, and Parenscript doesn't try to provide them-- <code>(cons 1 nil)</code> just becomes <code>cons(1, null);</code>. So it looks superficially a lot like CL, but isn't like it semantically at all. On top of that, Parenscript has a few bugs: it will just miscompile things sometimes, specifically its <code>loop</code> construct, and it has a bad habit of not declaring variables when you use <code>let</code>, which doesn't work in <code>use strict"</code> mode.
Parenscript is fundamentally flawed. It's a super-lightweight papering-over of JavaScript, and you become aware of that harsh reality immediately. It makes an attempt to look like the surface syntax of Common Lisp while maintaining none of its semantics, so you kind of have to juggle in your mind the JavaScript you want to get, the Parenscript you have to write, and the Common Lisp that would do something else if executed as CL. It's especially odd since JS has no cons cells, and Parenscript doesn't try to provide them-- <code>(cons 1 nil)</code> just becomes <code>cons(1, null);</code>. So it looks superficially a lot like CL, but isn't like it semantically at all. On top of that, Parenscript has a few bugs: it will just miscompile things sometimes, specifically its <code>loop</code> construct, and it has a bad habit of not declaring variables when you use <code>let</code>, which doesn't work in <code>"use strict"</code> mode.


= Conclusion =
= Conclusion =
Line 126: Line 131:


Check out the game here: https://online-freecell.com
Check out the game here: https://online-freecell.com
Source code is all here: https://github.com/SuperDisk/cardgames


By the way, I'm looking for a job, so if you or anyone you know is hiring and needs a really good developer, [mailto:yux50000@hotmail.com please let me know ;)]
By the way, I'm looking for a job, so if you or anyone you know is hiring and needs a really good developer, [mailto:yux50000@hotmail.com please let me know ;)]