Current State of General Purpose ElmPlus

Introduction

It has been about a year since my thread about the State of Elm and where Evan is going, and at that time there seemed to be quite a bit of interest in my proposal that there be a fork of the Elm compiler that supports more timely updates to Elm’s known issues, supports some often-requested features such as Haskell’s Type Classes (which I called Capabilities or now more favour Abilities as in the Roc language) and a few other minor syntax additions, and that the new language would be a more general purpose language while still being entirely back compatible with Elm. I am opening this thread to declare that I have been working on this project and while I don’t have anything to publish yet, I have made some discoveries on how the new improvements will be implemented that I wanted to share while welcoming any feedback and suggestions. This forum thread will hopefully reach most current Elm users who haven’t entirely moved over to the Roc language camp or elsewhere.

Addressing Previous Concerns

First, in the previous thread there were some concerns that I was setting out to break Elm and its goals as set out by Evan in his previous posts and his talks; that is not the intention of this fork, and in fact, it looks like none of my proposals would prevent the new compiler from being able to do exactly the same job when compiling current Elm projects to produce web pages that still can be run on browsers back to Internet Explorer as they can now. However, new projects that compile using the new compiler are not guaranteed to be able to be compiled with the current Elm compiler due to the introduction of a few new features if such features are used (This is unlike current Zooka language that is currently both forward and backward compatible). Thus, such changes as minor as being able to use ignored underscore separator characters inside numeric literals, use of square brackets symbols as List Type designators for Type Annotations, and being able to use negative integers as Pattern targets for case ... of expressions won’t be back compatible, among other similar syntax extensions. However, as long as the file extensions are “.elm” and the project file is “elm.json” with the current format, the output will be a web page exactly as produced now with the current compiler, although one will have the choice of outputting a WASM or ECMAScript backed web page as well as a ES3/ES5 one as now by some simple modifications to the project JSON file and command line options when building the project. If one wants to generate a command line application, a mobile app, a desktop GUI program, or most other things, there will be a extensions to the new project JSON file and format and possibly a new extension to be used for files that extend Elm syntax in significant ways. Thus, if one wants JavaScript FFI, it likely won’t be available within Elm projects other than through the use of ports just as now, but may be made available within the new extended language formats.

Why Not the Roc Language?

Second, it was suggested that if I want a functional language that is more general purpose but still strict (non-lazy) in evaluation, I should join the Roc language team as a contributor. I have done some major explorations of the current state of that language and have even contributed with some issues and explorations with their contributor team; however, it is my current opinion that it is going to take many years (if ever) for the language to reach stability. That opinion is formed for many reasons, as including the following ones:

  1. It is an entirely new language and breaks many similarities to ML based languages such as Elm in not supporting currying, order of function parameters, general syntax, etc.
  2. To join the Roc project as a contributor, one should have a knowledge of Rust in which the compiler is written, Zig which is used for many packages, LLVM which is used for the back end, Type Inference which in Roc departs quite extensively from Elm’s and even that of Haskell and PureScript, etc. The result of this is that progress on fixing Roc’s issues is quite slow with insufficient contributors in all of the different areas.
  3. The branches of the project don’t seem to be well managed in that a “testing” branch which fixed a huge array manipulation performance problem on which I filed an issue still hasn’t been merged back into the main branch after many months.
  4. The desire to deliver the utmost in performance has led to some severe premature optimizations such as combining the “Morphic lambda set” optimization with the Type Inference phase that has led to a severely broken Type Inference and not clearly demonstrated advantages of the “Morphic” optimizations.
  5. As to the currently broken Type Inference, the current Roc compiler can’t handle cyclic Type references within recursive functions that Elm can, with the same restrictions on cyclic references of local bindings. Whether this is due to the premature optimizations as in the above point or is due to more major problems in the Type system definition isn’t clear to me, but this major problem hasn’t been fixed in many months since I filed an issue and discussed it with them. The following Elm code just works whereas the equivalent in the Roc language crashes the compiler:
import Html exposing (text)
-- a lazily defined Co-Inductive Stream Type expressing an infinite stream...
type CIS a = CIS a (() -> CIS a)
showNCIS : Int -> CIS a -> String
showNCIS n cis =
  let go i (CIS hd tlf) str =
        if i >= n then str
        else go (i + 1) (tlf()) (str ++ Debug.toString hd ++ " ")
  in go 0 cis ""
