Lisplog

Blogging in Lisp

Search

Feed Aggregator

Rendered on Sat, 30 Sep 2023 04:31:51 GMT  newer latest older 
Next udpate: Sat, 30 Sep 2023 05:00:00 GMT feeds

Joe Marshall

via Planet Lisp by on Wed, 27 Sep 2023 16:31:00 GMT

Greenspun's tenth rule of programming states

Any sufficiently complicated C or Fortran program contains an ad hoc, informally-specified, bug ridden, slow implementation of half of Common Lisp.
Observe that the Python interpreter is written in C.

In fact, most popular computer languages can be thought of as a poorly implemented Common Lisp. There is a reason for this. Church's lambda calculus is a great foundation for reasoning about programming language semantics. Lisp can be seen as a realization of a lambda calculus interpreter. By reasoning about a language's semantics in Lisp, we're essentially reasoning about the semantics in a variation of lambda calculus.

vindarel: I published 17 videos about Common Lisp macros - learn Lisp with a code-first tutorial

via Planet Lisp by on Fri, 15 Sep 2023 15:07:23 GMT

For those who don’t know and who didn’t see the banner :D I am creating a Common Lisp course on the Udemy platform (with complementary videos on Youtube). I wanted to do something different and complementary than writing on the Cookbook.

I worked on new videos this summer and I just finished editing the subtitles. I have added 17 videos (worth 1h30+ of code-driven content) about Common Lisp macros!

We cover a lot of content: quote, backquote and comma, “,@”, comparison with C macros, comparison with functions, GENSYM and variable capture, useful patterns (call-with...), compile-time computing, read-time evaluation... (full summary below)

New: 17 videos to learn Lisp macros

I recorded the last one, about the MACROSTEP tool, inside the Lem editor. It’s short, you should have a look at how this new editor looks like. (I’m very excited about it. Did I say I started develop a Magit-like plugin for it?)

Who is this course for?

The whole course is for beginners in Lisp, although not total beginners in programming. This chapter is, logically, a bit more difficult than the others. If you didn’t write small Common Lisp programs yet, be gentle with yourself and stop if you don’t understand. (you can ask questions in the Udemy forum, of course) In your case I would advise to watch the introductory one, the comparison with C macros, the video on QUOTE, the “functions VS macros” one, and then carry on at your rhythm. Be sure to work on the previous chapters before tackling this one.

Content

This is what we see on the topic of macros. For a full overview of the course, what I want to do next (if you subscribe now, you’ll get new content for the same price) and read others’ feedback, see its GitHub project page (there are six more chapters including getting started, functions, iteration, condition handling...).

Table of Contents

7.1 A quick intro (FREE PREVIEW)

Macros do not evaluate their arguments and expand to new code at compile time. What does that mean? A quick intro before diving deeper.

7.2. A comparison with C macros (FREE PREVIEW)

Lisp macros are NOT manipulating text, unlike C. Text leads to many unnecessary problems. We have a fun tour of a trivial need yet complicated issue in C that is easily done in Common Lisp.

7.3 QUOTE (FREE PREVIEW)

QUOTE does not evaluate its argument.

What we see: how to use QUOTE outside macros. Data takes the shape of code. We pair it with eval and we go full circle. We introduce the need to extrapolate values inside a quote.

7.4 Backquote and comma

What we see: how we extrapolate variable values. How they can help create data structures. Real world examples.

7.5 How to spot you are using a macro

Four tips to recognize if you are using a function or a macro, and why it matters.

7.6 Functions vs macros

Macros do NOT replace functions!

What we see: they are not higher-level functions. The subtle but logic need to re-compile functions using macros.

Introducing MACROEXPAND.

Keeping compile-time computing in mind (more on that later). A look at a function’s disassembly. So... you might not need a macro yet ;)

7.7 COMMA SPLICE ,@ the third most important macro mechanism

What we see: when use it, understanding the common error messages, passing body forms to our macro. Our first macro model.

7.8 &body and other macro parameters. Our second macro model.

What we see: how &body differs to &rest. Macro parameters: lots of possibilities, but some conventions carry meaning. Our own DOLIST macro. Our second macro model you can follow.

7.9 Putting this together: with-echo macro. Macroexpand in use.

We build our first macro with backquote and comma-splice, even a quote followed by a comma. We use macroexpand.

7.10 GENSYM -the simple fix to the most dangerous macros gotcha

