As we all know, let expression is actually just a syntactic sugar for immediately invoked lambda expression (IILA). Actually not entirely correct, as pointed out in this comment.
For example, in Haskell:
let x = a in b
is the same as
(\x -> b) a
In the quest of minimising syntax, we can remove let expression, but it's obviously bad for UX because IILA is unreadable.
So why not the other way round, remove lambda expression and replace it with extended let expression?
Let's see.
Lambda abstraction:
\x -> b
can be written as let-expression that is not instantiated with any value:
let x; b
And so:
\x -> \y -> b
is equivalent to:
let x; let y; b
Application in extended let-expression is equivalent to lambda. For example in lambda:
(\x -> b) a
can be written as follows in let-expression:
(let x; b) a
Since Haskell does not have a built-in mechanism to define argument's default value, we can still achieve the same effect with the used of Maybe
type.
sayHello :: Maybe Int -> [String]
sayHello (Just count) = replicate count "Hello"
sayHello None = ["Hello"]
In this case it's acceptable to pass in None
explicitly, but in case where we have a lot of arguments with default values, the call-site will become cluttered with noise.
In extended let-expression, we can easily introduce a keyword, say overridable
to allow arguments with default values.
For example,
sayHello =
let overridable count = 1;
replicate count "Hello"
sayHello -- ["Hello"]
sayHello 2 -- ["Hello", "Hello"]
But of course, overridable arguments must only appear after all mandatory arguments.
As we can see above, with one simple extension to let-expression, we can completely eliminate the necessity of the lambda syntax.
Namely the uninstantiated let-expression, let var: type;
which allow us to introduce function abstraction.
Also, we can see that by simply introducing a minimal syntax, say the overridable
keyword, we can allow function argument's default value without cluttering the language grammar.
From the UX perspective, the implication of using extended let-expression to replace lambda syntax is the reduction of choice, which can ultimately leads to better experience.
And hopefully, from the point of view of programming language designers, this kind of syntactical unification can be useful for those looking to minimise their language grammar.
let rec factorial =
let x: int;
if x <= 1 then
1
else
x * factorial (x - 1)
let average =
let xs: int list;
let sum = fold (let acc; let x; acc + x) 0 xs;
sum / length xs
Then I also realised that this idea can be further extended to support a
do-notation
like syntax, for example:Can be translated as: