Dogelang – A Python with Haskell Syntax
pyos.github.io
pyos.github.io
(And to be clear: I use Haskell professionally and love it - but the syntax is _not_ why most people use Haskell).
I think most people who complain about the readability of a language like Haskell are confusing familiarity with clarity. We learned Java or C++ or whatever in school, and despite the fact that those objectively have more complex syntaxes and more meaningless symbols, they are “easier to read” because that’s what you started with. Haskell has a higher semantic density with fewer boilerplate symbols - surely easier to read from a first principles standpoint. If you started from a math background and are comfortable thinking in terms of equations, it’s almost certainly easier to read.
But it's much more common, at least for me, to eg just guess at operator precedences and leave out parens, because for the most part the type system will yell at you, if you get it wrong.
This is in stark contrast to the likes of C or C++, where the rule of thumb is 'when in doubt, add parens', because even when you get the precedence right when writing the code after looking it up, you'll most likely will have forgotten by the time you or someone else reads the code again; so you save everyone time with the extra parens.
The main thing I'd want from a better Python syntax would be to make more constructs in the language expressions with values instead of statements. For example, if-then-else. And introduce pattern matching.
The surface syntax might want to take more inspiration from Lisp instead of Haskell. Lisps are traditionally dynamically typed, so you can look at quite a few already thought through solutions.
It's amazing if you really try to write something haskell-like where it actually breaks.
There is Hacket, which is Racket with Haskell semantics (Haskell in S-Expressions)...
https://lexi-lambda.github.io/blog/2017/05/27/realizing-hack...
>>> f = lambda x: (
... (
... print('a') or
... print('b')
... ) if x % 2
... else (
... print('c') or
... print('d')
... )
... )
>>> f(42)
c
d
>>> f(43)
a
bThat is strikingly hideous. After such knowledge, what forgiveness?
Which looks a bit better.
The walrus operator moves Python closer to the expressions-camp.
In practice, the bigger problem with a more functional Python is that the ubiquitous dicts don't have nice and concise operators for persistent operations. Eg you can concatenate two lists via +, but dicts are more cumbersome.
Though that improved a bit, {dictA, dictB} mostly solves that problem. But more operators would be useful.
And, of course, lack of tail call elimination puts a damper on things. You can mostly work around that problem, but eg implementing state machines as mutually recursive functions is going to be a pain.
func(
lambda: otherfunc(
1, 2, 3, 4
)
)
And you don't need statements, given that with haskell semantics there are no statements.https://github.com/seq-lang/seq
I think you might be interested in looking at multi-paradigm languages that use static/inferred typing and combine the functional and imperative paradigms. Some worth taking a look at might be OCaml, Nim, and F Sharp. Nim leans more towards Python, OCaml and F# lean more towards Haskell.
https://en.wikipedia.org/wiki/OCaml
https://en.wikipedia.org/wiki/Nim_(programming_language)
https://en.wikipedia.org/wiki/F_Sharp_(programming_language)
The case studies here can show you the syntax: https://coconut.readthedocs.io/en/master/HELP.html
And the site has a good summary of some of the features over vanilla Python: http://coconut-lang.org/
The route is via the OverloadedStrings extension, and then implementing an instance for IsString (String -> String) and IsString (String -> String -> String) etc.
That's called curryfication (named after the logician Haskell Curry) in case you don't already know :)
I believe this is how it's called in non-English languages, but all my instructors and PL/semantics textbooks referred to this process as "currying" [0]. Which, if that's what you meant, is not what your parent comment was talking about. I believe they were talking just about syntax, not semantics, i.e., they wish in Python you could do `foo x y` instead of `foo(x, y)`.
https://en.wikipedia.org/wiki/Currying#Contrast_with_partial...
a function that takes a B and returns...
a function that takes a C and returns...
a value of type D.
This is determinable by just looking at the number of arguments provided. But a variadic function, in a world with currying, would be a function that takes an argument, and returns...well, it might be a function to take another argument, and it might be the actual computation.
Even if we didn't have currying, we can't just use whitespace in a Python-like language. We don't know if (print) is actually a function call, or just referencing the function object (perhaps to pass it to 'map' or something). Your syntax needs to distinguish between those two cases.
but there are strict impure languages with white space function application. i suppose they handle the issue by using an object of a type with only one value.
in principle, you could handle the issue by contrasting (print) with (print ()), with () a pseudo value only existing at syntax level.
i'm not sure if white space function application carries it's own weight once we abandon partial application, so this feels like a contrivance to save a bursted balloon.
Implementing a vararg printf in Haskell is a popular enough exercise.
A relatively common examples in practice is the testing library QuickCheck: your properties (ie tests) can take any number of arguments that the library fills with samples inputs.
I don’t know if that makes them worthwhile, but I always find them a breath of fresh air.
Applicative plumbing is especially easy in structure, so removing it from you doesn't hide any complexity from the experience programmer. It's easy to reconstruct the plumbing on the fly in your mind, if you want to reason about it.
Other tools like lenses or arrows take a lot longer for a lot of people to internalize like that.
about(haskell).flatMap(a -> {
return so(unreadable).map(s -> {
return whats(s, a);
});
});I'm wondering if the CPython bytescode is stable though. Is the Python bytecode spec managed independently from the language version (like JVM) so this kind of approach won't break when a new Python is released?
Clio transpiles to JavaScript and appears to target microservices or serverless architecture.
"Solutions at the time included various attempts to compile Haskell to JavaScript while preserving its semantics (Fay, Haste, GHCJS), but I was interested to see how successful I could be by approaching the problem from the other side - attempting to keep the semantics of JavaScript, while enjoying the syntax and type system of a language like Haskell."
https://leanpub.com/purescript/read#leanpub-auto-about-the-a...
[1]: https://github.com/fused-effects/fused-effects [2]: https://github.com/ghc-proposals/ghc-proposals/pull/180