What we see: what is variable capture and how to avoid it. Writing our own REPEAT macro. A little discussion about Common Lisp VS Scheme macros. GENSYM can be used outside macros too.

At this point you know enough to write all common macros. See the exercises for easy and not-so-easy ones.

7.11 CALL-WITH pattern: simplifying macros

We saw there can be subtle pitfalls when we write a macro. This pattern allows to offload most of the work to a function, which presents many advantages. We demo with our REPEAT macro.

7.12 Compile time computing

When writing macros, we have the full power of Common Lisp at compile time. This gives great tools to the developer: early type errors and warnings, faster runtime.

What we see: a simple example, writing a scientific macro for conversion of unit at compile time, existing libraries for that, introduction to dispatching macro characters and reader macros.

7.13 Lists VS AST

What we see: other languages don’t have macros but can manipulate Abstract Syntax Trees. Code as lists of symbols is not the same, we would need a third-party library to manipulate a Lisp AST proper. This doesn’t prevent us to develop crazy macros though, see this library adding Haskell-like type checking on top of Common Lisp, in pure CL macros.

7.14 Two example macros for compile-time computing

defstar allows to specify a function’s arguments’ types, Serapeum’s ecase-of does exhaustiveness type checking. At compile time, of course.

7.15 SYMBOL-MACRO

A symbol macro is not your everyday Lisp development tool, but it expands your toolbet. Again.

7.16 Read-time evaluation with #.

Macros occur at compile-time. But Common Lisp blurs the lines between read time, compile time and run time. This allows to execute code at READ time.

7.17 EDITOR TOOL: macrostep (FREE PREVIEW, Lem demo)

Macrostep is an editor extension that helps understand our macro expansions. It is only available in Sly and Lem. We demo with the Lem editor.

Thanks

Thanks for your support, it does make a difference (I am self employed, I don’t earn millions and I’d love to spend *even more time* on CL resources and projects). If you want to learn what I do for the Lisp community and why you should buy my course, read more on Github.

My complementary Lisp videos are on Youtube.

Don’t hesitate to share the link with a friend or a colleague :) Thanks, and happy lisping.

A demo about web development has been recorded and is coming.


ps: we just got a Dockerfile for CIEL, which is then easier to test, thanks to a “student” of my course. Thanks, @themarcelor. It will be on Dockerhub in due time.

The Udemy course by @vindarel is the best introductory material for a fast and practical intro to Common Lisp.

(thanks <3)

A wonderful course for someone with cursory knowledge of lisp. I’ve dipped my feet many times now, but always struggled to wrap my head around everything. This course really helped give me greater confidence in how to start a project. I really enjoyed the focus on having an executable early. The Lisp-2 reveal was beautiful and made me finally understand the difference. Thanks a lot!

Simon, August of 2023. (thanks <3 )

Old question: elm-spa: Cant Refresh the page or manually enter url

via Elm - Latest posts by @LTstrange LTstrange on Sat, 26 Aug 2023 14:50:09 GMT

Hi,
I’m pretty new to elm and front-end. And I encountered the same problem which has been solved in this previous post 4088, but it didnt work for me.
edit: Neither worked on github.io, here is my github repository, in case you want to check it.
I’m using vscode’s live server extention to host in local. And I have tried to set the proxy of it based on that post:

# settings.json
{
    "liveServer.settings.proxy": {
        "enable": true,
        "baseUri": "/index.html",
        "proxyUri": "http://localhost:5500/index.html?"
    }
}

And also set it in varies way. It never work.
The elm code I wrote is the guide code:

-- Main.elm
module Main exposing (main)
import Html exposing (..)
import Html.Attributes exposing (class, src, alt, href)
import Browser
import Url
import Browser.Navigation as Nav


-- MAIN
main : Program () Model Msg
main = Browser.application 
    { init = init
    , update = update
    , view = view
    , subscriptions = subscriptions
    , onUrlChange = UrlChanged
    , onUrlRequest = LinkClicked
    }


-- MODEL
type alias Model =
  { key : Nav.Key
  , url : Url.Url
  }

init : () -> Url.Url -> Nav.Key -> ( Model, Cmd Msg )
init _ url key = ( Model key url, Cmd.none )


-- UPDATE
type Msg
  = LinkClicked Browser.UrlRequest
  | UrlChanged Url.Url

update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
  case msg of
    LinkClicked urlRequest ->
      case urlRequest of
        Browser.Internal url ->
          ( model, Nav.pushUrl model.key (Url.toString url) )

        Browser.External href ->
          ( model, Nav.load href )

    UrlChanged url ->
      ( { model | url = url }
      , Cmd.none
      )


-- VIEW
view : Model -> Browser.Document Msg
view model =
  { title = "URL Interceptor"
  , body =
      [ text "The current URL is: "
      , b [] [ text (Url.toString model.url) ]
      , ul []
          [ viewLink "/home"
          , viewLink "/profile"
          , viewLink "/reviews/the-century-of-the-self"
          , viewLink "/reviews/public-opinion"
          , viewLink "/reviews/shah-of-shahs"
          ]
      ]
  }

viewLink : String -> Html msg
viewLink path =
  li [] [ a [ href path ] [ text path ] ]

-- SUBSCRIPTIONS
subscriptions : Model -> Sub Msg
subscriptions _ =
    Sub.none

Then compile it use elm make and uglifyjs to main.js, which linked in index.html:

<!-- index.html -->
<!DOCTYPE html>
<html>

<head>
	<meta charset="UTF-8">
	<script src="main.js"></script>
	<link rel="stylesheet" href="./assets/style.css">
	<link rel="icon" href="assets/logo.svg">
</head>

<body>
	<script>var app = Elm.Main.init();</script>
</body>

</html>

Is there any information I need to offer? Just mentioned below.
Please help me,
LTstrange.

Nicolas Hafner: I've opened up a Patreon - Confession 93

via Planet Lisp by on Fri, 25 Aug 2023 12:45:54 GMT

https://studio.tymoon.eu/api/studio/file?id=2570

I've been debating opening up a Patreon for many years and I've always been hesitant about accepting donations from people, but I think it's finally time to change my mind on that!

Why make a Patreon now?

I've been working full time on Kandria and associated projects since 2020, and continue to do so today. All of the work that I've done as part of that has been released as open source software, including Kandria itself as well as the engine it runs on, Trial.

Since the release, I've mostly focused on support and the pre-pre-production of my next title, which primarily involves adding new features to Trial that are necessary to create a full-3D game. I can't yet announce much about the game itself, other than that it is a character action game, meaning it features third-person hack and slash focused on slick and satisfying combat.

Unfortunately the release of Kandria has not gone as well as I would have liked, and revenue from it is minimal. Most months I receive only about 200 bucks from Steam, which as you might imagine is not enough to sustain myself full-time, let alone any other artists that are necessary to produce another high-quality game.

So I am finally opening myself up for continued public funding. I know people have wanted to support me in the past before, and I've always been hesitant about accepting that. But now with the financial pressure increasing, I think it's finally time to let people that want to be generous, actually be generous!

What can I expect from this?

Aside from simply funding my existence and allowing me to continue to produce high-quality open source libraries and applications, art, writing, and games, I'm also committing to a couple of extra features:

  • Every month I'll produce a patron-only update about what's currently happening with the development. This will also include development insight and details that won't be published elsewhere.

  • I'll also commit to a monthly art stream where I doodle around, and higher-tier patrons can request sketches from me.

  • Any patron will be able to submit their name or a name of their choosing for inclusion in the credits of any game in production during their backing.

  • Higher-tier patrons will also receive access to early game prototypes and demos.

  • You'll be able to directly ask me questions in the comments of the monthly updates and in the stream chat.

  • If you use Discord, you'll receive access to a special role and patron-exclusive chatroom on my Discord server.

  • An eternal feeling of debt and gratitude towards you.

What now?

Now I'm going to go back to working on Trial and the unannounced game. In the meantime, please consider backing me. There should already be a monthly update about the state of things out that's only accessible to patrons. In any case, thank you very much for your continued support, and I hope I'll be able to see you among the backer list soon!

Elm Bootstrap. Fixed aspect ratio broken in Safari

via Elm - Latest posts by @bwanab Bill Allen on Fri, 25 Aug 2023 11:32:46 GMT

Thanks for the reply. It did help to unblock me some. After moving the calc from the table body to the table it fixed the specific problem I mentioned. Now Safari behaves like Firefox and Chrome with respect to aspect ratio. But, now the viewport seems to have become strangely quantized where before in Chrome and FF it was pretty smooth. Alas.

Do you, or anybody else who might be reading this have any full samples of a browser application that uses the elm-bootstrap library? I haven’t been able to find any. I’m starting to wonder if maybe recoding with a different css library might be a better approach.

Useful Chompers

