LFE: Lisp Flavored Erlang
lfe.io
lfe.io
Some past submissions of clojerl:
I've been trying to keep track of them all: https://gist.github.com/macintux/6349828#alternative-languag...
Gleam is one of the more interesting recent additions:
What I'd really like to see is a Mix LFE compiler[7] that lets you write LFE modules in a bigger Elixir project[5], plus a set of mix tasks like `mix lfe.repl` that integrates with the other BEAM components of your Mix projects.
Long term I think it would be cool if the BEAM community comes together around the excellent tooling (mix, ExUnit, Iex) that the Elixir Team is bringing, I'd love to see other BEAM languages (LFE, Gleam[6]) with top notch integrations that let a mix project include them side by side.
I think the BEAM is a really cool piece of technology, and many of these new BEAM languages offer pretty neat advantages, I'm excited to see the interop story grow over time
[0] https://elixir-lang.org/getting-started/meta/macros.html
[1] https://elixir-lang.org/getting-started/comprehensions.html
[2] https://elixir-lang.org/getting-started/protocols.html
[3] https://elixir-lang.org/getting-started/basic-types.html#lin...
[4] https://elixir-lang.org/getting-started/basic-types.html#ano...
[5] This exists, but looks unmaintained https://hex.pm/packages/mix_lfe
http://www.petecorey.com/blog/2017/08/07/what-if-elixir-were...
It's much more pretty over there.
I haven't looked at Elixir but I have read a bit about Supervisors which I think are an excellent idea for fault tolerant systems.
However, the phrase "self healing" is thrown around a lot in talks about Elixir, as if it's a magical feature of the language, without much explanation as to what it means to universally heal from faults (I think maybe they mean self healing strategies that the programmer authors and implements, not some AGI that rewrites the code on the fly to fix it, lol, but that's my gripe, mostly the hype around that... and "coding for the happy path" which is not really a rigorous statement either... maybe someone can tell me what is the happy path for an algorithm in my head without knowing the algorithm, because that somebody is me, and I don't really know my algorithms until I have thought through the not-so-happy path... I mostly code to explore what I need to specify rather than describe some specification I already have.)
Maybe Elixir is not for me. Or maybe it is. I need to learn more, and go past the hype.
For me, Lisp is an alien technology with great power that will never be matched, except by another Lisp.
So it was great to find out about LFE! I even appreciate the separate var/fn namespaces, like nouns and verbs in natural language, e.g. fight the good fight, and I can see how it comes in handy, yet it's a new PL concept for me, so I'm still processing it.
It's not that complicated. A relatable example would be writing an API endpoint that receives data and then you do something with that data.
1. Write the endpoint so it works for the expected data.
2. You're done
That's it. You don't need to worry about anyone sending you malformed payloads or fuzzing your API. You can ignore it. They cannot exploit anything, they cannot crash BEAM. It will keep working.
Maybe they'll run you out of memory or CPU or bandwidth, but those are problems solved at other layers (unless you include rate limiting in your application).
Ignore all errors you do not need to explicitly handle. That's the whole point. It's wonderful.
Maybe you can do this in other languages with worker pools and supervisors, but it will be very expensive and high latency due to the cost of OS process forking and you're also wasting a lot of resource on context switches with that design anyway.
Both me and a friend were building an app that ingests cryptocurrency data and stores it in a database for later analysis. The important part was that we wanted data at regular intervals. I'm not much into all this but this is what was asked of us. He wrote the app in Python; I wrote it in Elixir.
Turns out the API's of the various exchanges are (or were) atrocious. We'd get responses ranging from weird error codes to malformed data to timeouts. API's would randomly change. It was a mess.
My app just chugged along, a process per request per api endpoint. When some of these endpoints 'misbehaved' the others just kept going.
His app kept crashing, restart, and so one misbehaving endpoint would cause trouble for all the other ones. He had to add try...catch statements and fix the problems. I would just look at my logs, update the happy-path code, push it, and recompile() in the REPL.
I also have a bunch of personal projects chugging along on my VPS. They actively handle requests a few times per hour at least, by me, and they're doing fine despite the fact that I was quick about it and only wrote the 'happy path'. There's crashes and errors all over the place, and yet when one part crashes the rest just keeps going. It's been a rare occasion where the entire supervision tree failed and the app gave up.
How about some computational task where coding only the happy path means accurate calculated results for 80% of the cases?
I think my problem is the marketing hype with Elixir around "happy path" and "self healing" are too broad and resemble hype more than feature description. I'd be happy if those terms weren't used outside of marketing literature, until we have an AGI that can manage complexity on its own :)
I just gave an example of how happy path programming works in a use case that is pretty common to my day to day. A lot of other stuff I do is similar enough that the same applies, and that's just my experience, not marketing hype.
Some articles might be a little over-excited, but I don't think I've generally read articles that claim Elixir is God's gift to programmers ;). And in situations where Elixir does help, it can be very exciting.
FWIW, I wasn't aware that Elixir is not optimal for CPU-bound tasks. I use Go for highly concurrent servers, but it uses goroutines not a process per request, and mutexes and/or persistent channels to synchronize shared memory access.
I guess Lisp is about beauty and poetic justice and it shall remain in that realm.
I'll look into Elixir. Seems to have a healthy and growing ecosystem.
I have programmed in Scheme before while taking a course using SICP. Final project was a Scheme interpreter. But I didn't have any epiphany/awakening like many others seems to have.
The second idea (not shared by the some Lisps like Scheme) is that of a system image which is modified in real time. This allows on the fly debugging, adding of new features, etc with no downtime.
Macros have worked there way into languages like Julia or Nim, while the system image idea is mostly constrained to Smalltalk and Common Lisp.
The best example of the power of macros is Racket, which has world class meta programming facilities and is probably the best language in existence for creating new languages, DSLs, and doing experimental PL research.
I've used both CL and Racket professionally, and they both shine in certain situations.
That being said, I've grown tired of the relative verbosity of both languages (minimal syntax has a high cost) and the performance and productivity cost of dynamic typing.
For most new engineering projects, I'd much rather use something like OCaml or Scala than CL or Racket. For scientific computing, I usually go with Julia or kdb+/q.
That being said, I think Racket especially shines in the development of internal business or research tools. The large number of high quality libraries (especially for GUIs) makes it a great choice for desktop apps.
I haven't really experienced the image feature, as I used Scheme. It doesn't sound like it would be consequential to how I program, but that might just be my ignorance of it.
My experience with the macro stuff is that they enable in-house implementation of language features like lazy evaluation (not possible in other languages without a lot of extra code), which we implemented during the course. But implementing features like that is not really something I need to do for my everyday programming.
For example, I used Racket macros at my old job as a bio informatics researcher at a hospital to design a DSL to specify bio informatics processing pipelines. You could either write it yourself, or you could use a GUI to manipulate a visual graph of operations and write out the pipeline using the DSL to a file.
These sort of DSL use cases are a lot more common than people think, mostly because they don't think of an API as a new language, merely as some code written in an existing one. When you realize that macros can be used to eliminate all repetition and all patterns, you start to think in a different way.
Designing proper interfaces with macros can be challenging though: you want to provide a nice syntax that is both composable with other macros, and clean.
Also macros allow the core language to be paired down and very simple. Stuff like Scheme, in which all desired features (OOP, actors, async, generators, etc) can be implemented by external packages.
But like I said, I don't use Lisp much anymore. Functional programming with higher kindred types goes a long way in matching the power of macros, albeit in a more structured way. Of course, type systems will always fall short of the unlimited power of macros, but for a lot of problems it's more than sufficient.
Think of Lisp as the prototypical programming language, the essence of transforming symbols into other symbols. Lisp forms the base, on top of which all other features can be implemented.
E.g. learning about red-black trees inspired me to write a decent C implementation of them, as something I could use. That ended up used in production all over the place, like in the ext2fsck program.
Undoubtedly, some of my classmates likely also had the reaction "when would I, in everyday programming, be rotating some nodes to balance a tree ..."
The best video I've come across thwt demonstrates images is https://youtu.be/3GEAINRCbJ4
TBH, one can use emacs to get a similar workflow in single file python projects. There are also julia-snail and slime variants in emacs for providing it for julia; none of which I've tested to comment much.
Note that Lisp existed for quite a while before macros. The regular syntax allowed for the evaluation algorithm of Lisp to be described as if it were a Lisp procedure itself, and it allowed the language to be extended with "FEXPR" routines.
There's exists no code that shows the full potential of Lisp because its potential lies not in the code itself, but in the way of working with it.
The language is interactive and image-based, meaning that you do not program in it by passing code through a compiler into executables, but instead you modify the running Lisp image until it contains the program you want. This is usually done by connecting the editor to the running Lisp image and sending commands to it: usually that is to compile-and-load (or compile-and-replace) individual forms/functions/variables, or whole files, or whole systems, or to work with the read-eval-print loop, or to interactively inspect objects, or to interactively resolve signaled errors via the debugger. This provides a lot of introspection into how the Lisp image and the program contained within works, and drastically reduces the feedback loop since the overhead of compiling-and-loading individual functions or even whole files is unnoticeable.
Disclosure: I am a Common Lisp programmer.
* Compojure-api (https://github.com/metosin/compojure-api) - for building web applications. Mix json-like, hiccup and clojure constructs for web applications and autogenerate Swagger documentation.
* LOL book (https://letoverlambda.com/). Extreme examples what you can do with macros and code modifications during compile time.
* Seesaw (https://gist.github.com/daveray/1441520) - library for building GUI apps with Clojure and Swing. Express GUI elements through declarative syntax. Qt and other libraries has similar feature, but is usually preprocessed with external tools.
* Scheme - MiniKanren (https://docs.racket-lang.org/minikanren/index.html)
Most of these things in regular languages would require modifying language parser or compiler, or adding external tool that will parse that code and generate new one; e.g. like React is doing with html chunks. Also, many Scheme/CL/Clojure implementations provide functions to modify syntax table in runtime, allowing you to alter how things are parsed. That is extremely hard in regular languages due unregular syntax constructs.
Try to build something with Clojure (bit easier to get started with than LFE, Common Lisp or others, except maybe Racket) and try to focus on a REPL heavy flow (with integration with your favorite editor) to see the full potential of Lisp.
Hacker news is written in Arc, which is a dialect of Lisp. There's an old copy of code base here:
I loved the little and seasoned schemer books, yet found both racket and CL somehow divergent and unappealing.
Does anyone here use it? I can’t find if it deals with continuations well. I’ve never used many languages that allow eloquent use.