nats : CIS Int
nats =
  let nxt (CIS hd tlf) = CIS (hd + 1) (\ () -> nxt (tlf()))
  in CIS 1 (\ () -> nxt nats)
main = nats |> showNCIS 10 |> text
-- produces "1 2 3 4 5 6 7 8 9 10 "

where the Roc language problem arises from the recursive nxt function using a recursive Type as an argument. Until this is fixed (which I can’t really help with other than reporting the problem), Roc is very much a Work-In-Progress and I only give it a moderate amount of interest, even if the syntax changes from Elm were acceptable.
6. The other advantage of a completely backward compatible ElmPlus language is that it gets to take advantage of all the available version 0.19 Elm packages that don’t require “native” code so would already have an extensive ecosystem of packages that don’t need to be developed from scratch, which problem the Roc language faces.

Proposal of What Extensions To Include

Next, one needs to consider the extensions to Elm that a new language would support without making it harder to learn than Elm. Anything that is added then needs to be considered very carefully as to if they are really needed. The major extensions that probably need to be included are an extended FFI and “Type Classes”.

Extensions such as providing an often-requested FFI may break the “no runtime crashes in practice” guarantee, but should definitely be provided for use that doesn’t involve web pages since the normal “native” back-end of the new language will be C, which can then be compiled to ECMASCRIPT, Wasm, and any machine code target as required. Thus, there definitely would be ways of providing FFI for C code but that may or may not be extended to pure non-ECMASCRIPT Elm-compatible web pages.

The other major request is for some sort of Haskell’s “Type Classes” which can be provided as in the proposed “Abilities”; however, in order to provide the full suite of “Mappable” (equivalent to Functor’s), “MultiMappable” (equivalent to Applicative’s), and “Chainable” (equivalent to Monad’s), and so on, one needs Higher Kind’ed Type’s (HKT’s), and without HKT’s there aren’t all that many things that “Abilities” can do that aren’t already provided by Elm’s constraints, perhaps with some minor extensions. For instance, if the constraints were extended from number, comparable, appendable, and comappend to include mappable which would mean that a bare map function works on any included type, nmappable, which means that map2 and higher would work with included Type’s, and chainable, which would mean that andThen and the proposed chain would work with included Type’s, there would be much less boilerplate code. However, there is still the problem that this would introduce many more combinations of desired constraints as in appendable combined with the added ones and become more complex than “real” Ability’s. So other than the problem of implementation of requiring HKT’s involving the Type Inference phase, it looks like full capability Ability’s would be a desired feature. It must be noted that just because the new language supports Ability’s doesn’t mean that the old constraints won’t continue to work for backward compatibility, and the compiler would just convert the old constraints as “sugar” into the new forms of Type Annotations so that use of the comappend constraint would be converted into (Comparable a, Appendable a) -> a -> ... to use Haskell syntax. The extensions to elm that I am considering are in an RFC document that I modify as my direction changes.

Implementation Approach

