Yes, you can! Enso is a purely functional language with currying, immutable memory, algebraic data types, data-flow errors, type inferencer, JIT compiler, and even a special kind of a monadic type system with auto-lifting (although we do not expose the ability to define custom monads to the users yet) :)
You can create closures in multiple ways:
1. The simplest and most visual is by using currying! If you create a node (a visual component) and you do not connect all its required inputs, the output of the node is the curried function (function with partially applied arguments). You can then connect it to other expressions, like `map`.
2. Another way is to use expressions on nodes. You can use the lambda syntax, like `x -> x.noise` to define a lambda. You can also make it shorter by using curried operator sections `(. noise)`, and even shorter by using the Enso's operator precedence as `.noise` and use it for example as `map .noise`.
If you want to learn more about Enso syntax, we have a page describing it here: https://enso.org/docs/syntax . Also, if you go to our language-focused subpage, please check out the video named "Higher order functions". It does not show currying, but it shows the usage of the expressions on the nodes: https://enso.org/language
Currying visual nodes that way is an amazing idea! Did it just come about naturally as you were building out the higher level language aspects? Most visual UIs choke if everything isnt fully wired up.
It did come naturally, kind of! This is the one thing we heavily underestimated when starting Enso – the amount of time that is required to make the language's design consistent in both representations. In fact, our desire to create a REAL programming language with strong math background was a two-sided sword. It took us a significant amount of resources, but at the same time, it provided us with a very consistent and a well working solutions.
The same is applied to our data-flow errors. Think of them like about Haskell's `Either` or Rust's `Result` types, but with automatic lifting (automatic applicative functor lifting). So in Enso, you can have a "broken value", like a string read from a file that could be broken because the file did not exist, but you can still concatenate it with other string WITHOUT using any special syntax, and the result may also be broken. In Rust and Haskell you can do the same, but with a special syntax like `(+) <$> str1 <+> str2`.
I hope it clarifies some things under the hood! :)
I believe that functional programming with immutable memory is the only model you can use in order to create a visual language that you can reason about. E.g. you do not want to allow some nodes (visual components) to affect the execution of other nodes if not connected with each other. There are many more such implications there!
You can create closures in multiple ways:
1. The simplest and most visual is by using currying! If you create a node (a visual component) and you do not connect all its required inputs, the output of the node is the curried function (function with partially applied arguments). You can then connect it to other expressions, like `map`.
2. Another way is to use expressions on nodes. You can use the lambda syntax, like `x -> x.noise` to define a lambda. You can also make it shorter by using curried operator sections `(. noise)`, and even shorter by using the Enso's operator precedence as `.noise` and use it for example as `map .noise`.
If you want to learn more about Enso syntax, we have a page describing it here: https://enso.org/docs/syntax . Also, if you go to our language-focused subpage, please check out the video named "Higher order functions". It does not show currying, but it shows the usage of the expressions on the nodes: https://enso.org/language
Did I answer your question? :)