Live data from Hacker News

The general value of typed functional programming lies in leaving no edge cases

np.reddit.com

131–140 of 172 posts

Re: The general value of typed functional programming lies in leaving no edge cases

#131

Earlier quoted context omitted.

You mean Scala 2. And likely even Scala 1 had this. (Scala 3 is still in development). But Kotlin? Since when does Kotlin have pattern matching with exhaustive checks? Also it does not have an `Maybe a` / `Option[A]` type out-of-the box.

Kotlin distinguishes between nullable and not-null references, which is an alternative to the maybe type. It serves roughly the same purpose, to replace runtime errors with compile-time verification that you haven't missed a corner case.

It's not an alternative for a Maybe Monad. It's an orthogonal concept.

Whether a type is nullable or not is a property of the type. This creates more or less two mirror universes of types, and you can't mix even "the same" types from that incompatible universes.

An Option type is on the other hand side a distinguished parametric type on its own (I wrote `Maybe a` / `Option[A]` for a reason). It can "wrap" arbitrary other types without "infecting" those with the nullability property. Being a proper parametric type makes `Maybe` / `Option` also "just a type constructor". Therefore it can be composed with other types and type constructors according to common rules. `Maybe` / `Option` doesn't require any special rules in the language (which would make the language more difficult to learn and use)!

