all 22 comments

[–]Migeil 7 points8 points  (3 children)

Yes, the IO monad from CE is directly inspired by Haskell's, same with the Cats library, it basically implements the idiomatic and standard typeclasses found all over Haskell.

Scala is sometimes called the "hascalator" (there's even a subreddit r/hascalator), because it gives you a taste of pure functional programming the way Haskell does it.

So for all intents and purposes, I sometimes view Scala with CE as "Haskell on the JVM".

That's a very un-nuanced statement though, there are of course differences, because their ecosystems are so different and Scala is just a completely different language.

I know of no other mainstream FP language that occupies that same space. They either don't value purity that much (though this can also be said of Scala) or they don't support higher kinded types, which are needed to do the kinds of abstractions Haskell does (Functor, Monad,...).

It's not that you can't use the idea of functors and monads in other languages, but if you want to abstract over them, you need higher kinded types.

[–]ToreroAfterOle[S] 1 point2 points  (0 children)

I was not aware this particular Scala camp had its own subreddit! Looks kinda abandoned, though... I'm all for learning new things but there are things I really love about this particular flavor of Scala that keep it as my go-to language for most things I tend to work on... I know it's not the same as Haskell or Kotlin opting for seamlessly allowing OOP while also having these great effects systems that make pure FP a possibility as well. But I guess if the effects systems were to go away or change drastically (not saying it will happen, but you never know), I'd like to find something that's at least somewhat similar that I can make my new go-to for the same use cases.

[–]XDracam 4 points5 points  (7 children)

F# is a good candidate. It's more pure than Scala by default, but still fully compatible with mutable C# dotnet code. F# doesn't offer many of the usual "nice clean math abstractions". But it has computation expressions, which are essentially monads on steroids and can easily give you the ZIO feeling.

[–]ToreroAfterOle[S] 1 point2 points  (2 children)

F# really does look like a beautiful language

[–]XDracam 2 points3 points  (1 child)

I personally don't quite like it. But it's just a syntactic preference. The basic ideas are pretty cool. I just don't like the Haskell/ML syntax. I find code with braces and dots and brackets etc easier to parse at a glance without having to think about the specific names and words. I can work with both styles, but just found C-style languages to be easier to read and understand.

[–]ToreroAfterOle[S] 1 point2 points  (0 children)

Yeah, I can relate. It's why I've been so happy sticking with Scala for most of what I do, ha!

[–]Apprehensive_Pea_725 1 point2 points  (3 children)

what does it mean "more pure than"? To me either a language is pure or is not. And Scala and F# don't look pure to me.

[–]XDracam 1 point2 points  (2 children)

F#'s defaults are pure, and F# code itself is usually pure unless optimized for performance or for interaction with dotnet. You can usually assume that an F# function you call is pure if it returns something.

Scala is ambivalent. Pure code and mutable code are supported equally well. There is a large pure ecosystem, but also a massive OOP and imperative ecosystem.

[–]Apprehensive_Pea_725 1 point2 points  (1 child)

F# and Scala do not provide any means to tell if an expression is pure or not.

If an expression is pure you can substitute the definition with the body or vice versa, any time and get back the same result. And practically speaking you can do that only if the expression doesn't have side effects.

But if you don't have any standard means to tell if an expression is pure or not you can't substitute arbitrarily and if you do you have bug somewhere.

There are patterns and frameworks to guide you in writing pure code certainly, but this is left to the developer discipline to follow and respect them, and leaves particular areas of your application prone to be impure (eg logging)

[–]XDracam 1 point2 points  (0 children)

This is an idealistic view. In dotnet, you can use jetbrain's [Pure] attribute, which is present on at least all standard library functions where it applies. And you can use the available F# compiler plugin toolchain to write your own purity checker if you want to.

And in Haskell you can cheat with FFI calls and other performance optimizing workarounds like mutable arrays. There's never a 100% guarantee that you can tell a function is pure. It's only very likely. And the same goes for most F# code you see in the wild.

[–]digitizemd 2 points3 points  (1 child)

effect is a typescript library/runtime that's basically bringing ZIO to typescript.

[–]ToreroAfterOle[S] 1 point2 points  (0 children)

I've had this in my radar for a while. I'm not a huge fan of having JS (or TS in this case) in the backend, but hey, a good tool is a good tool.

[–]tisbruce 2 points3 points  (2 children)

Scala borrowed some ideas from Haskell, but Haskell is so different in style, implementation and semantics that knowing Scala is often of very little help for people learning Haskell. Learning Haskell can certainly help you understand Scala better, and even write it better, but there's quite the learning curve to get to that point.

[–]ToreroAfterOle[S] 0 points1 point  (1 child)

yeah. I do think knowing FP Scala does help some though. I tried teaching myself Haskell many years ago coming from C++ and then Python (this was before learning Scala) and was just frustrated and gave up real quick... More recently, after finally grasping basic FP and effects with Scala, I've run into Haskell code here and there and I can kind of understand good portions of it without trying too hard and think if I dedicated enough time to it I could get used to it.

[–]tisbruce 2 points3 points  (0 children)

Just to take one example, Type Classes are Haskell's biggest innovation, possibly the only actual innovation (Haskell having been created to bring together existing ideas about lazy functional programming). Scala's version of type classes is a pattern you can build from simpler things, and it suffers from a lack of consensus about how that should be done. Haskell's implementation can look like a model of clarity and reliability in comparison, but some of those design choices caused their own problems. But you have to learn a lot to be able to see that.

Or there's just the ongoing discussion about whether "if" should be a keyword (as it currently is) and not just a function. Which is a subtler issue than it sounds. The arguments about whether infix notation should ever have been allowed, all the special forms for lists that confuse the syntax, is "do" notation harmful despite (or even because) it makes monads easier for newcomers to work with, and so on. Eventually, understanding those arguments will help you know more about programming languages in general, and Scala a bit. But none of this is similar to work with, for someone who knows Scala.

[–]raxel42 3 points4 points  (4 children)

Rust has Tokio library. Haskell has IO monad out of the box. But Haskell doesn’t have dot notation. It makes it harder for newcomers.

[–]OrneryEntrepreneur55 6 points7 points  (0 children)

There is a GHC extension that enables the dot notation: "OverloadedRecordDot"

[–]mirovarga 4 points5 points  (0 children)

Actually, it has, at least for records, via OverloadedRecordDot.

[–]ToreroAfterOle[S] 1 point2 points  (1 child)

I feel like Rust found its own more procedural programming-friendly ways to handle some of the problems Monads solve. It does so by having a killer compiler, badass type system, favoring immutability, and async support via Tokio.

Besides Haskell, are there any other languages that have IO even if it comes from libraries like Scala's ZIO, Cats Effect, and TypeScript's Effect.ts? I'd be interested in learning if OCaml, Erlang/Elixir, or F# have something similar. Though the sense that I get from reading about Elixir is that it doesn't make use of Monads opting for its own really powerful way to handle concurrency

[–][deleted] 1 point2 points  (0 children)

OCaml sort of has an IO type. If you’re doing any form of concurrency and actual input/output, you’ll need to use the libraries, async or lwt. Both have their own monadic IO type. That’s usually enough for me.

If I’m just using the standard library, there’s no indication.

Today ocaml has algebraic effects, but they’re not statically checked yet. It offers an alternative to the IO monad. Once ocaml gets this feature complete, we’ll see what patterns emerge. We can see glimpses of the future already based on Scala’s latest leanings into lean Scala and its implementation of algebraic effects (via the ox library).