Fork me on GitHub
#clojure
<
2019-09-07
>
ahungry02:09:34

For writing web-based presentational elements, what does everyone prefer writing in? [html/xml/jsx, hiccup, django/templating type, pug, markdown, org] ?

ahungry03:09:19

I'm thinking about putting together a "simple" web framework, where the most common user base could write dynamic pages without ever firing up a repl or having to deal with dependency management etc., by supporting a custom read/evaluator that works on files of this form:

(use 'hiccup.core)

         (defn input [name]
           (html [:label {:class (str name "-label")}
             [:input {:name name :type "text"}]]))

         (def pinput (comp println input))

         <!DOCTYPE html>
         <html>
           <head>
             <title>You can freely mix html and hiccup just fine.</title>
           </head>
           <body>
             <form name='my-form'>
               (println (format "Hello %s, thanks for visiting!" (:name request)))
               (pinput "username")
               (pinput "password")
             </form name='my-form'>
           </body>
         </html>

ahungry03:09:33

sort of a callback to the early days of PHP, when things were very easy for new programmers

seancorfield03:09:01

@m131 It depends. If I want to work closely with front end folks, I like the Django-style HTML templating that yogthos/Selmer offers. If I'm generating HTML from data that doesn't need interaction with frontend folks, then Hiccup is nice.

ahungry03:09:16

I want to make a system that doesn't rely on all the busy work of compojure/other routes, that will just leverage the host OS filesystem for that - so, something in /greetings/hello.clj would contain that content up above, and produce the appropriate html

seancorfield03:09:37

We had one process that used Hiccup extensively and had to switch it to Selmer when our UI/UX czar redesigned the website... that was not actually as painful as I was expecting: instead of using Hiccup to turn data into HTML, we just simplified the data a bit and then transformed it into the raw data needed for Selmer.

ahungry03:09:47

in my day job I'm doing a ton of front end react spa stuff, and a variety of php/typescript apis to support it on the backend, and I feel like (even with clojure luminus) 90% of work is busy-work, of writing php symfony type framework code, where a route calls / maps to a controller, which maps to a service, which maps to a repo, which calls to a database and serializes into a model. All for a glorified "custom database skin"

seancorfield03:09:00

I wrote a small, simple MVC framework (for CFML) that relied on conventions to match /section/item style URLs to handlers and HTML views (and has a cascading layout wrapper based on the same structure). I ported it to Clojure and we used it at work for a while. Feel free to read and steal ideas from it: https://github.com/framework-one/fw1-clj

ahungry03:09:12

nice ty 🙂

seancorfield03:09:50

I can't remember what state it ended up in before I decided to sunset it but it may give you ideas...

seancorfield03:09:16

(it was the one of the top two MVC frameworks in CFML back in the day)

ahungry03:09:17

cold fusion markup language? (sorry, I probably could have google in the time I asked)

seancorfield03:09:25

Yeah, CFML = ColdFusion. You'll want to look at the v0.10.2 version of the code I think -- that has the usermanager example that is still FW/1-based and uses conventions. As I was sunsetting the framework, I rewrote it to use Compojure routes (instead of FW/1 conventions).

ahungry03:09:55

btw, this is another question in a slightly related domain/thought - I notice a few different attempts at a faster clojure bootup experience, usually by rewriting a subset of clojure (babushka bb, or joker etc.) - while very useful, why isn't there (or perhaps there is) a paradigm out there that just involves running a persistent clojure jvm that imports many many dependencies at once, and then over socket, you tell it a script location on server, like "~/bin/foo.clj", and it just reads and evals the file?

ahungry03:09:17

almost as if one big clojure jvm is equivalent to a mini OS

seancorfield03:09:27

Several people have built such a thing but they've never really caught on.

ahungry03:09:11

interesting, I wonder why, as when I was reading some of the various state-of-clojure stuff throughout the years, the most repeated complaint was "slow startup time"

seancorfield03:09:44

For development, I think most people eventually get to a place where they start a REPL and leave it running for days (or weeks) and just work within that. For production, at least for long-running server processes, again who cares about startup time?

seancorfield03:09:06

I probably only restart my REPL/REBL combo once a week at most.

ahungry03:09:20

yea, this would definitely be a niche for writing local utilities that are primarily used on cli

seancorfield03:09:01

Even for CLI utilities, I don't find Clojure's startup time too annoying -- assuming I don't have a lot of dependencies...

ahungry03:09:52

I recently wrote a C module for the janet language (a close-to-C clojure-like) that involved forking, and obviously a by product of C fork is the entire memory gets copied. Is there a best-way to do similar in clojure? if I wanted to perhaps do a fork() and then an eval, to ensure the eval executes in an isolated env?

seancorfield03:09:01

Threads are the closest on the JVM I think.

seancorfield03:09:14

FYI

(! 578)-> time bin/time.sh
Time is now 2019-09-07T03:23:15.487Z

real	0m1.913s
user	0m5.059s
sys	0m0.218s

Fri Sep 06 20:23:15
(sean)-(jobs:0)-(~/clojure)
(! 579)-> cat bin/time.sh 
#!/usr/bin/env clojure -Sdeps {:deps,{clj-time,{:mvn/version,"0.14.2"}}}
 
(require '[clj-time.core :as t])
(println (str "Time is now " (t/now)))

Fri Sep 06 20:23:19
(sean)-(jobs:0)-(~/clojure)
(! 580)-> 

ahungry03:09:21

I think the "run .clj files as if they were running over mod_php or CGI" and "run .clj files as if they were shell files" has a lot of overlap, but in either case, before I execute the clj, I would want to scoop up the entire existing environment and execute in a new pid that just discards all the stuff after

ahungry03:09:37

what was neat in the janet experiment, I was able to use mmap to relay the fork results back to the parent pid

ahungry03:09:45

without serializing the entire janet ENV VM stack

seancorfield03:09:54

Two seconds for a "simple" script... if the script has few dependencies and does a fair bit of heavy lifting, the startup time is not really an issue.

ahungry03:09:00

(ultimately it was to implement futures in that lang, since it has no concurrency or parellelism support)

seancorfield03:09:44

If I want a fast CLI "script", I just write bash 🙂

ahungry03:09:45

well, its not terrible, ultimately its a balance of work done vs startup time - if I wanted to reimplement a lot of the shell toolchain (ls, grep etc.) in clojure, each pipe target would add up

seancorfield03:09:23

"a lot of the shell toolchain" -- then just start a REPL and use it as your "shell"

seancorfield03:09:49

Don't context shift between bash and the JVM for every command. That makes no sense.

seancorfield03:09:20

That was one of the intriguing things about Boot, compared to Leiningen. With Boot you could start a REPL in your project and then run all the Boot tasks in your REPL.

seancorfield03:09:41

You paid the startup time once, and then it was your new "shell".

ahungry03:09:40

well, a more seamless integration is my idea - I notice very few clojure apps available in various *nix distro package managers for instance

seancorfield03:09:59

Very few JVM apps period, I would expect.

ahungry03:09:03

and I see a lot of stuff for rust rewriting old tools like grep -> ripgrep with performance boosts - I bet clojure could do better

ahungry03:09:22

but, to pull people out of their established environments into brand new ones, lots of people seem to avoid lisps

seancorfield03:09:06

It's nothing to do with Lisps really, it's about the JVM. If you want fast command-line stuff that gets run a lot, you don't use a JVM language.

seancorfield03:09:54

Rust, C, C++ -- they all benefit from tightly managing their own memory (and Rust makes that easier/safer because of the borrow checker). If I was writing stuff that targeted native code for speed, and wanted fast startup, I'd write in Rust. I like Rust.

seancorfield03:09:21

Clojure is designed to be used wherever Java is suitable. Not everywhere. There are places where Java is not suitable 🙂

ahungry03:09:30

I'm sure its a compound problem (jvm and lisp) - its funny, those in that space would tend to view both as a positive, those outside it, view both as a negative

ahungry03:09:03

I don't think theres many lisp things at all though in *nix land - stumpwm / common lisp, maybe gnu cash with scheme as the scripting engine

ahungry03:09:07

emacs obviously

seancorfield03:09:10

I've been entirely based on the JVM for about 22 years. Beyond my attempts to "learn a new (programming) language every year" I haven't even looked at Lisps elsewhere.

seancorfield03:09:55

I did learn Go (didn't like it) and Rust (loved it). Also Elm (but I don't do front end work, just like I don't do native/command line work).

ahungry03:09:21

nice, I tend to language hop but (despite coworker and community interest) have avoided rust a bit - I've been very down the lisp-hole (basic -> C -> c++/java -> php -> js -> common lisp / emacs lisp -> prolog -> clojure) - very lightly tinkered with scheme dialects and haskell and erlang, but I always settle into the lisp things the most and they hold my interest the best by far

ahungry03:09:47

well, I'm not so sure it matters the lisp as much as the tight repl driven development that I enjoy

ahungry03:09:34

I wonder if a jvm process calls a C jni / jna / jnr or whatever they're at now, and that calls fork(), if the jvm ends up duplicated or if it generates a nice big segfault

seancorfield03:09:44

My first Lisp experience was in... 1982 I think?

ahungry03:09:46

hah you have some years on me, that was the same year i was born - my light basic tinkering was sometime early 90s I want to say

ahungry03:09:04

I'm glad to see a bit of a lisp resurgence though (well, maybe my media bias makes it seem its so)

seancorfield03:09:24

My final year project at uni was to write an APL interpreter (in Pascal!) and my best friend wrote a Lisp interpreter (also in Pascal). Then I stayed on for a PhD in functional programming (language design and implementation) so I wrote another Lisp interpreter as the basis for that and then built a series of ML-like languages on top of it (so Lisp was my "assembler"). Back then I was doing SASL, Miranda, ML... as well as lots of Pascal and assembler (and some BASIC). When Haskell finally appeared as the culmination of all the university FP language experimentation, I was convinced it was going to take over the world! Hahaha...

seancorfield03:09:43

Meanwhile I got a job writing assembler and COBOL, then more assembler, and then C, C++, Java, CFML, Groovy, Scala, and finally Clojure (with a bunch of other niche languages along the way -- I worked for an actuarial company for a few months that used Prolog heavily, as well as Parallel C for the Inmos Transputer -- that was fun).

ahungry03:09:54

lol - well, maybe in time, functional languages seem to be on a slow upward trend

seancorfield03:09:25

Yeah, every language is sprouting functional features. I've actually given talks at CFML conferences about functional programming in CFML 🙂

ahungry03:09:40

have you ever found yourself using prolog paradigms like logic programming outside of the actual "prolog" space? (with other language prolog implementations etc)

seancorfield03:09:07

No, I've only really used Prolog for that sort of stuff.

seancorfield03:09:43

I love the description of Prolog in the Seven Languages book as a two year old child. You ask it a question and you either get the answer or just "no" 🙂

seancorfield03:09:57

"How old are you?" "no"

ahungry03:09:20

so accurate

seancorfield03:09:18

I did play with core.logic a bit when I was learning Clojure but it just doesn't really fit any of the problems I'm solving. But then I really use Clojure as a general purpose language for mostly boring stuff. https://www.infoq.com/presentations/Real-World-Clojure/

seancorfield03:09:42

Going back to CFML for a minute... https://github.com/seancorfield/ring-cfml 🙂 🙂

ahungry03:09:25

nice! that's pretty slick

ahungry04:09:54

https://github.com/ahungry/scratch/blob/master/java/HelloJNI.java - so, it doesn't seem (at a cursory glance) there is any inherent limitation or blocker at the java/jvm level that explicitly prohibits forking - the toy program seems to show the correct behavior

ahungry04:09:35

now to sprinkle on a little clojure

hiredman04:09:47

I believe calling fork will break the jvm

hiredman04:09:54

I haven't used jni much, but I seem to recall you can end up with a jvm where non-existent threads are holding locks, etc

ahungry04:09:16

(if (= 0 (HelloJNI/forkYea)) "child" "parent")
         6                                                                                                                                                                
         All done?                                                                                                                                                        
         3                                                                                                                                                                
         All done?                                                                                                                                                        
         "parent"
         "child"

ahungry04:09:17

it does seem to be inconsistent/odd when running via lein run, but I think it may have some untapped potential

hiredman05:09:40

Fork gives you the same memory, but not the same threads, so for example your child jvm doesn't have a GC thread running

ahungry05:09:38

thanks, I definitely appreciate all info regarding this - https://github.com/ahungry/fork-yea is what I'm toying with atm - I think if I want to mimic the old mod_php - a lack of GC is just fine, the forked process would be a short-lived eval and die type thing

ahungry05:09:08

same for a CLI launcher that I want to avoid having the long-lived host "corrupted" by requesters running against it

Aklscc09:09:16

When I use lein run, the for-loop doesn't not run unless I will println it. Why is it?

borkdude09:09:54

for is lazy

borkdude09:09:13

if you want to force evaluation of side effects, use doseq or run!

💯 4
borkdude09:09:26

forcing a lazy seq can be done using doall

Aklscc10:09:52

How to use clojure.spec.alpha/fdef, I define some specs for a function, but when run it, the specs doesn't work:

(defn like [name]
  (println name))

(s/fdef like
        :args int?)
(like "apple")
;; just print apple
And the format works for macro.

sogaiu10:09:59

have you used instrument to enable the fdefs? https://blog.taylorwood.io/2017/10/15/fspec.html

Aklscc10:09:32

@sogaiu It's a great job. Thank you~

borkdude11:09:56

related, if you want to unit test your fdef: https://github.com/borkdude/respeced 😉