The rest of writing a new compiler just becomes implementation details, and I have considered many of these as follows:

  1. The biggest amount of work that needs to be done is related to Type Inference especially when adding HKT’s to an already somewhat fragile Elm Type Inference phase: When one looks at the majority of the serious issues filed on the Elm compiler that are complex to fix, the majority are related to Type Inference. I think that the Elm Type Inference phase may need to be entirely re-written and tested against all the edge cases where it fails in Elm as well as whatever new edge cases are introduced when adding HKT’s. I have recently spent some time with the PureScript compiler and tested many or most of the cases where Elm fails Type Inference by converted those cases into PuresScript code to find that PureScript doesn’t fail for those cases even though it includes a more complex Type system including HKT’s, Existential Types, RankNTypes, row polymorphism, and functional dependencies. I may need some help on this other than if the Type Inference phase can just be translated from the PureScript version, although one would have to remove some features that may not be desired such as the last two or three.
  2. For efficiency when using reference counted memory management, one needs to elide away unnecessary reference counts. This is obvious when one considers the many examples where this is not done, such as the Fable language (F# to JavaScript) being able to produce reference counted Rust code where the performance of the non-optimized reference counted Rust arrays is worse than the performance of JavaScript “TypedArray’s” or even regular JavaScript arrays, and an attempt to make PureScript generate C code using non-elided reference counting instead of garbage collection is a huge amount slower than even the slow JavaScript PureScript produces (PureScript treats all function calls with more than one parameter as a succession of single parameter calls generating closures which is about five times slower than as Elm does it when all arguments are provided in one call). Eliding away redundant reference counts looks to be not that hard given that the Elm compiler already uses Data flow “graph” analysis to determine recursion in that the same code can be slightly augmented to produce topographical graphs that indicate when a value is “last used” and thus can be destroyed or the value “escapes” and may need a heap based allocation and reference count, for which this type of eliding has been done successfully by many compilers for other languages, even imperative ones.
  3. For efficiency of code generation, the compiler should implement more function inlining, whether automatically performed by the compiler or as flagged by the programmer using “pragmas”; this is particularly necessary when extending function recursion to do tail call optimization when the recursion is across more than one function and was considered by Evan for a future upgrade to the Elm compiler although I have forgotten where he posted this.
  4. Again, for efficiency when inlining tail call optimizations involving chained functions as used by the Chainable (or andThen) functions, the compiler needs to be able to do “lambda lifting” where functions are inlined to avoid building stack and the inner functions are converted so that the loop created to replace the function recursion uses the internal contents of the Type’s instead of the Type’s themselves as loop parameters along with eliding away code that allocates memory for a wrapping Type and immediately consumes that Type while de-allocating the storage used.
  5. The final optimization for efficiency may be to lift array bounds checks outside of “hot loops” (if the C compilers don’t do this) when possible as when the bounds check is comparing a condition which is inherent to the loop condition expression.
  6. When the above optimizations have been done, the remaining C code will be optimized by the “tried-and-true” imperative-type optimizations as already employed by efficient C compilers such as GCC and Clang; this is the advantage of generating C code rather than writing one’s own code generation or even using LLVM: efficient code can be readily generated for almost any targets including WASM, many already available C libraries can readily be used through FFI, and code generation for C code is relatively simple as C is actually a fairly simple language and only a subset of what C provides may need to be used.
  7. Due to outputting C, writing GUI applications for platforms that support WebKit/WebView such as macOS, Linux, Windows, and mobile apps for iOS and Android (although these last may require some “wrapper” code written in Kotlin/Java and Swift, respectively) is a relatively simple matter of changing Elm’s VirtualDOM system to make calls to a WebView interface layer, of which I have found working examples in projects on GitHub that can be used or adapted.
  8. So, working backwards, I have written some C code examples of what I want the new compiler to generate and tested them even including one of the WebView interface packages as per the above, to find that this manually generated C code has excellent performance for generated native code and even for WASM as generated through emscripten.

Where to Start?

Finally, given that adding “Ability”'s to Elm isn’t an essential part of showing the use of the new language and can be added later as a sub-project to re-work the Type Inference phase as mentioned above, the first step in making Elm a general purpose language is to make it output efficient reference counted C code, perhaps quickly followed by adding LinearArray’s to the language including in-place mutation when possible. Investigating this is where I have spent much of my time over the past year. At first, I was just going to have an alternate code generation phase that could be triggered by a compiler command line option for the compiler written in Haskell; however, to do that I had to better understand the AST syntax that is being generated by the previous compiler phases and that was difficult as I didn’t know all that much about how compilers worked. So then, I had the idea of translating the Haskell compiler phase code to Elm and thereby understanding the code in spite of the resulting compiler phases being much slower than the Haskell code due to being restricted to Elm’s persistent data types. I was even going to contribute the resulting code back as an Elm package as it might be useful in writing a learner’s Elm IDE that doesn’t require a server to compile; however, @pit has already done this or most of it and he was able to help me to complete my version of the compiler phases. With @pit’s version made public, there didn’t seem much point in developing this as a package, and re-examining the resulting Elm code with the new languages features in mind, I realized that what I had translated would need to be translated again once the new language was in place including Ability’s, LinearArray’s, and a new library supporting STRef’s, which would make the resulting compiler about the same speed as the current Elm compiler. So having achieved the goal of better understanding the compiler through the translation exercise, it seemed better to go back to just adding to the forked version of the Elm compiler written in Haskell starting at the C code generation phase, and that is where I am now.

The Elm code generation is only about 2,400 lines of code but a version to generate C code is likely to be a little bit more due to having to add reference counting and a home-grown version of implementing closure functions capturing bindings external to the function’s scope, so is likely to take a couple of months or more, and then in order to test it thoroughly, the LinearArray type should likely be added which may add another couple of months. So by mid this year, I might have a repository where interested people would be able to compile and use some Elm-like code or use Elm code to generate C code and through the emscripten compiler to generate WASM code. I’ll post anything ready to be tried here at that time.

Naming of the New ElmPlus Language

I have considered what the new forked language would be called and have considered “elmplus-lang” and “elmp” to pay homage to its Elm origins, but am currently thinking of “fir” and “fir-lang” as starting with the letter “f” and still paying homage to Elm by continuing the arboreal reference, but by being the next letter in the alphabet just as is the word “functional” to indicate that it is both extended while being close to “F#” and not so close to “Haskell”. What do you think?

10 Likes

On the naming side, Fir and fir-lang would be a great candidate!

1 Like

On the Roc Zulip forum, someone once mentioned a “fir” language, and indeed there already is one, unfortunately: GitHub - fir-lang/fir :worried:

Hi @pit,

I hadn’t seen that, and it’s unfortunate that I didn’t open a placeholder repo as I had chosen the name more than a year ago and it looks like the owners of that repo have only been working on it for about eight months by the earliest commits to their repo.

An alternate would be “fig-lang” and “fig” which still works because “fig” is both a tree and a tropical fruit. There is also a GitHub repo with that name but nothing has been done with it for six years other than a ReadMe file, so perhaps I could ask the owner if he could archive it so I could replace it with this version?

1 Like

I generally think it’s fine for projects to have the same name. Especially in this case where there are actually a handful of projects named “fir-lang” across the internet. A quick search showed me at least 1 other project on GitHub named “fir-lang” and another project outside of GitHub with that name.

I’d focus more on your ideas and the actual implementation and worry about the name later.

Just in case you’re unaware of @Brian_Carroll ‘s prior work, it may be of interest:

I believe he also wrote a summary for why he stopped somewhere but I couldn’t find it at a quick glance.

You may also like to consider Gren http://gren-lang.org/ if your intent is to have a community around your new language, as our niche is already quite small relatively speaking to sustain so many forks perhaps :smile:

Thanks @wolfadex:

Although there are other projects with the name “fir-lang”, only the linked one is a project name, which would be a problem if later we want the project to be easily found.

There is another choice that doesn’t seem to be used as the actual name of the fig tree as in “ficus-lang” and “fic” for the command line program and file extension…

But as you say, it isn’t that important other than to know that everyone is talking about the same thing, other than the name often gets hard-coded into documentation and source files other than the buzz if the Internet press gets interested in it wouldn’t refer to the final name; however, if the temporary name chosen is unique enough, a simple global search-and-replace could take care of the documentation and source files although not the online buzz…

@supermario:

I think I remember posts by Brian away back when, but didn’t realize that he had been working on something like this project. From the discussions and his discontinued work on the compiler, it looks like a lot of his effort went into the Wasm Garbage Collector (GC), which I believe isn’t necessary anymore since the Wasm spec now includes an optional GC. However, skimming through his work, I realize that I may have to track back a little further into the output of the compiler phases in order to provide Type Annotations to the Code Generator as well as any necessary modifications to the AST.

That would be very interesting as to whether it was due to lack of time to complete the project, major technical problems, that the output wasn’t very performant (as I suspect from what I see of some of his design decisions emphasizing the use of the heap), etc.

If performance was the problem, it would be like the project where someone put quite a bit of effort into trying to generate C code from PureScript AST, which works but has terrible performance due to essentially emulating JavaScript’s dynamic heap-bound typing but using non-optimized reference counting for memory management (on top of PureScript’s already poor code generation performance). I need to quickly be able to do some reasonable benchmarks to be sure that isn’t the result from C code that can be reasonably easily generated.

I have considered Gren but although it looks to be quite a “live” language as compared to Elm which seems to be stagnating, its goals seem to be mostly to generate JavaScript which, although a worthy goals, I would like to try to push to a more general purpose use of being more what Dart is to Flutter…

You are absolutely right that the already small base of enthusiastic Elm users are being dispersed hither and yon when one thinks of all the users who have moved to Gren, Roc-lang, and perhaps several of the other forks. I am hoping that a new language, if it works out as per my goals, might attract some of them back if it is more general purpose than Gren (even as to being able to make Web Assembly web pages, and may get to a stable status before the Roc compiler does…

Gren has a stated goal of migrating off Javascript to WASM when it will be reasonable to do so.

As of now, the focus is on rewriting the parser to Gren, and eventually the full compiler.

To me, Gren is very much ElmPlus, it keeps the focus on readability and explicitness, and adds support for use cases like backend, cli… It broke backward compatibility with Elm, true, but I find the changes sensible overall (if not unquestionably necessary).

Also, the plan is to introduce parametric modules rather than type classes.

Whatever you decide to invest your time into, please keep us posted. I really appreciate your thoughtful posts.

1 Like

@axelbdt:

Ah, I didn’t know that, it will be interesting when that comes to pass…

The nice thing about all the forks working to fix the issues of Elm is that if I continue with ElmPlus, many of those fixes can be used almost directly to improve the ElmPlus code without me having to do all the research myself, especially when they get to to point of fixing the Type Inference phase…

I’d like to explore whether it is possible to maintain complete backward compatibility while still adding the new features although there are some things related to the core packages that need to be broken for new format source files such as the ambiguous treatment of Int’s/Numbers…

I looked into using parametric modules as OCaml uses, too, but (at least the the way OCaml uses it) doesn’t seem to offer much in the way of advantages over Haskell Type Class’s in that the language still needs Higher Kinded Type’s (although OCaml limits it to Kind of one, meaning that Elm’s Result e a Type would need to be expressed as Result (e, a). True Type Class’s just seem so much cleaner, although maybe I’m biased in having gotten used to the way Haskell and PureScript work…

I’m happy to post here with any progress, and also enjoy a benefit in return as to feedback and input as to what people would be happy with as compared to the other available languages, some of which I may not be aware…

Brian had been very active in the Roc community, too. If I remember right he was responsible for being able to run a Roc REPL in the browser via WebAssembly.

@supermario:

Quoted from Brian’s ReadMe file: “Having type information in the AST would allow me to generate the most optimal code.”

I had a few further thoughts as to Brian’s previous work, as follows:

  1. I think that in order to generate the performant code that I desire for ElmPlus to be a general purpose language, it will be a requirement that the Type Annotations as determined from the Type Inference phase must be provided to the code generator.
  2. This will especially be a requirement given that there will be all the bittedness and signedness for integers as has F# (Int8, Int16, Int32, Int64, UInt8, UInt16, UInt32, UInt64) as well as Float32 and Float64, although this will still be mostly backward compatible by making Int = Int32, Float = Float64, and number default to Float64 in if the Type can’t otherwise be determined by Type Inference.
  3. Brian had to create various workarounds due to the ambiguities about when Int’s are just Number’s and when they are 32-bit integers, which is also one of the inconsistencies with Elm and the core package: for instance, many may not realize that integer multiplication produces a number which is actually a Float64, but integer division produces an integer truncated to 32 bits; round, floor, and ceiling produce a number range with an assigned Int Type, but truncate produces a 32 bit integer, etc.
  4. Due to no Type information, Brian also had problems with distinguishing between ctor’s for the different complex Type’s, which wouldn’t be a problem with available Type information.
  5. There was also a problem with the Time module which assumes that Int’s are number’s that are actually Float64’s but if Int’s are always restricted to 32 bits, then posixToMillis function return value will overflow; to re-write the Time module to output a different type with a wider range would be a breaking change for all existing code, but then so are many of the fixes required to fix Elm’s ambiguities about Int’s/number’s.
  6. Other transpilers to JavaScript also have this problem in inconsistent treatment of integer bittedness: In Fable (F# to JavaScript, plus now some other languages including Rust), Int’s are truncated to 32 bits, but the others such as Int8/UInt8 etc. are just allowed to overflow to number’s/Float64’s; In PureScript literal numbers that could be Int’s are considered to be Int’s but everything else is treated as Number’s/Float64’s and in order to be able to handle either, one has to declare the type variable as of class type Ring or EuclidianRing for division, which in typical PureScript fashion favours pedantic correctness over conciseness or performance…
  7. Providing Type information to the code generation shouldn’t be all that hard as the information has already been determined in the compilation phase and could be passed to code generation just as the AST node graph is…
  8. Due to Elm’s ambiguities as to numbers that can’t really be fixed while maintaining backward compatibility, web pages produced from “.elm” source files with “elm.json” project files will need to continue to have the current (wrong) behaviour, but when generating Wasm web pages using the new “ficus.json” project file will correct the behaviour if the package file used is the new “ficus” core packages but not if it still refers to the old “elm” core package…

With this consideration, I think I can still provide backward compatibility to current Elm while providing a path forward to use the full features of the new language.

Although I don’t want to do premature optimization, I also want the end result of the language when compiled to C to be efficient as proof that functional languages don’t need to be considered as slow, just as Haskell isn’t really slow other than for a few bottlenecks due to Garbage Collection, boxing and unboxing almost everything where optimizations eliding the requisite allocations and deallocations don’t work, non-strict cases that the strictness analyzer doesn’t catch, and lack of automatic SIMD vectorization optimizations; these are all limitations that my proposed implementation of ElmPlus shouldn’t have, with no GC, minimized and elided memory allocations/deallocations, strictness by default, and the C compiler taking care of SIMD autovectorization…

Sounds like you’re very knowledgeable in this domain and have a lot of clarity about how you want to proceed :+1: looking forward to seeing what you achieve with your new language.

1 Like

Minor point, Roc just addressed majority of the problems you listed above by committing to a rewrite. Astute timing.

Rewriting Roc

@Elorm:

Thanks for the timely update on Roc. I wonder how long the rewrite will take before one can see if it fixes the issues I reported…

@axelbdt:

I spent some time on the Gren repo and don’t see the “unquestionably necessary” breaking changes from Elm:

  1. I see the use of {} instead of Elm’s () for the “Unit” Type (like Roc language).
  2. I see the elimination of the Tuple Type in favour of encouraging the increased use of Records including having Record having more Pattern Matching and Updating features (which Record changes are extra features that make Record’s more usable but don’t necessarily break Elm syntax although lacking the Tuple Type does). This and the above change mean that the round parenthesis characters are only parsed as grouping characters in Type Annotations and expressions with no other meaning which may be a small advantage.
  3. I see the re-naming changes of case .. of ... to when .. is ... (again like Roc language) which is a breaking change and changing filter to keepIf (like Roc) but the latter is more a package API definition and not a keyword change so wouldn’t necessarily be backward breaking if there were provision for multiple package sources. This are minor semantic changes that aren’t essential as most programmers are already familiar with the previous words and new programmers quickly learn the older vocabulary.
  4. I see that Custom Type Constructor “payloads” are now limited to only zero or one Type’s rather than as many as one wants as in Elm; this enforces a frequent pattern that one use variant Constructors to contain Record’s when more than one “payload” is desired as is an often used style in current code but I don’t see that it is necessary as the compiler could implement it this way automatically when given more than one payload.
  5. Likely more breaking changes to come, as once backward compatibility is broken, the sky is the limit (like Roc language).

Although I concede that there may be good reasons for these backward-incompatibility-to-Elm changes, I don’t see that any of these are “unquestionably necessary”. Perhaps you can clarify on that point?

Anyway, it is beside the point for ElmPlus in that I regard the main reason for maintaining backward compatibility is be to try to build a community more quickly with former Elm users and/or people familiar with Elm: New languages as Gren is becoming and the Roc language is (although it already seems quite popular with former Elm users, perhaps due to Richard Feldman’s influence) have a much harder development course because the whole compiler needs to immediately be changed right from parsing onward and because they then will have to build an ecosystem of available packages once the language becomes stable. The advantage of maintaining backward compatibility for ElmPlus is that it can immediately tap into the fairly large range of available packages and programmers already familiar with Elm.

1 Like

I just checked in the builder/make code for where the current code generator is called in “Generate.hs” and as I suspected, the Type Annotations are already available and are, in fact, already used in two of the different code targets of four, being for the modes of “debug” where Type Information is provided, “dev” (which is just like “debug” except doesn’t provide Type information and which is used for “reactor”), “prod” for optimized production code where Type information is not provided, and “repl” where Type Information is provided by a different means - argument instead of a “payload” for another argument). Since Type information is provided generally and passed onward at this split point, it is very easy to provide Type information for all Mode’s. It’s strange that Brian didn’t see this…

I think this may perhaps be a communication issue due to a double negative. The original quote was “if not unquestionably necessary”. If we cancel out the double negative, we’re left with “if questionably necessary”, so perhaps @GordonBGood and @axelbdt are in agreement regarding the necessity of Gren’s breaking changes with Elm.

@happyraul:

You may well be right that due to the ambiguity of the double negative, @axelbdt may have the same opinion as do I; at any rate, Gren has thereby started on the route that Gren can never compile Elm source code as well as new Gren source code can never be easily converted back to be used with Elm, although as yet to a lesser extent than the Roc language…

Here is what I meant:

It is questionable that the changes made to Gren that broke compatibility with Elm were necessary. However, I think the changes made are sensible and make for a good language with a coherent design.

So yes, I very much agree with @GordonBGood post regarding these changes.