So this says three true things we know about this 'fac'
Firstly it says it takes an int and it returns an int.
Secondly that if the int it takes is specifically zero then the int it returns is specifically one.
Thirdly and lastly, for all other ints it returns that int, times (in the arithmetic sense) a recursive call to itself of the int one less.
I worry about why it doesn't have to say abs(n) but then I remember two negatives multiplied together are positive.
The function doesn't terminate if n is negative, so you are right to worry!
fac :: Int -> Int
(0) => 1
(n) => n * fac (n-1) fac :: Int -> Int
fac x = case x of
0 -> 1
n -> n * fac (n-1)
or even {-# LANGUAGE LambdaCase #-}
fac :: Int -> Int
fac = \case
0 -> 1
n -> n * fac (n-1) fac :: Int -> Int
fac(0) => 1
fac(n) => n * fac(n-1)OP's first four words: "The thing about syntax".
The differences may be miniscule. They are syntactic. "Might be cool to" have this other syntax. You're just belaboring the obvious.
In Haskell you could always avoid that style and use the case syntax (or for this example guard syntax) instead. All varieties eventually desugar to a case analysis anyway. Haskell even added curly braces as an alternate to indentation!
If clean has a REPL (which I couldn’t easily find out from its web page) I expect it to have that, too.
In the Haskell REPL, you would need to enter the definitions together.
I think it’s just a matter of familiarity. Once you learn how it works, it’s actually extremely elegant and in no way “disconnected.”
Conflating of data types with storage types is a surprisingly common occurrence, although I can appreciate it's largely due to pragmatism.
Point being, you're spot on about mixing types and even "simple" functions need not be trivial (for various reasons, eg: factorial of a negative number - might be defined as negative infinity):
Everything is way more "disconnected" in OOP. For example I have to implement length in both the classes EmptyElement (0) and Element (1+next->length()). Both belong together, but they are scattered across the source code. With clean like syntax i would have written them right next to each other.
I had started with F# and was put off by it like you described. I moved to Haskell and had difficulty understanding it yet again, but when I moved back to F# one more time it was a delight.
Also, I've come to appreciate when I write in say Rust, the fact that I can let go of the syntax, just write and focus on my ideas and let my editor sort the mess at the end.
Having types and removing statements from the language make this much less of an issue
> I can let go of the syntax, just write and focus on my ideas and let my editor sort the mess at the end.
Elm has a whitespace significant syntax, but it too has an excellent code formatter tool.
I still like languages like Python and F# though.
About style, after a while spent reading about FP, I just can't solve problems without it.
Most of my code quickly ends up in lines of
// f <param-1 subset> <param-2 subset> => desired structure //// type
// f <param-1 subset'> <param-2 subset'> => desired structure //// type
which then gets translated on whatever language I end up writing (javascript, java, emacs lisp ..)I do understand your argument and tend to agree, I would like to see a syntax error if there is anything between the multiple lines of implementation.
also, this is syntactic sugar for a case statement.. and you couldn't just use those.