Also `null` is very different form `Empty` / `None`: A null value can still "sneak in" through arbitrary references and cause run-time errors if one has to interoperate with a "null unsafe" language or system (and that's more or less "the whole universe" out there). A `None` can not "sneak in" as it can be only assigned to something of `Option` type. Sure this doesn't solve the "null problem" as such but one can at least distinguish the cases where the model value is really optional, and the cases where one needs to deal with `null`s coming (potentially) form the outside world. In the one case one will use the Option type throughout the rest of the program, in the other case one will preform some null-check as early as possible and "unwrap" the underlying none-null value to work with it directly thereafter.

Re: The general value of typed functional programming lies in leaving no edge cases

#132
post #46
post #28

Earlier quoted context omitted.

This is also one of the problems with programming with exceptions. You lean on the fact that an exception will be thrown and just propogate up until it finds something, so the normal mindset of an exception-based programmer is to program for the happy-case and only vaguely think about what happens if it goes wrong. I know, I've written a lot of code in this style for many years myself. In some cases, that's not a pro…

There's also the OTP model, where you have exceptions as well as a robust "something" up there. You don't have to deal with the minutiae, and the pattern (processes (aka, isolated processes) and restarts) lets you deal with known or unknown errors.

That doesn't help you detect logic bugs, only bad input.

Re: The general value of typed functional programming lies in leaving no edge cases

#133
post #28

Earlier quoted context omitted.

This is also one of the problems with programming with exceptions. You lean on the fact that an exception will be thrown and just propogate up until it finds something, so the normal mindset of an exception-based programmer is to program for the happy-case and only vaguely think about what happens if it goes wrong. I know, I've written a lot of code in this style for many years myself. In some cases, that's not a pro…

Exceptions and Maybe types aren't mutually exclusive. They should be used together. Maybe types handle variability that is expected and predictable, like a dictionary not having a certain key; exceptions handle the unexpected situations, like a file handle being closed too early. Maybes explicitly express what the programmer has foreseen, exceptions form a safety net over it. You need both in a wholesome language. Th…

Having used Rust, which almost exclusively uses Maybe types, I have to disagree. The "safety net" introduced by exceptions just hides possible errors, and causes reliability problems that would be entirely avoidable if Maybe types were used. The `?` operator makes propogating errors painless if that is what you want, but at least they're all documented so you have to make a concious chocie to pass the buck on error handling.

Re: The general value of typed functional programming lies in leaving no edge cases

#134

Earlier quoted context omitted.

> Exception handling doesn't mean you should be handling all exceptions, just that you can recover if one does occur. You can't because you don't know where it came from so you don't know what happened, why, or if it was recoverable. You can take a stab at it, but that's it.

> You can't because you don't know where it came from Maybe that's a particular language constraint? In general exceptions have a stack trace and it's evident where an exception came from. At least in my experience exceptions don't have to be handled unless or until they're disruptive. If you have a scheduler or task processor code that needs to run continuously then you're going to have robust exception handling, bu…

My problem with exceptions, is it's generally not possible to tell which exceptions a given function might throw (especially problematic with nested dependencies). Thus even if there was a condition hat you could handle you might not know it was possible (until you hit the bug in production!)

Result/Maybe types are awesome, because you just don't get unexpected runtime exceptions due to programming error. You only get them when a genuine problem occurs.

Re: The general value of typed functional programming lies in leaving no edge cases

#135

Earlier quoted context omitted.

You mean Scala 2. And likely even Scala 1 had this. (Scala 3 is still in development). But Kotlin? Since when does Kotlin have pattern matching with exhaustive checks? Also it does not have an `Maybe a` / `Option[A]` type out-of-the box.

Kotlin will do exhaustive checks in a `when` block when it is an expression. You can add a `.let { }` at the end of the when statement if you want to enforce this and not really return anything. sealed class Communication data class Email(val emailAddress: String) : Communication() data class Shouting(val preferredName: String) : Communication() object HiveMind : Communication() fun exhaustiveWhen(comm: Communication…

Sure. But that doesn't look helpful to me as it works only sometimes…

Even the syntax can be made less awkward[1] this look still like a wonky hack to me where the user can do way to much wrong.

[1] https://proandroiddev.com/til-when-is-when-exhaustive-31d69f...

Re: The general value of typed functional programming lies in leaving no edge cases

#136
post #35

Earlier quoted context omitted.

plenty of other languages do so, but it's both unfairly diminishing to the parent & questionably correct to refer to it as "basic type safety stuff"

JS has this too in TS, so I’m pretty sure the perception among users of typed languages is that this is a basic part of what type systems do. What does this have to do with FP?

TS actually has quite an advanced type system compared to languages like Java.

Re: The general value of typed functional programming lies in leaving no edge cases

#137

Earlier quoted context omitted.

C. typedef struct{int filled; union{void* data;} val;} maybe;

How does that fail at compile time if you try to read a value when there is none?

If you try to use it val directly, it fails. You must unpack it first.

To do something like x.get() in Scala when x is empty is a runtime error, not a compile-time error.

Re: The general value of typed functional programming lies in leaving no edge cases

#138
post #56
post #51

They say that, and then turn around and show an example using IEEE-754 Doubles. You've got two infinities and two "not a number" values. What your type system thinks is a number isn't even necessarily a number. Maybe. Better be sure to call the "isNaN()" and "isInfinite()" methods all over the place, because those are the worst edge cases of all and your language provides no help in detecting or avoiding them. It's l…

You can always use a fixed-precision numeric type. You might be thinking "what about libraries that require floating-point", and that's a good concern. Luckily in Haskell there's the Num typeclass, so your libraries can let users use whatever fractional number type they'd like.

Fun fact, Num basically requires inf and nan unless you're willing to tolerate either terrible overflow (non-)handling (eg Int,SatInt) or memory blowup (eg Integer,BaireReal).

  # where R is some r such that Num r
  let huge = 
  let inf = huge * huge  # can't be finite unless you
  # truncate or saturate or something
  let nan = inf - inf  # can't be either of
  # infinite (infinitely  -inf), or
  # finite (catastrophic rounding errors)
Num doesn't allow inf or nan to be Maybe R, so you're stuck filtering out in-band errors from a bare R.

Re: The general value of typed functional programming lies in leaving no edge cases

#139
post #63
post #44

I am not sure this is the only value, but it is generally true. And this is one of the great advantages of haskell. By the way, now that I know some haskell, I see this edge case sloppiness all the time and it is really annoying. For example, here is something that annoyed me just recently. You go to yahoo finance and there they will show you the revenues of a company as well as the revenue growth from past year. But…

Ive noticed other languages with this feature, they call them optional types, you can pass in a null or a type value. /s slightly, maybe more snarky, but seriously, optional types are way overhyped. Null objects are far more useful than optionals. Optionals tend to leak all over the show.

The point of optionals is that they leak, and leak visibly. You cannot use an optional where a non-optional is expected, whereas in C nothing will stop you from writing `x->y` when x is, or might be, NULL. Thus the optional (and non-optional) types turn null pointer errors into compiler errors.

If you wish to reduce "leakiness", you can dispatch on the null-ness of the variable; Swift lets you write `if let x = x { /* x is non-optional in here * / }`, which binds a non-optional value inside the braces, and `guard let x = x else { return } /* x is non-optional now * /` which binds a non-optional value after the braces. So as soon as you consume an optional value, you are free to "un-leak" it and extract its value (if it has one) immediately.

Re: The general value of typed functional programming lies in leaving no edge cases

#140
post #24
post #19

Earlier quoted context omitted.

If you have a Rust enum with Color{Red,Green,Blue), if for some reason you update to Color{Red,Green,Blue,Yellow}, the rustc compiler will force you to manage the new Yellow case wherever you want to "match" a pattern on Color, so overall you have less chance to forget to manage the new case everywhere. This kind of error is not managed by a C or C++ compiler (as far as I know). But from my understanding, this is not…

If you compile with `-Wall` GCC will emit a warning for failing to exhaustively check enum values inside a switch statement (I don't remember the specific underlying feature flag name, sorry). EDIT: It's called `-Wswitch`

The big difference is that C++ emums can't contain data. C++ has checking on enums, but enums are a relatively niche data structure for specific use cases.

In Rust, enums are a core data structure with completely equal status to structs, and are used pervasively throughout the language including the standard library.

Notably, both Option (which is used instead of null) and Result (which is used for error handling) are enums, which means that you thse correctness checks for every null and every error condition, on by default. Thats a huge deal as these are often the source of unexpected runtime errors.

Post reply on HN