via Elm - Latest posts by @pit Pit Capitain on Thu, 24 Aug 2023 17:48:36 GMT

Done :slightly_smiling_face:

Smart Event Listeners

via Elm - Latest posts by @eberfreitas Eber Freitas Dias on Thu, 24 Aug 2023 13:24:30 GMT

I’m really enjoying the great content you’ve been sharing, @dwayne. Thanks a lot!

Elm Bootstrap. Fixed aspect ratio broken in Safari

via Elm - Latest posts by @passiomatic Andrea Peltrin on Thu, 24 Aug 2023 09:42:50 GMT

A few suggestions:

  • Set html and body elements to height: 100% so your UI will stretch to the full browser viewport height.
  • Move your link to bootstrap CDN to head
  • From the code it seems you are using a mix of Bootstrap grid and HTML tables, which may work - but ask yourself where you are telling the browser the amount of vertical space to use for each portion of the UI. It seems to me this is left to the browser to apply some defaults and so they comply differently.
  • I would do things a bit differently. The body is a display: flex with flex-direction: column container, which contains a header a main and a footer elements. main contains the table you are using for the game board.

Hope this help to unblock you a little more.

What sins make my first Elm app non-reactive?

via Elm - Latest posts by @Ron on Thu, 24 Aug 2023 01:47:53 GMT

I read only the first para and got some ideas (and made some silly mistakes) I will try to solve it again - and then read the full reply - thanks for the detailed reply

What sins make my first Elm app non-reactive?

via Elm - Latest posts by @Ron on Thu, 24 Aug 2023 01:45:38 GMT

Thanks for introducing me to Ellie app, I googled Elm formatters and evals but never found it.

Actually I want to write an RxJS clone and when choosing between learning Haskell or Elm - I chose Elm because Wikipedia lists it as FRP and also because I’d get visual feedback making webapps. It is surprising to know Elm isn’t FRP anymore.

The document's code seems missing

via Elm - Latest posts by @novid Novid Emami on Wed, 23 Aug 2023 21:11:59 GMT

You could have a look at URL handling with Browser.application or the missing part of the Elm guide :ok_hand:t2:

The document's code seems missing

via Elm - Latest posts by @LTstrange LTstrange on Wed, 23 Aug 2023 15:07:53 GMT

Hi,
I’m following the offical guide, and reading the URL Parsing chapter, but the Synthesis part’s code is TODO. So I come here to send a feedback.

Elm Bootstrap. Fixed aspect ratio broken in Safari

via Elm - Latest posts by @bwanab Bill Allen on Wed, 23 Aug 2023 14:29:26 GMT

I’ve built a simple Othello/Reversi app as a front end to a machine learning project I’ve been doing to implement an Othello playing bot. I’m not a front end dev, but I know a lot more about it now than I did a week ago :).
I want the Othello board to be based on the size of the browser and responsive to both width and height of the browser. This is a compromise that I think is needed since I want it to be playable on a small screen (phone) as well as a large screen. Width was easy, in fact it seemed to be the default behavior, but it took a bit of css research to figure out how to make height responsive. The problem is that while my solution works with Chrome, Vivaldi, and Firefox, it doesn’t in Safari and all the iPhone browsers (since they’re all required to use the same display engine). I don’t have an Android phone, so it hasn’t been tested there.

Here is the code that made height responsive:

reTbody : List String -> Playing -> Table.TBody Msg
reTbody board playing =
    Table.tbody [ Html.Attributes.style "height" "calc(75vh - 16px)"] (boardTable board playing)        

The game URL is here, if that would help to visualize it (sorry, it isn’t very pretty): http://reversi.k2bea.org.

Any ideas on why it wouldn’t work in Safari and how it could be made to work?

Useful Chompers

via Elm - Latest posts by @dwayne Dwayne Crooks on Wed, 23 Aug 2023 10:33:57 GMT

Thanks for finding these interesting ways to improve performance. I’d have to try them out myself at some point but also feel free to share your sample implementation.

Smart Event Listeners

via Elm - Latest posts by @dwayne Dwayne Crooks on Wed, 23 Aug 2023 10:25:18 GMT

Smart Event Listeners

Learn how smart constructors, JSON decoders, and “Parse, don’t validate” help you write better event listeners.

What sins make my first Elm app non-reactive?

via Elm - Latest posts by @mburri Max Burri on Wed, 23 Aug 2023 10:19:53 GMT

Hey Ron

