This question of making Nim more functional seems to come up from time to time, 
so I thought it might be useful to summarize "the state of Nim as to being a 
functional language", at least as I see it...

**History**

Nim's background story would seem to in no way include any back trace to what 
have been called "functional languages" of the past such as Lisp/Scheme/etc. 
The reason for Nim's existance would seem to be to build a better C/C++, which 
it does quite well to the point where I avoid using any "imperative" language 
other than Nim other than to learn enough about them so as to compare features 
and abilities. As a better C/C++, it shares some common goals with quite a few 
other newish languages such as Zig, the V language, and so on all the way up to 
Rust, but having learned something about all of these, I reject them in favor 
of Nim because none of them offer the features I want and they either reject 
automatic memory management altogether or use a very complex system of memory 
management that makes them onerous to work with (Rust, I'm looking at you, and 
you can't even break cycles automatically).

**The Tendencies of Modern Languages to be "Safe"**

Modern **imperative** languages have adopted some functional programming 
precepts in order to enable writing safer code, so most new languages have some 
way of expressing that a variable should not be mutated (Nim's `let`; other 
language's `const`, etc.), some control or indication of what functions are or 
are not side effect free (Nim's `strict func`), support for anonymous functions 
(lambda's), support for closure functions (which may be anonymous - which Nim 
has), some form of support for Algebraic Date Types (Nim's variadic objects) 
and the means to pattern match based on these types (which Nim can do in a 
limited way). In order to be more "safe" Nim has in the works a version of 
Haskell's Type Classes and Rust's `Trait`'s called `Concept`'s and a version of 
Rust's control of mutation called `View`'s that are intended to be easier to 
use, but they are "opt-in" features and will have to be for the foreseeable 
future ax they are experimental and no libraries use them

**Functional Things that Imperative Languages Lack**

However, some features that functional programmers would expect to be able to 
use such as full type inference and currying/partial application of function 
arguments (very difficult to implement in current Nim due to function 
overloading), the ability to eliminate imperative loops by using tail call 
recursive functions (difficult but not impossible in the current Nim as Nim 
mostly compiles directly to C or C++ which do not guarantee tail call safe 
code, so Nim would have to emit code that forces C/C++ to be tail call safe). 
Also, as long as a language such as Nim accepts mutable (`var`) function 
arguments and can return `var`/`lent` values, it is not side effect free and it 
is no use pretending that it is. Another thing that many imperative languages 
lack, especially those that restrict control of variable bindings in the 
interest of "safety" is recursive references to variable definitions, even when 
such recursive definitions can be shown to be safe; in Nim and many other 
imperative languages, we can only do this (when necessitated by the algorithm) 
by forward defining a mutable variable and then assigning it with the 
recursively derived final definition but that breaks being "purely" functional. 
I have read some proposal from some years ago to make Nim more functional 
(can't find it now), but it degraded into just using a package of templates and 
macros that would prevent the use of `var` arguments, provide better versions 
of pattern matching (of which there are template/macro packages that do this), 
and so on, and this proposal died on the vine and hasn't gone anywhere, or at 
least that I am aware.

**Assumptions about Functional Programming that are Incorrect**

There are two things that are commonly assumed about functional languages that 
aren't necessarily true, as follows:

  1. That all functional languages need to have Garbage Collection (GC); this 
is no doubt because most if not all functional languages currently use (GC) 
from Lisp all the way to Haskell, including everything between, although some 
of those languages have had GC forced on them due to the platform on which they 
run (Clojure/Scala/F#/etc.). There is no reason that a functional language 
could not use automatic reference counting as in Nim's Arc and include cyclic 
reference breaking as in Nim's Orc (or something like it).
  2. That functional languages are slow. It's true that functional 
implementations are sometimes slower than more imperative implementations but 
that is more the tendency of functional programmers to overuse list processing 
rather than using recursive function "loops" for time critical code, and to the 
tendency for functional programmers to not consider the time implications of 
constant allocation/deallocation of and wrapping/unwrapping of functional 
"containers", which overhead can often be avoided and/or eliminated by a fully 
optimizing functional language compiler but not always. An example of this 
being done is my Haskell contribute to [the Software Drag 
Race](https://github.com/PlummersSoftwareLLC/Primes/blob/drag-race/README.md), 
where the only reason that my Haskell contribution is slower than my Nim 
contribution is that the Haskell LLVM code back end doesn't emit code that is 
compatible with "auto-vectorization" to SIMD operations where the C code that 
Nim emits is compatible with such optimizations.



**Attitudes Against Functional Programming in Nim**

The general attitude seems to be as @Araq's "if we want functional programming, 
we know where to find Haskell", along with the above two incorrect assumptions 
about functional languages in general, and the common acceptance that if one 
wants to write purely functionally in Nim, one can do it by discipline and by 
using template/macro packages that would help enforce those disciplines. 
However, I don't know that is correct in that, in order to be efficient, 
functional paradigms need to be optimized with some compiler support.

**Final Observations**

It is my feeling that a lot of the recent work in imperative languages 
regarding "safety" as in Rust's lifetimes and mutation control/Nim's views, 
wouldn't be necessary or would be so much simpler if the languages were purely 
functional in the first place, as when everything appears to be immutable, we 
don't have to control mutation other that for some carefully sequenced mutation 
wrapper code that can all be elided away when compiled (for instance, `IO`/`ST` 
monad control of mutation).

It is true that all system code can't be written in a purely functional 
language, as even the Haskell compiler source includes quite a bit of C code 
for the "primitive" operations, but it is my feeling that many of the types of 
errors that plague Nim currentlly could have been avoided if the "outer" code 
had been more functional.

It is still true that Haskell has its fair share of issues in the code base, 
but part of this is that Haskell is mostly an experimental academic language 
for investigating extremely advanced functional concepts; I don't propose that 
a new functional layer be like that at all as to its complexity (I have spent 
about 10 years off and on learning Haskell and still regard myself at only an 
intermediate level) but would rather use a purely functional language like Elm 
as a model with about three or four extensions for things it can't currently 
do, but with C/C++ output as in Nim's. In Elm, there is no talk of "Functor's", 
"Applicative's", and "Monad's" although it uses all of those, just not named 
that way. For instance, in Elm a monad is a container that supports a very 
specific definition of the "andThen" (which is actaully a "bind") function with 
examples such as the `Task` type which is actually something like Haskell's 
`IO` monad except that it also has some elements of the `Either` monad in that 
it can indicate success/failure. Functional langauges don't have to be 
complicated in that Elm is so simple that it can be learned in a few days to a 
week by an experienced programmer, and is used to teach school children of as 
little as age about ten.

Elm only has about 20 keywords, a few more than that operators, and all of 
those can be learned gradually due to its nature. It lacks some desirable 
features such as templates/macros that could be of use for advanced purposes, 
but there is likely a clean way way to implement those; in addition it also 
should have some form of Type Classes/Families/Traits for more extensive type 
use as the current system has some deficiencies that will restrict its progress 
as a language.

The above is the kind of purely functional language I would like to see as a 
front end to Nim used as a build chain to generate C/C++...

There must be others out there that feel as I do?

There would appear to be two approaches to be able to use Nim as a code 
generator but enable writing more functional code, as follows:

  1. To have a package that provides some tools as a form of DSL 
templates/macros that cause Nim to do what is necessary to convert that DSL to 
code as was already proposed and not continued; I feel it would be quite 
difficult to accomplish all of the goals within the Nim framework, especially 
doing things like type inference, etc, but perhaps others more proficient at 
templates/macros than eye could give a better opinion.
  2. Build a new language that uses Nim as a back end, using the functional 
code from a language such as Elm to emit Nim code that would be converted to 
c/C++ by Nim. The reason for using Nim as an intermediate step is to avoid 
having to solve many of the problems that Nim has already solved in providing 
closure functions, a great non-GC memory management system, nested functions, 
etc., etc...


Reply via email to