My problem with Haskell is that it is not pragmatic and the reason I stopped using it. The philosophy of separating pure and impure code – and to enforce it – does more harm than good. Often its difficult to understand what your "forth level of abstraction code" above the usual abstraction levels really does. Haskell is fun to learn. Haskell has a lot of interesting concepts to explore and it will make you a better programmer. Haskell code can be really dense. And Haskell code can be totally unreadable, using concepts which take weeks to learn – which is... not so great.
Lisp (Common Lisp) is what you get when you reduce the rules and syntax to a minimum and try at the same time to be a maximal flexible programming language. This simplicity and applied pragmatism is what makes it so great. Like the authors in the Lisp books warned, after learning Lisp, coding won't be the same. Now I see everywhere code that could easily be written in Lisp, but is not so great in language XYZ. All those SDL's and stringified API's and code-generators they need to do real things and data formats like JSON... all obsolete, if they would have chosen Lisp (or a language with brackets and Lisp like evaluation rules).
The other thing that is often overlooked regarding Lisp is, that in a Lisp like language there is no need to wait for a new version of the programming language to be released to get new syntax – you can simply add it yourself (or have a library doing it for you). E.g. a lot of the JavaScript mess could be fixed by code transformation, if they would have chosen a Lisp like syntax in the first place.
I don't see Haskell as uber language. If I want super correct code, speed and I'm okay with using a more complex language, then there is Rust – which is probably more correct than Haskell and faster, too.
Haskell, Lisp and C++ would really benefit form a std library 2.0 and a default package manager... one can dream.
The ability to come up with new syntax in LISP is in my opinion vastly overrated.
Yes, LISP code can be easily parsed for as long as you stick to lists. However the ability to do anything complicated with LISP code is simply not there because LISP code loses type info, which is extremely important for anything you’d want to do with code, like all kinds of non-superficial transformations and refactorings.
So here are some facts given as examples:
(1) LISP code does not make the job of IDE authors easier; just ask the authors themselves
(2) in terms of macros, anything more complicated than lazy evaluation of thunks and kiddie examples is out of reach; for example .NET’s LINQ cannot be expressed in LISP, not unless you add a static type system, but at that point you’re no longer talking about LISP ;-)
In my opinion LISP has been vastly romanticized, yet it doesn’t live up to expectations. It falls short in every department, from functional programming, to available tooling, to the great enlightenment people are supposed to experience once they learn it.
Whereas Haskell really does live up to expectations, being one of those very few ecosystems that does make developers better, at this point being the lingua franca of FP.
Of course not. You could always implement C# (static types and all) and LINQ atop Lisp, or indeed any other turing complete language.[1]
The point of static type systems isn't that they add power -- it's that they take power away. Thus empowering the programmer to reason in a restricted subset of "all the possible things".
[1] I'm ignoring the detail of there actually being a way to interface with the actual physical hardware, but I think we can assume that in any remotely practical TC language.
I guess the question you are begging is: how far can Lisp-LINQ go from the syntax, semantics, and ergonomics of C#'s LINQ before you stop calling it LINQ?
Turing completeness doesn't describe expressivity of source code, as far as I understand it - it describes the ability to produce a desired outcome. So yes, you can perform the same operations LINQ allows, in Lisp or any other Turing-complete language. But can you do it while giving the programmer the same (or a similar enough) experience? From the .NET docs:
> Language-integrated query allows query expressions to benefit from the rich metadata, compile-time syntax checking, static typing and IntelliSense that was previously available only to imperative code. Language-integrated query also allows a single general purpose declarative query facility to be applied to all in-memory information, not just information from external sources.
Now, to be fair, I'm not sure you can express all that in Haskell either. And it could be reasonable to argue that a few of those benefits (e.g. intellisense) just _haven't_ been done for Lisp, not necessarily that they _can't_ be done.
Syntax is different from abstraction. The problem in doing those things in turing machines and assembly is the low level of programming. Otherwise their syntax is pretty simple: commands and operands.
It's lambda complete. Which has been famously proven to be the same as turing complete.
Also type checking is basically more or less putting restrictions on your code. If there is a language that doesn't have the syntax to compute certain things, how would restricting that language via type checking suddenly cause it to be able to compute everything and be turing complete? Makes no sense. To make a language change from not turing complete to turing complete you have to expand.. not restrict ... but expand it's computational ability. Again Type checking serves to restrict.
Type checking is essentially just pre runtime special syntax for:
define add (x,y)
if type(x) != 'int' or type(y) != 'int':
throw TypeError
return x+2
It's not a big deal. I'm not sure what LINQ is, but you don't have to add a type checking system to a language to do type checking if you know what I mean.
Type checking is much more than that. It is not the same as throwing exceptions when types don't match; it is about proving that a type checked program will never produce type errors.
You're right. I wanted to illustrate that type checking shouldn't be a factor in the the parents context as you can use code to explicitly type check or prove it.
The advantage of LINQ in my eyes is that it allows you to build an expression tree that you can wait to evaluate until a result is actually needed.
Then the provider can decide how it wants to do it, based on the types and operations involved. A SQL DB based provider can’t safely push any work to the database if it doesn’t know the structure or types of the objects and properties in the expression tree. It would have to return whole tables and perform all evaluation in the app where types could be handled dynamically.
I think something like typed racket or clojure may support it, but many lisps would not.
Common Lisp has a way better OO type system than C#. It has to be queried at run-time, granted, but that doesn't stop something like LINQ from working.
> The advantage of LINQ in my eyes is that it allows you to build an expression tree that you can wait to evaluate until a result is actually needed.
Isn't that the exact definition of the "unquote" and "unquote-splicing" operators inside a quasiquote?
Honestly asking since I'm not past the "makes my head hurt" stage of scheme macros, probably doesn't help that the quasiquote macro is buggy in my minischeme sandbox.
It's been a while, and I've only really used Racket extensively, but "unquote" and "unquote-splicing" operate at macro expansion time, not run time. So unless you're using a typed lisp like Typed Racket, the macro won't have enough information to (for example) know that something is a number vs a string - the kind of distinction that's important for efficient and correct SQL query generation.
> Haskell code can be totally unreadable, using concepts which take weeks to learn – which is... not so great.
I don't think it's fair to say something is "not so great" because it takes more than a few weeks to learn. That logic leads to a sad place. You suggest using Lisp and maybe Rust, but both of those languages would probably take more than a few weeks to learn.
Also, you could just use the IO type on every function in Haskell, and thus eliminate the separation between pure and impure code. You'd only have to a type a few extra characters here and there, which isn't ideal, but is acceptable while being pragmatic.
Haskell has had a default package manager, called "cabal", for many (15+ ?) years.
I learned Haskell and then moved to OCaml and found it to be a much more practical language. Haven’t tried Lisp yet, but it’s quickly moving to the top of my list.
Oddly enough it was the opposite for me. I think I might have given up on Haskell if I hadn't learned O'Caml first. It's been 15 or so years, so I'm sort of guessing at this point, but I think the combined cognitive effort of going from typical imperative languages to Haskell and the whole algebraic data types+pattern matching thing might have broken me.
I found Haskell much more practical because it makes refactoring truly easy, but I'm very "programmer-centric". I mean 'refactoring' in the strict sense of preserving absolutely all the existing behavior, including side effects. That is extremely difficult if you can have side effects all over the place. (Strictness obviously helps a lot, but it also has downsides.)
Btw, given your experience and if you want to try Lisp seriously I'd probably recommend Racket, Typed Racket and their Turnstile experiment(?).
You’re right about the advantages of Haskell regarding side-effect strictness and refactoring. That’s a huge win. I think now that I have significant experience with OCaml it’d make going back to Haskell easier for me. That’s probably something I’ll do. But right now I’m finding OCaml has been great for me.
It holds less closely to the ideal of being a purely functional language. It gets out of my way and let’s me get things done. But I think I misspoke a bit. A big part of what has made OCaml better for me is that the tooling has been easier for me to jump into as a beginner.
>I don't see Haskell as uber language. If I want super correct code, speed and I'm okay with using a more complex language, then there is Rust – which is probably more correct than Haskell and faster, too.
But it doesn't even have Higher Kinded Types! What a disgrace. /s
I do wonder, what would OP, or any seasoned Haskell programmer, think about Rust. Would they consider it strange? Primitive? Too mainstream?
Rust doesn't really have a way to control for effects in functions. The type of the function doesn't define what the function can do. This I think is the biggest problem coming from haskell.
How does that work in Haskell, is it the pure/impure dichotomy regarding I/O that is encoded in the type signature? Because one could argue that Rusts ownership is a primitive version of that, but yielding most of the gains ("if it compiles, it works").
The combination of type classes (traits in Rust) and HKTs in Haskell mean that you can quite easily restrict a function to only be able to do a subset of what's possible in "the world".
A trivial example would be
foo :: Logger m => Int -> m ()
where Logger is a type class (trait in Rust-speak) which implements the Monad type class (thus allowing 'imperative'-style programming), but also has a "log" method which allows the program to emit a string to the log.
The thing is that given the restrictions of parametric polymorphism that function 'foo' has to work for any Logger instance we give it, so it absolutely cannot do anything other than pure computation or invoking the "log" method. It cannot do any other side effects at all. So, it's not "pure", but it's also much more restricted than "impure".
That's a very powerful tool for modeling highly effectful systems.
>Well there are more Haskell jobs than rust here, so I wouldn’t say more mainstream.
Only inside the echo bubble of Haskell.
Rust can be put to use in any place, without advertising it for it even -- and already powers widely used code across the world (and not just in Mozilla).
Haskell use mostly concerns a few financial institutions...
Do they? Or did they just had a head-start and aren't going anywhere booming further soon?
It's not like all the financial sector runs on Haskell or something -- the majority uses the usual culprits. The stories behind these organisations that I've read of is of some opinionated PM going in and making Haskell or OcamL and the like the officially supported language -- in Jane Street and Standard Chartered and the like. Which is the definition of being an outlier choice.
So, yeah, they are "more programmers" wanted for those 20+ and 30+ year languages, compared to a merely 3 year old language (Rust 1.0 was released in 2015).
But nowhere near the momentum.
Rust has been used already by Dropbox, Canonical, Cloudflare, Atlassian, Microsoft (Azure), Chef, Mozzila (of course), and tons of others in backend and client code. And that's in 3 years, and while the language is changing and core libs are still being built.
And it's silly to judge by job postings, as those places don't need to advertise for specific Rust roles (they can get regular systems programmers or use their own existing devs and have them do Rust). It's not a huge paradigm shift like with Haskell where you'll need people with that kind of training already. Besides, Rust is so new, there would be little point asking for Rust devs explicitly.
Scala is interesting language, but I wish it had tighter focus. John De Goes describes pain points quite accurately in Scalapeno keynote: https://youtu.be/v8IQ-X2HkGE
> My problem with Haskell is that it is not pragmatic and the reason I stopped using it.
I find it extremely pragmatic, but perhaps not in the way most people use the word. It's pragmatic about the abilities of programmers to get everysingle detail right all of the time, i.e. it discards that notion entirely. We will not get things right enough of the time and so the language must support and help you. This goes all the way from simple static typing to enforcing Purity of Essence, I mean, Functions.
If you don't care about managing side effects you can always just write everything in IO and slap a "pure" here or there. Haskell's actually also a pretty good imperative programming language, IME.
I find that haskell is great to learn about functional programming and programming ideas, but to get things done, I stick to imperative or OO languages. Could be that I learned CS with C/C++/Java and work with C#/C++. So I just stick to what I'm familiar with.
Lisp (Common Lisp) is what you get when you reduce the rules and syntax to a minimum and try at the same time to be a maximal flexible programming language. This simplicity and applied pragmatism is what makes it so great. Like the authors in the Lisp books warned, after learning Lisp, coding won't be the same. Now I see everywhere code that could easily be written in Lisp, but is not so great in language XYZ. All those SDL's and stringified API's and code-generators they need to do real things and data formats like JSON... all obsolete, if they would have chosen Lisp (or a language with brackets and Lisp like evaluation rules).
The other thing that is often overlooked regarding Lisp is, that in a Lisp like language there is no need to wait for a new version of the programming language to be released to get new syntax – you can simply add it yourself (or have a library doing it for you). E.g. a lot of the JavaScript mess could be fixed by code transformation, if they would have chosen a Lisp like syntax in the first place.
I don't see Haskell as uber language. If I want super correct code, speed and I'm okay with using a more complex language, then there is Rust – which is probably more correct than Haskell and faster, too.
Haskell, Lisp and C++ would really benefit form a std library 2.0 and a default package manager... one can dream.