You commited no sins to make your app “non-reactive” - thanks to the Elm Architecture…
But you are just not finished yet.

Issue 1:

If you delete everything in the Password field, your DelPass msg is triggered with x being an empty string in your update function. There, you are validating the now empty input string - and get all validation errors. You have to handle the case where x is an empty string.

Btw. since fdbk in your model is derived from model.pass I’d suggest to not keep track of the feedback in your model at all and move the validation to your view function.

Issue 2:

If you want to clear Password2 - you have to clear pass2 in your model. I think it is save to clear pass2 every time the value of pass changes.

To finally answer your questions

    1. Keep going - you have not covered all your goals yet and you have to update your model in the update function according to your goals. But keep in mind that you should usually not keep derived state in your model. E.g. do not keep something like passwordsAreSame in your model
    1. You have to be explicit on what should happen for a given message - e.g. also clear pass2 if pass changes…
    1. There is actually no stale state in your app. It works exactly as you told it. But you didn’t tell it everything it needs to implement all features and missed some stuff.
      You can “Debug” your state - in Ellie, there is a Debug tab where you can inspect the actual state of your model. If the state looks correct but your Application does not what you expected, then there is something wrong in your view function. But if your state looks wrong, e.g. you have validation errors but pass is actually empty - then you have to look at your update function and fix the issue…
  • 4+5. You can write a function that takes both passwords and returns a boolean if both are the same. Then in your view function (not in your model) disable the button if this function returns False. Again, empty strings are a special case - you probably never want to enable the button if pass is empty
    1. You could probably use List.concatMap - go and check the documnetation of the List package: (List - core 1.0.5)

Some general (subjective) feedback on your code:

  • I usually try to name things what they are and not use abbreviations, e.g. feedback instead of fdbk or passwordAgain instead of pass2. Your future self will thank you for this.
  • It’s a common pattern to name messages after what happened. So I would name my messages PasswordChanged and PasswordAgainChanged instead of DelPass and DelPass2. First, I thought these messages are triggered when the user deletes the password input.

I hope my feedback is helpful and leads you on the right track…

What sins make my first Elm app non-reactive?

via Elm - Latest posts by @rupert Rupert Smith on Wed, 23 Aug 2023 07:18:33 GMT

BTW, we often share code snippets using Ellie. Here is one I created from your pastebin code:

https://ellie-app.com/nJFdVhY7d6ba1

Elm isn’t FRP (Functional Reactive Programming) any more, it was prior to 0.17. In version 0.17 the architecture changed from signals and FRP to TEA (The Elm Architecture). The elm guide describes this: The Elm Architecture · An Introduction to Elm

Sorry, doesn’t really answer your questions, but I thought these might be some helpful pointers to get you started working with Elm.

What sins make my first Elm app non-reactive?

via Elm - Latest posts by @Ron on Wed, 23 Aug 2023 01:50:23 GMT

My sign-up mini app is not reactive. Things flow in one-way only - so (I maybe wrong) my app is not reactive.

I am a Python programmer and never touched F[R]P before. I read till Forms · An Introduction to Elm and wanted to add some extras.

Goals:

  1. don’t show password feedback if password field is empty
  2. “password again” input field should be disabled by default, and activated only if password is good (8+ characters, upper and lower case, has punctuation and numbers).
  3. submit button becomes active only when both passwords are same.

