[HN Gopher] Learning Common Lisp to beat Java and Rust on a phon... ___________________________________________________________________ Learning Common Lisp to beat Java and Rust on a phone encoding problem Author : medo-bear Score : 165 points Date : 2021-10-01 17:42 UTC (5 hours ago) (HTM) web link (renato.athaydes.com) (TXT) w3m dump (renato.athaydes.com) | tyingq wrote: | Not the same phone encoding challenge, but an interesting feature | of the bsd globbing built into bash. The map is the typical map | of letters on a touch-tone phone..where "2" can be a, b, or c. | | All letter combos for the number "6397": | #!/usr/bin/bash m[0]=0;m[1]=1;m[2]="{a,b,c}";m[3]="{d,e,f}" | ;m[4]="{g,h,i}";m[5]="{j,k,l}"; | m[6]="{m,n,o}";m[7]="{p,q,r,s}";m[8]="{t,u,v}";m[9]="{w,x,y,z}" | var=$(echo ${m[6]}${m[3]}${m[9]}${m[7]}) eval echo $var | eggy wrote: | Before Python became ML's darling, there was Lush: Lisp Universal | SHell by none other than Yann LeCun and Leon Bottouk[1], and I | thought my Lisp knowledge was going to finally pay off ;) I've | since moved on to J and APL. SBCL is amazing and aside from | calling C directly, it is still very fast. Python is slow without | its specialized libraries from the C-world, but SBCL produces | short, readable code that is pretty efficient. There ECL[2] for | an embedded Lisp, or uLisp[32] for really small processors like | the ESP chips. | | [1] http://lush.sourceforge.net/ [2] https://common- | lisp.net/project/ecl/ [3] http://www.ulisp.com/ | pvitz wrote: | I am using J on and off, but I am not aware of a ML package for | it. Are you writing all algorithms yourself or could you | recommend a package? | ruste wrote: | I'd also like to know. I'm considering writing some for fun | in GNU APL right now. | liveranga wrote: | There's a handful of resources around that look fun though I | haven't dug into them yet. | | There's this on the J wiki: https://code.jsoftware.com/wiki/U | ser:Brian_Schott/code/feedf... | | And there's this YouTube series for APL: https://youtube.com/ | playlist?list=PLgTqamKi1MS3p-O0QAgjv5vt4... | kragen wrote: | This is a pretty introductory CL article, mostly a commentary on | Norvig's solution to the problem. Still, I learned about the #. | readmacro from it. The conclusion: "[The Lisp implementation] was | the fastest implementation for all input sizes except the largest | one, where it performed just slightly worse than my best Java | implementation." GH repo at | https://github.com/renatoathaydes/prechelt-phone-number-enco.... | Sounds like he was mostly measuring the performance of the SBCL | bignum implementation. | xxs wrote: | The tests are just few seconds, it's measuring bootstap, | compilation time for the most of the part. | kragen wrote: | It's true that they're just a few seconds, but if he were | measuring compilation time then none of the Java tests would | come in under 10 000 ms, and (although the graph isn't | usefully labeled) it looks like they're around 100 ms. | tehjoker wrote: | I tried experimenting with Common LISP but never understood how | one was supposed to deploy a finished binary to the end user. It | seemed like you were supposed to load up an environment? It made | me leery of continuing. | vindarel wrote: | See this recipe: https://lispcookbook.github.io/cl- | cookbook/scripting.html The core of it is to call "sb-ext:save- | lisp-and-die" after you loaded the system, and the portable way | across implementations is to use asdf:make, with 2 lines on | your system declaration (.asd). | aidenn0 wrote: | For most common-lisp implementations you can just save the | current state as an executable, along with specifying an entry- | point. | | Obviously doing this from your dev environment is a bad idea, | so most people write a script that loads the system and dumps | the image. Or you can use one that someone else has already | written. | | Not all lisps work this way (most notably ECL does not), and | you can use ASDF's program-op to create an executable directly | from a package definition, which should work on any | implemntation supported by ASDF. | tehjoker wrote: | Thanks, the saving the dev environment part was the part that | scared me. It certainly helps to have someone tell you you're | not crazy. Maybe I'll give it another try sometime! | aidenn0 wrote: | One of the things I've learned with powerful tools is that, | by their nature, they empower you to do bad things almost | as much as they empower you to do good things. | | It's even alluded to in the ruby bare-words example in this | famous talk[1] | | 1: https://www.youtube.com/watch?v=3se2-thqf-A | gibsonf1 wrote: | We have a makefile and: sudo make it creates an executable. | | Its slightly tricky in that the whole make process can only | happen on a single thread, so you have to turn off all parallel | threads during make - so we have a key on many functions :make | that turns off parallel for using make. Ultimatel the make file | uses (asdf:make :package-name) to compile to an exe. The other | tricky part is to get a web server in the exe to work, and | thats handled like this: | | (handler-case (bt:join-thread (find-if (lambda (th) | #+os-unix (search "clack-handler-woo" (bt:thread-name th)) | #+os-windows (search "hunchentoot" (bt:thread-name th)) | ) (bt:all-threads))) ;; Catch a | user's C-c (#+sbcl sb-sys:interactive-interrupt | #+ccl ccl:interrupt-signal-condition #+clisp | system::simple-interrupt-condition #+ecl | ext:interactive-interrupt #+allegro | excl:interrupt-signal () (progn | (format *error-output* "Aborting.~&") (stop) | (uiop:quit))) (error (c) (progn (format t "Woops, | an unknown error occured:~&~a~& - restarting service..." c) | #+os-unix (trivial-shell:shell-command "service trinity | restart")) )) | xondono wrote: | Some weeks ago I tried to start learning Lisp too. | | I found it odd, but I've seen weirder. Then I tried the simple | example of writing a function to compute the nth fibonacci number | (it's almost the first example). After testing some numbers, I | thought it seemed slow, so I quickly implemented the same | function in Rust (with recursion too). | | Rust took less time in compiling, and computing fib(n) for n | 1..50 than Lisp to compute fib(50). Maybe I did something very | wrong, but for now I'd rather learn Rust better. | xondono wrote: | Maybe it came out wrong (English is not my first language). | | I did not mean to say that I believe it inferior or anything, | just that I felt writing performant code in Lisp is non trivial | to learn, while rust feels pretty natural (to me). | | I'll try again at some point, but I don't think it's a wise | investment of time at this point, I'd rather be "fluent" in | rust first, then maybe try lisp. | rscho wrote: | Maybe not knowing how to use a tool efficiently is a good | reason to refrain from strong criticism? | Jtsummers wrote: | The only way anyone but you can know if you did anything wrong | is if you include your code, and your common lisp | implementation. They don't all have the same performance. | remexre wrote: | I believe that unless you put the inline pragma on the Lisp | function, the compiler isn't allowed to optimize the recursion. | | In general, Common Lisp doesn't favor recursive functions (as | opposed to Scheme), and as far as I know, most implementations | don't put huge amounts of effort into optimizing it. | | I'd be interested to see whether the same written using the | loop (or iterate:iter) macros would still be as slow. | gibsonf1 wrote: | If you make it tail recursive, its insanely fast in common | lisp. (sbcl) | jonsen wrote: | How would you code a tail recursive Fibonacci in LISP? | gibsonf1 wrote: | I haven't looked at trying and don't really have time to, | so I don't know, but with lisp, you can make anything | fast as a general rule, very very fast. (at least with | compiled sbcl) | lowbloodsugar wrote: | If you were a non LISP programmer and thought "Fibonacci | using recursion", you might think you'd have something | like fib(a)=fib(a-1)+fib(a-2), and scoff at its possible | performance, and also wonder how that would be tail call | optimized. But as a LISP programmer, you'll think of this | in the way a Java programmer would think of a loop, and | just treat this as a count over two values n times, | adding along the way. And this is why I don't like LISP: | because I have programmed assembly and built up from | there, and doing loops using tail call recursion seems to | be a lie. It is a faulty abstraction. It is a loop. You'd | be better off doing it in a loop, in any sane language, | and if you do it right in LISP, the code will generate a | _loop_ in assembly language. Just the fact that this | thread devolved into _how to specify compiler options_ so | that it actually generates that loop shows how absurd the | whole thing is. | oconnor663 wrote: | I think the appeal of expressing loops with recursion is | that it lets you to avoid mutation. But I agree with the | point that Rust folks frequently make, about how being | able to mutate things safely is a lot nicer than avoiding | all mutation. | emptybits wrote: | There may be a faster or cleverer way to do it but here's | a basic tail recursive fibonacci: | (defun nth-fibonacci (n &optional (a 0) (b 1)) | (if (= n 0) a (nth- | fibonacci (- n 1) b (+ a b)))) | gibsonf1 wrote: | WEB> (time (nth-fibonacci 9999)) Evaluation took: 0.005 | seconds of real time 0.004520 seconds of total run time | (0.000168 user, 0.004352 system) 100.00% CPU 13,120,881 | processor cycles 4,716,256 bytes consed | | 207936082371334980721126489886428368250870360940159031196 | 829458665285014234556866489274560343052265155917573432971 | 901580106247942672509731761338101799027380382317897483462 | 355564831914315919245323944200280678103204087244146934628 | 490626683870833080482509206544933408787332263775808474463 | 248737976037347946482581138586315504040810172603812029199 | 438923709428526016473982135544790818235937154295669451493 | 129936648467790904377992847736753792842706601751346648332 | 663776986420121068913557911418727769340808035049567940946 | 482928805660563647181876626689707585373833526774208355741 | 559456585420036347653245410061210124467856891714948032624 | 086026930912116019739382294466360499015319632861596990778 | 804277202892355393296718771829156434190791865251186788568 | 216008975201710704994376570673424008710839088118009762597 | 274318205395542568694608153559184582533982343823604357627 | 598231798961167484242695459246332046141379928508143520187 | 384809235815539889908971514694061316956144977837207434613 | 737562186851068568260906963398154909212537145372418669116 | 042505973537478237332681781821985092402269558264160166900 | 847498160728435824886131848299053831501800478443537515542 | 015738331055219809981238332532612286898240517778465884610 | 797908078283671323847984517940110765690575221586803789615 | 321608583872238829743804839319295412221008003135806885850 | 025988795664632214278204484925650731065958088374016489964 | 235633861097820456341224678729218456064091743606356182168 | 838125623216644428229525375774927153653211342045306867424 | 354545051032697681443701184949063902549349423589040315098 | 773697224370533831653603885951169802459279352259015376349 | 256548723808771830083010745694440024264364147569050945350 | 728047646844921056800247399144905559043913692186963870929 | 181892461571034503870502293006032416114107074539600801709 | 282779518347632167052424858208014238665266338160829214428 | 830954632590804718193292017101478280252213856563402074897 | 963176632788722076077910344317001127535588134788887275038 | 253890668230986833556957181378678829821117107964227067785 | 369131923427333645567279280189539891531060473797412807940 | 91639429908796650294603536651238230626 WEB> | Jtsummers wrote: | You may want to put extra spaces in front of those lines, | and also manually break up that massive number. | weavie wrote: | SBCL doesn't optimize tail calls by default. | | If I recall you have to set the optimization level prior | to compiling the function using `(declaim (optimize | xxx))` - where xxx is something I've forgotten. Perhaps | someone can come along and point out what xxx should be? | gibsonf1 wrote: | Oh thats interesting!, we don't use that but use tail | recursion extensively | | https://0branch.com/notes/tco-cl.html#sec-2-2 | Jtsummers wrote: | ; disassembly for NTH-FIBONACCI ; Size: 94 bytes. | Origin: #x2264E531 ; NTH- | FIBONACCI ; 31: 498B4510 MOV RAX, | [R13+16] ; thread.binding-stack-pointer | ; 35: 488945F8 MOV [RBP-8], RAX ; 39: | 488B55F0 MOV RDX, [RBP-16] ; 3D: 31FF | XOR EDI, EDI ; 3F: E8AC343BFF CALL | #x21A019F0 ; GENERIC-= ; 44: | 750A JNE L0 ; 46: 488B55E8 | MOV RDX, [RBP-24] ; 4A: 488BE5 MOV | RSP, RBP ; 4D: F8 CLC ; | 4E: 5D POP RBP ; 4F: C3 | RET ; 50: L0: 488B55F0 MOV RDX, [RBP-16] | ; 54: BF02000000 MOV EDI, 2 ; 59: | E8C2323BFF CALL #x21A01820 ; | GENERIC-- ; 5E: 488BC2 MOV RAX, RDX | ; 61: 488945D8 MOV [RBP-40], RAX ; | 65: 488B55E8 MOV RDX, [RBP-24] ; 69: | 488B7DE0 MOV RDI, [RBP-32] ; 6D: | E84E323BFF CALL #x21A017C0 ; | GENERIC-+ ; 72: 488BF2 MOV RSI, RDX | ; 75: 488B45D8 MOV RAX, [RBP-40] ; | 79: 488BD0 MOV RDX, RAX ; 7C: | 488B7DE0 MOV RDI, [RBP-32] ; 80: | B906000000 MOV ECX, 6 ; 85: FF7508 | PUSH QWORD PTR [RBP+8] ; 88: E99507DBFD | JMP #x203FED22 ; #<FDEFN NTH-FIBONACCI> | ; 8D: CC10 INT3 16 | ; Invalid argument count trap | | Looks like it's doing tail call optimization to me, this | is without doing anything special with declaim. Note that | where it returns to the top is with _JMP_ not _CALL_. | | http://www.sbcl.org/manual/index.html#Debug-Tail- | Recursion | weavie wrote: | Ah, so as long as optimize is 2 or less you should get | tail call recursion. I found when I tried it (several | years ago) the default was debug optimize. | | I wonder if different installs (or perhaps it's Slime) | sets this value to different levels. | | Something to bear in mind anyway.. | gibsonf1 wrote: | Wow, that worked, thanks!!! | | WEB> (declaim (optimize (debug 0) (safety 0) (speed 3))) | | NIL | | WEB> (defun nth-fibonacci (n &optional (a 0) (b 1)) | (if (= n 0) a | (nth-fibonacci (- n 1) b (+ a b)))) | | WARNING: redefining LOBE/SRC/WEB::NTH-FIBONACCI in DEFUN | NTH-FIBONACCI | | WEB> (time (nth-fibonacci 9999)) | | Evaluation took: 0.002 seconds of real time 0.001887 | seconds of total run time (0.001887 user, 0.000000 | system) 100.00% CPU 5,476,446 processor cycles 4,715,760 | bytes consed | Jtsummers wrote: | Yep, that's how you'd do it. So long as your CL | implementation supports tail call optimization, that will | be on par with the same algorithm using _loop_ or another | looping construct. | aidenn0 wrote: | That's pretty darn close to the single worst benchmark you | could use for CL vs. rust. | | It's going to make a lot of memory allocations and make a lot | of uninlinable function calls, both of which are places that I | would expect rust to have an advantage. That being said, I'd be | curious to see your rust version, as just the number of bignum | operations done for fib(50) is going to take a long time. | fsckboy wrote: | I don't know for certain at all, but I'd propose that you are | being downvoted for not going far enough with lisp to grasp the | point of lisp, which has more to do with recursive data | structures--and data that is code--than just recursive | procedure calling. | | Factorial and Fibonacci as a pair are good introductions to the | issue of recursion--and lisp had recursion back in the dark | ages before other languages did--but they're aren't what lisp | is about. | | so, were some other newbie to read your comment, it might | really put them off learning lisp for entirely the wrong | reasons. | | cheers :) | get52 wrote: | Common Lisp? Yuu mean the language that got horsefucked like 30 | years ago and barely anyone uses? I'll pass lol | Decabytes wrote: | Is it weird that I don't like Common Lisp at all but I like | Scheme a lot? I just never liked Lisp 2s and separate name spaces | for functions and variables. But really that is the biggest issue | for me. I'm sure if only Common Lisp existed it wouldn't bother | me at all. | | That being said, I think CL is a fantastic language and there is | a lot more libraries out there to do useful things than in | scheme. My C programming is really weak so I find it challenging | whenever I come across a library in c that isn't already wrapped | aidenn0 wrote: | Not at all weird. I'm the opposite; I never liked Lisp 1s, and | whenever I use one I always end up shadowing function or macro | bindings by accident. | sleibrock wrote: | I'm a bit the same. I've been writing Racket for a number of | years now and looking back at Lisp I see a lot of ugliness that | I don't really think I enjoy. | | Racket has a nice package manager and module system that kind | of works for me, and the documentation is honestly some of the | best I've ever used, if not my favorite. Comparatively, I've | tried using GNU Guile and found the online documentation to be | horrendous, and trying to find online documentation for what's | considered to be the "standard library" in Common Lisp still | confuses me. | | I love seeing people use CL and other Lisp-likes in the wild, | and Norvig was a big inspiration for me. | bitwize wrote: | Not really. I'm a diehard Schemer as well, for the same | reasons: the Lisp-1 nature helps you treat procedures as true | first-class values and fits in with Scheme's more functional | style. Something you can do in CL also, just with a bit more | ceremony. And CL programmers are more "haha, setf go brrrr" | anyway. | | That said, I'd rather use CL by far than any other language | _except_ Scheme, and there are cases where CL is the right | thing and Scheme is not. The most brilliant, beautiful, joy to | maintain enterprise system I 've ever seen was written in CL. | orthecreedence wrote: | I miss my common lisp days, and I think rust being able to export | C ABIs makes it a really great companion language for CL. I also | think common lisp (or really, any _fast_ lisp) is a really great | tool for game development. The fact that you can redefine a | program as it 's running really helps iterate without having to | set up your state over and over. Pair that with a fast, low-level | language that can handle a lot of the lower level stuff (been | trying to find time to look at Bevy on rust), and you have a | killer combo. | | The main reason I stopped using lisp was because of the | community. There were some amazing people that helped out with a | lot of the stuff I was building, but not a critical mass of them | and things just kind of stagnated. Then it seemed like for every | project I poured my soul into, someone would write a one-off copy | of it "just because" instead of contributing. It's definitely a | culture of lone-wolf programmers. | | CL is a decent language with some _really_ good implementations, | and everywhere I go I miss the macros. I definitely want to pick | it up again sometime, but probably will just let the async stuff | I used to work on rest in peace. | kjgkjhfkjf wrote: | It's not quicker to write code in Lisp when you don't know Lisp, | and you have to learn it to make a change to some Lisp code that | someone randomly added to the codebase. | | This is true of all niche languages that are supposedly quicker | to use than regular mainstream languages. | [deleted] | Jtsummers wrote: | Your first part is tautological but worthless. It's never | faster to write in a language you don't know versus one you do | know. | | EDIT: To amend the preceding sentence, it's _almost_ never | faster. There are probably languages which people know that are | sufficiently in conflict with the problem domain that they | could be faster in some other new-to-them language versus the | one they know for specific problems. | fsckboy wrote: | APL comes to mind as a language that could be faster to learn | and use for a particular task, if your particular task | involves a lot of pure matrix combination and manipulation. | | though, the fact that it wants its own keyboard is a bit of a | hurdle :) | wedesoft wrote: | If you're coming from Java, you might want to look at Clojure. It | has immutable datastructures and Java interop. | afandian wrote: | I just used a Clojure library from a Kotlin project via Java | interop. And it wasn't even weird. | medo-bear wrote: | From the author: | | > Even though Clojure might be a more obvious choice for | someone, like me, who is used to working with the JVM, I | actually wanted to try using something else with a lighter | runtime and great performance. | facelessuser wrote: | Unless you go out of your way in writing non-idiomatic Clojure | like in Java with parenthesis Clojure, Clojure will the slowest | of the three by far. | kubb wrote: | I found Common Lisp to be surprisingly ahead of its time in many | regards (debugging, repl, compilation and execution speed, | metaprogramming), but unfortunately it doesn't have a large | community, and it's showing its age (no standard package | management, threading not built into the language). It's also | dynamically typed which disqualifies it for large collaborative | projects. | gibsonf1 wrote: | It has great package management with | https://www.quicklisp.org/beta/ and some truly great and high | quality libraries, especially Fukamachi's suite of web | libraries and so many others. Woo, for example, is the fastest | web server. https://github.com/fukamachi/woo (Faster then the | runner up Go by quite a bit) | | For parallel computing, we use: https://lparallel.org/ Its been | great at handling massive loads accross all processors | elegantly. And then for locking against overwrites on highly | parallel database transactions we use mutex locks that are | built into the http://sbcl.org/ compiler with very handy | macros. | Mikeb85 wrote: | Slightly off-topic but I'm in awe of Fukamachi's repos. That | one person has built so much incredible stuff is amazing to | me. Not sure it's enough to get me using CL all the time, but | it's very impressive. | gibsonf1 wrote: | The math library we use is incredibly fast with quaternion | matrix transformations: https://github.com/cbaggers/rtg- | math/ | | The only gaps we've had with our production code and lisp | is PDF (we use the java pdfbox), translating between RDF | formats (also a java lib) and encrypting JWP tokens for | PKCE dPop authentication (also java) | | The complete conceputal AI system and space/time causal | systems digital twin technology is all in common lisp | (sbcl) | | Also fantastic is the sb-profile library in sbcl that lets | you profile any number of functions and see number of | iterations and time used as well as consing all ordered by | slowest cummulative time. That feature has been key on | finding those functions that are slow and optimizing | leading to orders of magnitude speed improvements. | medo-bear wrote: | are you able to go into detail as to what sort of AI | technology you are using ? when you mention causal | systems do you mean causal inference ? | gibsonf1 wrote: | We basically build a space/time model of the world, where | systems are performing functions in events that take | input states and change them into output states such that | those events either causally trigger each other or the | causal link that the input states to an event means that | the event outputting that states is the cause of the | current event. | | The conceptual AI models operational concepts based on an | understanding on how human concepts work, inference and | automatic classification using those concepts, and then | learning new concepts. The operational side of the | digital twin uses functional specifications held | elsewhere, which is also true of the operational concepts | which use specifications in the form of conceptual | definitions. | | And the technology takes in RDF graph as data for input, | builds the digital twin model from that data with | extensive infererence, then expresses itself back out | with RDF graph data. (Making https://solidproject.org/ | the ideal protocol for us where each pod is a digital- | twin of something) | dunefox wrote: | Do you have links to more information? | gibsonf1 wrote: | We have a beta running on that framework: | https://graphmetrix.com The live app and Pod Server is at | https://trinpod.us | | We are working toward commercial launch in the coming | weeks. (We are adding Project Pods, Business Pods, Site | Pods with harvesting the sematic parse we do of PDFs into | the pod, so we handle very big data) | formerly_proven wrote: | > It's also dynamically typed which disqualifies it for large | collaborative projects. | | I've been around the block for long enough to see how far the | pendulum swings on this one. I'm guessing that it starts going | the other way soon. | ballpark wrote: | I'm not following, could you elaborate? | varjag wrote: | Dynamic/static typing came in/out of fashion several times | already. Any trend is temporary; neither kind of type | system is a help or impediment in collaboration. | brundolf wrote: | I think it originally tanked as a backlash against how | verbose and painful it was in Java and friends (as well | as the limited benefits because of things like | nullability) | | Modern static type systems are a totally different beast: | inference and duck-typing cut down on noise/boilerplate, | and the number of bugs that can be caught is dramatically | higher thanks to maybe-types, tagged | unions/exhaustiveness checking, etc. I think we've | circled around to a happy best-of-both-worlds and the | pendulum is settling there. | blacktriangle wrote: | Line noise is a red herring in the static/dynamic | comparison. You will still run into serious problems | trying to shove ugly human-generated data into your nice | clean type system. | | For mechanical things where you the programmer are | building the abstractions (compilers, operating systems, | drivers) this is a non-issue, but for dealing with the | ugly real world dynamic is still the way to go. | bitwize wrote: | Alexis King's "Parse, don't validate" is pretty much the | final word on using type systems to deal with "messy real | world data": https://lexi- | lambda.github.io/blog/2019/11/05/parse-don-t-va... | | tl;dr when used properly, static type systems are an | enormous advantage when dealing with data from the real | world because you can write a total function that accepts | unstructured data like a string or byte stream and | returns either a successful result with a parsed data | structure, a partial result, or a value that indicates | failure, without having to do a separate validation step | at all -- and the type system will check that all your | intermediate results are correct, type-wise. | blacktriangle wrote: | You're not the first, nor fourth for that matter, person | to respond to dynamic typing advocation with that blog | post, and it's an interesting post but it misses the | whole point. The problem is not enforcing rules on data | coming in and out of the system. The problem is that I | have perfectly valid collections of data that I want to | shove through an information processing pipeline while | preserving much of the original data and static typing | systems make this very powerful and legitimate task a | nightmare. | brundolf wrote: | They don't, though. | | For example: a technique I've used to work with | arbitrary, unknown JSON values, is to type them as a | union of primitives + arrays of json values + objects of | json values. And then I can pick these values apart in a | way that's totally safe while making no dangerous | assumptions about their contents. | | Of course this opens the door for lots of potential | mistakes (though runtime errors at least are impossible), | but it's 100% compatible with any statically-typed | language that has unions. | rewma wrote: | > The problem is that I have perfectly valid collections | of data (...) and static typing systems make this very | powerful and legitimate task a nightmare. | | What leads you to believe that static typing turns a task | that essencially boils down to input validation "a | nightmare"? | | From my perspective, with static typing that task is a | treat and all headaches that come with dynamic typing | simply vanish. | | Take for example Typescript. Between type assertion | functions, type guards, optional types and union types, | inferring types from any object is a trivial task with | clean code enforced by the compiler itself. | brundolf wrote: | Presumably the GP's data is external and therefore not | checkable or inferrable by typescript. This makes the | task less ideal, but still perfectly doable via | validation code or highly agnostic typing | rewma wrote: | > Presumably the GP's data is external and therefore not | checkable or inferrable by typescript. | | There is no such thing as external data that is not | checkable or inferable by typescript. That's what type | assertion functions and type guards are for. | | With typescript, you can take in an instance of type any, | pass it to a type assertion function or a type guard, and | depending on the outcome either narrow it to a specific | type or throw an error. | roca wrote: | Not a nightmare at all. For example, if you're doing JSON | processing in Rust, serde_json gives you great tools for | this. The serde_json::Value type can represent any JSON | value. You parse JSON to specific typed structures when | you want to parse-and-validate and detect errors (using | #[derive(Serialize, Deserialize)] to autogenerate the | code), but those structures can include islands of | arbitrary serde_json::Values. You can also parse JSON | objects into structs that contain a set of typed fields | with all other "unknown" fields collected into a dynamic | structure, so those extra fields are reserialized when | necessary --- see https://serde.rs/attr-flatten.html for | an example. | ballpark wrote: | I've found this to be a non-issue in Clojure with | specification validation. Some call this gradual typing | olau wrote: | People seem to interpret blacktriangle's post in a parser | setting. I don't know why, but if you're writing parsers, | you're explicitly falling into the category he's | mentioning where static types make a lot of sense. | | GP's claim was that Java was too verbose. But verbosity | isn't really the problem. There are tools for dealing | with it. The problem is a proliferation of concepts. | | A lot of business applications goes like this: Take a | myriad of input through a complicated UI, transform it a | bit and send it to somewhere else. With very accurate | typing and a very messy setting (say, there's a basic | model with a few concepts in it, and then 27 exceptions), | you may end up modeling snowflakes with your types | instead of thinking about how to actually solve the | problem. | brundolf wrote: | If you're referring to the "parse, don't validate" | article, it's using the word in a different sense. The | idea is that you make a data model that can only | represent valid values, and then the code that handles | foreign data transforms it into that type for downstream | code to handle instead of just returning a boolean that | says "yep, this data's fine" | mwcampbell wrote: | I'm not sure I understand what makes dynamic typing | better for handling real-world data. Yes, the data is | messy, but your program still has to be prepared to | handle data in a specific shape or shapes. If you need to | handle multiple shapes of data, e.g. varying JSON | structures, you can still do that with static types, | using sum types and pattern matching. | [deleted] | lamontcg wrote: | Most modern static languages have some way to hold onto a | dynamically typed object, stuff them into containers of | that dynamic type and do some basic type reflection to | dispatch messy dynamic stuff off into the rest of the | statically typed program. Sometimes it does feel fairly | bolted on, but the problem of JSON parsing tends to force | them all to have some reasonably productive way of | handling that kind of data. | brundolf wrote: | This has not been my experience. | dtech wrote: | Can you elaborate on that? As I see it dynamic was | popular in the 90's (Python, JS, Ruby), but outside of | that it's always been pretty much dynamic for scripting | and static for everything else. | varjag wrote: | Consider that first Fortran (statically typed) and Lisp | (dynamic) implementations date back to late 1950s. Since | then there was a constant tug of war between these camps, | including BASIC, COBOL, Smalltalk, Pascal, and trends | falling in and out of favour. | | All this however is rather orthogonal to the strengths of | type systems. CL type system, for instance, is stronger | than one of Java or C. | bcrosby95 wrote: | None of those languages were popular in the 90s. | [deleted] | AlchemistCamp wrote: | > It's also dynamically typed which disqualifies it for large | collaborative projects. | | Like Github or WordPress? | jcelerier wrote: | .. are those considered "good" ? github is meh at best | considering it's 13yo and the billions of dollars poured into | it, and wordpress, I don't think anyone can reasonably say | that it's a sane software. They are both good arguments | against dynamic typing imho (especially the latter). | medo-bear wrote: | common lisp supports type annotations. there is even an ML-type | language impletmented in it (see coalton). quick lisp [1] is | used for package managment, bordeaux-threads [2] for threading. | | 1. https://www.quicklisp.org/index.html | | 2. https://github.com/sionescu/bordeaux-threads | lkey wrote: | As with all of these forays, I applaud the author for learning | new things, but these benchmarks are primarily testing reading | from stdin and format printing to stdout, followed by testing | bignum impls as mentioned elsewhere. For this reason, the title | and benchmarks are a bit misleading. | xxs wrote: | ...also testing half-compiled code. JIT compiled tests that run | few seconds just discredit their authors. It's yet another | example of how not to perform microbenchmarks. | twicetwice wrote: | Worth pointing out for those who don't know that the Java | runtime does JIT optimizations of bytecode based on observed | runtime patterns. Blew my mind when engineers were talking | about "warming up the code paths" during startup of a large | Java service and I found out it wasn't joke/superstition. | xxs wrote: | Warming is a tiny part of microbenchmarking actually. | Knowing how and what the compiler optimizes, like purpose | lattice checks to ensure no extra array boundaries in the | code. Call site optimizations (class hierarchy analysis) - | single site (direct calls, inclines), dual site - guarded | checks, vs 3-5 - inline caches vs. full virtual calls. | Card-marking of stores for concurrent GC. L2 and L3 CPU | cache sizes awareness (certain dataset may fit and the | results are misleading)... There are a lot other techniques | to consider, including looking directly at the assembly | code. | | Microbenchmarking requires rather deep knowledge how the | JIT works, how the hardware - CPU/cache/memory operates, | the costs of calls certain system calls and what not. | mhh__ wrote: | I just tentatively found a 10% speedup in the D compiler | frontend by enabling profile guided optimization. | xxs wrote: | Java effectively always have guided compilation with | perf. counters and all. | eigenhombre wrote: | I write Clojure for food, and Common Lisp for fun. One reason for | the latter is CL's speed -- awhile back I compared a bit of (non- | optimized) Clojure code I wrote for a blog post with a rough | equivalent in CL, and was stunned that the Common Lisp code ran | about 10x faster. This made me curious as to how fast it could be | made if I really tried, and was able to get nearly 30x more[1] by | optimizing it. | | Clojure is definitely fast enough for everything I've done | professionally for six years. But Common Lisp, while having | plenty of rough edges, intrigues on the basis of performance | alone. (This is on SBCL -- I have yet to play with a commercial | implementation.) | | [1] http://johnj.com/from-elegance-to-speed.html | NoahTheDuke wrote: | Your post is a lot of fun! I have a fondness for these kinds of | deep dives. That being said, I feel like comparing the initial | unoptimized Clojure code to highly optimized Common Lisp is | kind of unfair. I wonder how fast the Clojure code could run if | given the same care. Maybe I'll give that a try tonight! | vindarel wrote: | It would be cool if you updated the link to the Cookbook in | your article from the old one (on Sourceforge) to the newer one | (on Github): https://lispcookbook.github.io/cl-cookbook/ Best, | lmilcin wrote: | I have implemented real algotrading framework in Common Lisp | (SBCL) that connected _directly_ to Warsaw Stock Exchange. | | The application was receiving binary messages from the exchange | over multicast, rebuilding state of the market, running various | (simple) algorithms and responding with orders within _5 | microseconds_ of the original message, at up to 10k messages | per second. | | With SBCL you can write a DSL and have ability to fully control | the resulting instructions (through vops). It is just the | question how dedicated you are to writing macros upon macros | upon macros. | | I used this to the fullest extent and the result was as good as | any hand optimized C/assembly. | | For example, the binary message parser would receive a stack of | complicated specifications for the messages in the form of XML | files (see here if you are curious: https://www.gpw.pl/pub/GPW/ | files/WSE_CDE_XDP_Message_Specifi...), converted XML to DSL and | then, through magic of macros, the DSL was converted to | accessors that allowed optimal access to the fields. Optimal | here mans the assembly could not be improved upon any further. | | Large parts of the application (especially any communication | and device control) was written in ANSI C but the Foreign | Function Interface makes integrating it into the rest of | application a breeze. | | I write all of this, because I frequently meet complete | disbelief from people that Lisp can be used in production. | | I personally think it is exactly the opposite. Lisps offer | fantastic development environment. The problem are developers | who are mostly unable to use Lisp for work effectively, I guess | due to too much power, freedom and complete lack of direction | on how to structure your application. ___________________________________________________________________ (page generated 2021-10-01 23:00 UTC)