Where is Elm going; where is Evan going

@rupert,

I just had a quick read through the MLIR documentation and it seems that the “ML” stands for Multi-Level not “ML” or Meta-Language as in the family of FP languages such as OCaml, Haskell, F#, and Elm, etc.; further, the examples in implementing a language dialect seem to be more OOP oriented than FP oriented, but maybe there are other ways of applying it?

I would think that using MLIR for a FP language code generation back-end might be considerably more complex than emitting C code as I propose

@axelbdt,

Thanks for keeping us informed.

That allows us to save our donation budgets for projects that may move Elm forward if Evan doesn’t announce that he is promoting a way to do this at the coming meetup…

@rupert,

From the README file, the main reason they want Zokka code to run on current Elm is to be able to use the Elm built-in package manager to contribute packages back to the Elm ecosystem (perhaps important until Zokka has its own package repository fully in place, at which time this ability might rarely be used…

Now that I know Zokka exists, my contributions to fixing Elm’s issues should likely be to Zokka rather than on my own, and the few fixes not desired to be merged into current Zokka such as negative indices in pattern matches can be added in later, even if next year, as the problem appears to be mostly a hacky kludge to pattern parsing where a number is converted to a string too early before it has a chance to have the negative unary applied.

However, that also leaves me free to pursue researching extensions to the Graphing data flow analysis as to determining scope escape points (where if an object escapes it needs to be allocated on the heap or if already on the heap, a reference count incremented), last used points (where an object can either be destroyed, or if part of a recursive loop is a candidate for in-place mutation), assignment points (as places where a reference count needs to be incremented only if both the right side and the left side of the assignment escape, etc. It should be a fun project…

MLIR is very well suited to FP, because of regions. There is likely a steeper learning curve than going to C. But there is working code already for Lean4 → MLIR to show the way which certainly makes it more accessible.

This paper explains why MLIR is a good fit for FP and will enable FP to use LLVM which has generally not been the case so much as for other language paradigms:

For completeness, a much earlier paper examining the idea that SSA is functional programming. But as it turns out functional programming is not just SSA, and regions are what fills the gap:

That could be a problem…

Suppose I develop a new package that relies on some bug fixes in Zokka, and publish it to package.elm-lang.org. Now someone using Elm picks up my package and finds that it does not work as expected because it contains a bug.

Really Zokka should disable the publish command (which is a one liner in the code). I will point this out and suggest that this be done if it has not already been done.

@Lucas_Payr Elm-studio sounded like a product I wouldn’t be interested with, but I’d be curious to hear what didn’t work out, since it never launched as far as I know?

When I watched the talk a while ago, I thought to myself “What is the product?” and “Who are the customers?” On the other side of the spectrum I believe even researchers have to please (at least to some degree) whoever is funding them.

As a developer, I’m in part interested in having great tools. So I’d be happy to pay for an editor plugin if it helped me work faster or more efficiently. This in turn could help indirectly fund a language I would think, providing having enough users which could take time to build up.

Maybe some context:

The last status update by Evan was in 2021

After that he went silent (probably working on Elm-studio). The next public appearance was the talk at strange loop in 2023(That’s the talk that started this thread). In it he talked about a project he has been working on.


So yeah, I’m just adding one and one together to assume that he is talking about Elm Studio.

Got it. It seems that I understood the opposite of what you did, that he is working on Elm Studio, which is to be released and nothing so far has shown that it didn’t work out.

Which is why this thread makes sens to me. How will the language evolve or open up to enable solving different problems? Until we get answers, I suppose we can only speculate.

@rupert,

From what I think I read, disabling publishing from Zokka is already done as far as the Elm package system goes (any publishing it can do is only to Zokka private servers)…

1 Like

@rupert,

I’ve been mulling over this, and have the following response:

  1. I have no problems with having a formatter specification embedded in a program, and in fact like the languages where this facility is embedded in the main application, as in v format so would we have a elm format.
  2. I quibble a little bit with the choice of four spaces for indents rather than my preferred two in the interests of code not growing too far to the right, but that’s minor and I can live with four which is kind of a standard across languages.
  3. I also don’t have a problem with the current Elm format program adding lots of extra lines for readability, as that reduces line length if anything.
  4. There is just one area that the current Elm formatter gives me problems when there is an occasion to emulate Haskell’s “do notation”, as in the following example:
module Main exposing (main)

import Html exposing (Html, text)

flip : (a -> b -> c) -> b -> a -> c
flip f x y = f y x

main : Html Never
main =
    let rslt = -- note use of flip and back piping so that nested brackets aren't necessary
            flip Maybe.andThen (Just 42) <| \n ->
            flip Maybe.andThen (Just "Hello") <| \hstr ->
            flip Maybe.andThen (Just []) <| \lst ->
            Just hstr
    in rslt |> Maybe.withDefault "Error!" |> text

which when run through the current Elm format program turns into the following Elm code:

module Main exposing (main)

import Html exposing (Html, text)


flip : (a -> b -> c) -> b -> a -> c
flip f x y =
    f y x


main : Html Never
main =
    let
        rslt =
            -- note use of flip and back piping so that nested brackets aren't necessary
            flip Maybe.andThen (Just 42) <|
                \n ->
                    flip Maybe.andThen (Just "Hello") <|
                        \hstr ->
                            flip Maybe.andThen (Just []) <|
                                \lst ->
                                    Just hstr
    in
    rslt |> Maybe.withDefault "Error!" |> text

Notice how far this code is caused to “grow to the right” for just three “do statements” where one might have occasion to use many more than this.

Making the Elm format handle this means recognizing the exception of what the code is doing, which may be complicated. Adding a “do notation” or “backpassing” as in Roc means that the unique syntax can be recognized as having different alignment rules; as well, having Abilities/Capabilities would mean that the (in this case) Maybe qualifier wouldn’t be necessary, so the “backpassing” code might look like the following and the formatter would respect that unless the line length was too long in which case it would make minor adjustments using piping:

module Main exposing (main)

import Html exposing (Html, text)

flip : (a -> b -> c) -> b -> a -> c
flip f x y = f y x

main : Html Never
main =
    let
        rslt =
            _ <- flip andThen <| Just 42
            hstr <- flip andThen <| Just "Hello"
            wstr <- flip andThen <| Just "World"
            Just <| hstr ++ ", " ++ wstr ++ "!"
    in
    rslt |> Maybe.withDefault "Error!" |> text

The advantage of the “backpassing” is that it is easy and clear to read but being “backpassing” and not “do notation” it doesn’t only work for monads as in the Maybe monad here, but for any chainable functions producing any Type at the cost of having to add the flip <whatever chaining function> although the flip could be included automatically with the <-.

To me, having conciseness of code means more readable and understandable code, which leads to less potential syntax errors…

Could you give a more concrete example? To me, the elm code looks like a very complicated way of saying Just "Hello", so I’m not sure why you would want to structure code this way (I am not familiar with Haskell do notation).

I just never use the left pizza <|.

1 Like

The PureScript/Haskell do notation explained for Elm developers :wink: GitHub - laurentpayot/purescript-for-elm-developers: PureScript crash course targeted at Elm developers

@dta

Sorry, choosing the Maybe monad (which it actually is although it’s not called that) was not a good choice, although I tried to show how it would work if there were three things we ran, one whose number 42 result we didn’t care about, and two that produced strings or nothings that we did care about which if both successful we would combine them and show the result. Following is a current Elm example showing a chain of Task’s, the first getting the current time, the second running some kind of computation that produces a number, and the third getting the current time again, from which the answer and the time to accomplish it are produced. This is a common pattern for IO operations in Haskell although one can use TEA steps to avoid the chaining in Elm; however, there could be a need for chaining on occasion, which is why we have andThen functions for monads…

import Task exposing (Task, perform, andThen, succeed)
import Time exposing (now, posixToMillis)
import Html exposing (Html, text)
import Browser


flip : (a -> b -> c) -> b -> a -> c
flip f x y =
    f y x


expensiveComputation : () -> Task Never Int
expensiveComputation() =
    succeed 42


test : () -> Cmd String
test() =
    perform identity <|
        flip andThen now <| \ startTimeUTC ->
        flip andThen (succeed (posixToMillis startTimeUTC)) <| \ startTime ->
        flip andThen (expensiveComputation()) <| \ answer ->
        flip andThen now <| \ stopTimeUTC ->
        flip andThen (succeed (posixToMillis stopTimeUTC)) <| \ stopTime ->
        let elapsed = stopTime - startTime in
        succeed <| "Found " ++ String.fromInt answer ++
                   " in " ++ String.fromInt elapsed ++ " milliseconds."


main : Program () String String -- both model and message are String!
main =
   Browser.element
       { init = \ _ -> ("", test())
       , view = \ mdl -> mdl |> text
       , update = \ msg _ -> (msg, Cmd.none)
       , subscriptions = \ _ -> Sub.none
       }

This is a bit of an artificial example as many Elm users would just use TEA for the steps, but one might want to use this if ElmPlus were more than a web page generator and the code was used in a CLI in which case there would be IO Task’s such as reading and writing to console as some of the steps.

It would also come into play if there were other monads used to control the side effects of mutation in a “pure” way…

@rupert,

I don’t use it often but my use in these examples is a very good reason for its existence in that it avoids the need for a bunch of nested parathesis in the ending nested functions…

It fills the same place a the $ function in Haskell - to avoid parathesis…

Roc doesn’t have it, but can avoid the need for it by the use of the “backpassing” sugar…

@rupert,

I see that perhaps the Zokka route may be overly restrictive as to only allowing bug fixes where the resulting Zokka code is fully compatible with the Elm compiler: it seems to me that a higher percentage of the current Elm issues are to do with Type Inference caused by local type signature’s Type variables not being unified to their global ones of the same name (although the inferred ones are unified in the absence of a local Type signature), causing conflicts and/or ambiguities. If Zokka imporoved the Type inference in unifying these Type variables, and thus could compiler more cases where local Type signatures were used successfully, the resulting Zokka code would not compile successfully on current Elm and thus the fix would be disallowed (at least for 2024).

I think that may severely limit the issues that can be fixed in Zokka, although as you have said, the attempt to fix the “organizational” problems is still good…

Thank you for the new example!

It seems to me that do notation is useful when:

  1. the value is used by the next step in the chain[1]; and
  2. the value is used at some later point in the chain; and
  3. there needs to be some deferred execution.

Without (1), we could use mapN instead of a chain. Without (2), the source code of the chain doesn’t grow to the right because each step is applied at the top level instead of inside the lambda. Without (3), Elm’s current let in notation is the same as the do notation.

(3) also indicates that this syntax applies only to certain specific monads like Task a or () -> a[2]. It appears to have limited usefulness in current Elm, and it is unclear to me how useful it would be in an ElmPlus since I don’t know the use cases relevant to the new execution environment.


  1. Note that Tasks can have an implicit dependencies since they can have an effect on the outside world. I think this may be where the original example got lost in translation. ↩︎

  2. What is this one called? ↩︎

In my view, it is a good approach to take and solving the “organizational” problem should be higher priority.

Suppose Zokka decides to add a load of new features, and makes incompatible changes, now we all have to make a choice right away - stick with Elm, or move to Zokka. Gren and Roc have both kind-of already presented us with that choice, and some people did indeed move away as a result, but a significant group has remained on Elm.

By maintaining compatability the choice is delayed, we can use both side by side, get comfortable with the new project, contribute to it, see how it is working out and what direction it might take. If there is genuine community involvement, then when the choices part comes we will hopefully be involved in it with an established project that has a plurality of contributors.

I anticipate that will be hard though - how do such things get decided? Some will want one feature, and another group will not. That is the advantage of the BDFL way of working. But we need to solve this somehow if Elm is to have a future.

2 Likes

@dta,

Remember, “do notation” or Roc’s “backpassing” is just syntax sugar for how it can be written otherwise as I tried to show, with the problem of the nesting making the code grow to the right when conventionally formatted and increasing levels of bracket nesting if the <| isn’t used and without the “sugar”.

  1. there needs to be some deferred execution.

It looks like deferred execution but it’s just the FP way of expressing chained computations; with a slightly more sophisticated compiler than Elm’s, the functions get lifted/elided away and the “native” code is just running a linear sequence of instructions.

Without (3), Elm’s current let in notation is the same as the do notation.

Not quite: let...in’s don’t specify the execution order if the bindings aren’t recursively dependent, whereas function chains control the exact order.

(3) also indicates that this syntax applies only to certain specific monads like Task a or () -> a . It appears to have limited usefulness in current Elm, and it is unclear to me how useful it would be in an ElmPlus since I don’t know the use cases relevant to the new execution environment.

The technique applies to all monads, although current Elm has only three: Task, Maybe, and Result. My first Maybe example wasn’t so good as I just used Just calls to create monads, but what if each of those was a call to a routine that might fail? Chaining the (implied) calls would then mean that one can concentrate the error handling to the end of the chain, and in the case of Result, specialize the error handling dependent on the variation of the error.

ElmPlus may have at least another monad in an equivalent to the Haskell ST monad so it can express things like STRef to be able to express the side effect of mutation in a “pure” FP way, which when chaining monadic computations is best expressed this way and much more concisely with “do notation” or at least “backpassing”.

ElmPlus may well have more direct FFI (using Task monads to express side effects) and some environments may find the use of TEA MUV loops awkward to use, so there very likely will be increased need of this construct to use chaining instead.

Also, if “backpassing” is used as in Roc, the use applies to any function chain and not just monadic ones…

@eniac314,

When and how do we find out what was announced at the above Meetup?