Code: module Main exposing (Issue, Model, Msg(..), PasswdFeedback, init, inp, main, up - Pastebin.com

Issues

  1. when I start typing password, I get accurate feedback, if I remove all number, then I get feedback that digits are missing - but if I delete the entire input - the feedback stays - violating 1st goal and the stale state makes it feel non-reactive
  2. I type good password and manage to activate password-again field, but if I then go back to password and type just “abc” then password-again becomes disabled but content locked - this looks ugly and I want it to clear

Questions

  1. How can I achieve the goals in an elegant manner that respects elm/FRP style?
  2. On theoretical level what was wrong in my thinking process, how can I model the signals/messages better?
  3. How can we prevent code written in FRP from leaving stale state and looking non-reactive?
  4. What is the elegant way to implement goal #3?
  5. What would be the future-proof way to implement goal #3?
  6. Ideally feedback should be bunch of paragraphs, but if I write a function showFb : String -> List msg and then do List.map showFb feedbacks I get List (List msg) so how to flatten it to List map?

I thought I’d post this in Request and Feedback - but since my app goals are not met - I am asking under the Learn tag.

Remove unused modules across project

via Elm - Latest posts by @Birowsky on Tue, 22 Aug 2023 15:27:54 GMT

Well gentlemen, I managed to pull this off. Here are the notes for my future self, or anyone else:

  • Clear out unused imports across the project using IntelliJ’s Elm plugin:


image

  • Use the following script to clear out unused modules, including the resulting empty folders.
import * as fs from 'fs-extra';
import * as path from 'path';

export {
  removeUnusedElmModules,
}

function removeUnusedElmModules(
  directory = 'src/scripts',
  entryModuleName = 'MainModule',
  excludeDirectories = [
    'src/scripts/Api',
    'src/scripts/ElmFramework',
    'review/src',
  ],
): void {
  const allElmPaths = findFiles(directory, excludeDirectories, '.elm');
  const allUsedElmPaths = getUsedElmPaths(directory, entryModuleName);
  const allUnusedElmPaths = excludeMembers(allElmPaths, allUsedElmPaths);
  removeFiles(allUnusedElmPaths);
  removeEmptyDirectories(directory);
}


function getUsedElmPaths(directoryPath: string, entryModuleName: string): string[] {
  return getUsedModules(directoryPath, entryModuleName)
    .map(moduleName => getModulePath(directoryPath, moduleName));

  function getUsedModules(baseDir: string, entryModuleName: string): string[] {
    const visitedModules = exploreModule(baseDir, new Set(), entryModuleName);
    return Array.from(visitedModules);
  }

  function exploreModule(baseDir: string, visitedModules: Set<string>, moduleName: string): Set<string> {
    if (visitedModules.has(moduleName)) return visitedModules;
    const modulePath = getModulePath(baseDir, moduleName);
    if (!fs.existsSync(modulePath)) return visitedModules;
    visitedModules.add(moduleName);
    const content = readModuleContent(modulePath);
    const imports = extractImports(content);
    imports.forEach((importName) => exploreModule(baseDir, visitedModules, importName));
    return visitedModules;
  }

  function readModuleContent(modulePath: string): string {
    return fs.readFileSync(modulePath, 'utf-8');
  }

  function extractImports(content: string): string[] {
    return (content.match(/^import\s+([^\s]+)/gm) || []).map((line) => line.split(' ')[1]);
  }

  function getModulePath(baseDir: string, moduleName: string): string {
    return path.join(baseDir, moduleName.replace(/\./g, '/') + '.elm');
  }
}

function removeFiles(filePaths: string[]): void {
  filePaths.forEach(filePath => {
    try {
      fs.unlinkSync(filePath);
    } catch (error) {
      console.error(`Failed to delete ${filePath}:`, error);
    }
  });
}

function excludeMembers(toChange: string[], excludeThese: string[]): string[] {
  const setB = new Set(excludeThese);
  return toChange.filter(item => !setB.has(item));
}


function findFiles(directory: string, excludedPaths: string[], extension: string): string[] {
  return excludedPaths.includes(directory)
    ? []
    : fs.readdirSync(directory).flatMap(decider);

  function decider(candidate: string): string[] {
    const fullPath = path.join(directory, candidate);
    const stat = fs.statSync(fullPath);

    return stat.isDirectory()
      ? findFiles(fullPath, excludedPaths, extension)
      : path.extname(candidate) === extension
        ? [fullPath]
        : [];
  }
}


function removeEmptyDirectories(directory: string): void {
  const files = fs.readdirSync(directory);

  files.forEach(file => {
    const fullPath = path.join(directory, file);
    const stat = fs.statSync(fullPath);

    if (stat.isDirectory()) {
      removeEmptyDirectories(fullPath);
    }
  });

  if (fs.readdirSync(directory).length === 0) {
    fs.rmdirSync(directory);
  }
}

Jeroen, I’ll try to reach out in a couple of days to see if you can debug the elm-review issues.

Cheers!

Elm Town 63 – Opening the doors of functional programming

via Elm - Latest posts by @absynce Jared M. Smith on Tue, 22 Aug 2023 14:54:09 GMT

Hey folks! Join Mika Naylor on her journey with functional programming through the doors of Elm to Elm Land and beyond.

Elm Town 63 – Opening the doors of functional programming:
https://elm.town/episodes/elm-town-63-opening-the-doors-of-functional-programming

YouTube (audio-only)

Elm Town 63 – Opening the doors of functional programming

Thanks to Mika Naylor for coming to Elm Town.

 newer latest older