Earlier quoted context omitted.
The point is that if you forget to write one of the ReactSpecialization methods nothing detects it. Perhaps more importantly, if you add a new type of Animal there's nothing to tell you you need to write a bunch of new ReactSpecializations.
Aha! There's the crux of an argument. It's a valid point actually, that ties into the expression problem. As an OO apologist I do feel compelled to point out that type Animal = Cat | Dog | Mouse let react = function (a, b) | Cat, Dog -> ... ... | x, y -> $"{x} is not interested in {y}." Would suffer from the exact same issue though.
Pattern Matching for Java
141–150 of 152 posts
Re: Pattern Matching for Java
#142Earlier quoted context omitted.
Aha! There's the crux of an argument. It's a valid point actually, that ties into the expression problem. As an OO apologist I do feel compelled to point out that type Animal = Cat | Dog | Mouse let react = function (a, b) | Cat, Dog -> ... ... | x, y -> $"{x} is not interested in {y}." Would suffer from the exact same issue though.
Only if you deliberately add the catch-all case. If you don't and just handle all the cases explicitly, many languages will give you a warning or error when you add a new unhandled variant.
Re: Pattern Matching for Java
#143Earlier quoted context omitted.
To me it seems pattern matching without possibility to actually enforce that all cases are matched seems to miss the most useful scenario: What I want to do is create proper sum types. For some reason most OO languages were designed around the idea that being "open for extension" was a good thing. Normally if I make a base class, I want a closed and known set of sub classes. E.g. if I make a payment method then I wan…
In the C# example in the code I posted, all cases are matched. If you add a new animal subclass, that's not "explicitly" handled, it will route to the default (Animal, Animal) method. It's equivalent of _ -> default_function At the end of a pattern matching statement. Essentially it's impossible for not all the cases to be matched, as far as I can see.
What I want is to make a new type added mean the code won't compile until it is explicitly handled in all pattern matches that do not have a default clause. Basically what I'm saying is that this:
bool HandlePayment(PaymentMethod pm) {
switch(pm) {
case Invoice(addr):
SendInvoice(addr);
return true;
case CreditCard(ccdetails)
return PrcessCreditcard(ccdetails);
default:
// What?
}
}
Would be a lot better if it didn't require the default, and the compiler could detect the error that occurs when someone adds a new case (BitCoin) to the types of payment method. Inheritance and abstract baseclass for the processing means that you'd do this bool HandlePayment(PaymentMethod pm)
{
return pm.Process(order); // sends an invoice, charges credit card etc
}
and you simply have
abstract class PaymentMethod { abstract bool Process(order); }
class CreditCard : PaymentMethod { ... }
class Invoice : PaymentMethod { ... }
And this is of course the regular way of writing OO, but I find it unnatural and cumbersome in many cases. It's hard to define concise descriptions of what types are actually available, and you have to write a ton of boilerplate to ensure a closed hierarchy and complete matching in all scenarios where you can't enclose the logic inside each type.Re: Pattern Matching for Java
#144You can do this in Java using derive4j: https://github.com/derive4j/derive4j . It generates code for algebraic data types which offer a typesafe interface similar to the `case` statements in languages that natively support pattern matching.
In the same way you could "do" lambdas with single method interfaces in Java pre-8. It was possible, but it was still a pain to do.
sealed trait Either[A, B]
final case class Left[A, B](a: A) extends Either[A, B]
final case class Right[A, B](b: B) extends Either[A, B]
and it is still not a real sum type due to exposing subtyping (cf. https://github.com/quasar-analytics/quasar/wiki/Faking-Sum-T...)with derive4j:
@Data
interface Either {
X match(Function left, Function right);
}
or, at your preference: @Data
interface Either {
interface Cases {
X left(A leftValue);
X right(B rightValue);
}
X match(Cases cases);
}
So it is actually not much boilerplate.The real drawback of (any) java implementation is lack of TCO.
Re: Pattern Matching for Java
#145Earlier quoted context omitted.
To me it seems pattern matching without possibility to actually enforce that all cases are matched seems to miss the most useful scenario: What I want to do is create proper sum types. For some reason most OO languages were designed around the idea that being "open for extension" was a good thing. Normally if I make a base class, I want a closed and known set of sub classes. E.g. if I make a payment method then I wan…
Scala gets this right, in my opinion, with sealed traits and case classes. "enum classes" feel like a much more limited option by comparison. Although I'd take either over the status quo to be sure.
The example they give for notification = email | sms | voice looks a lot like what I was trying to sketch with the paymentMethod example.
http://docs.scala-lang.org/tutorials/tour/case-classes.html
I didn't intend for my proposal to be more limited than than the Scala case classes at least :). I want exactly that. An FP closed type hierarchy with enforced pattern matching.
Re: Pattern Matching for Java
#146Earlier quoted context omitted.
I wonder what the programming world would look like now if functional programming concepts had broken through in the 80s and 90s, instead of OO. C would no doubt still be C, filling the same minimalistic imperative close-to-iron niche, but what about higher-level languages?
The move towards Interactive Applications has helped OO and scripting languages. There are still plenty of applications for functional programming in the most demanding of environments, for safety and high availability. However, most students are not good enough at math to learn functional programming concepts. Whether that's the teachers' fault or the students' fault or society's fault, I can't say. But if we paid p…
Re: Pattern Matching for Java
#147Earlier quoted context omitted.
Yep, there is no way to check the exhaustiveness in the compile time. Say you have an Expression parent class and a list of child classes: say Add, Const, Var, and a corresponding visitor. Add new child class, and the visitor would be non-exhaustive. Multiple dispatch is not very different in that sense. I think that Alan Key spoke about dynamic nature of OOP, that's one of the examples. Even in the statically typed…
I'm lost here. Either people didn't read the article and are just relying on preconceived notions about what stuff can and can't happen with multiple dispatch, or I am missing something blindingly obvious. Anyway, consider this example: class Expression {} class Add : Expression {} class Const : Expression {} class Var : Expression {} void FSpecialization(Expression e1, Expression e2) { ... } void FSpecialization(Con…
If you would remove FSpecialization(Expression e1, Expression e2) a.k.a wildcard pattern, compiler would not warn you about your dispatching is not exhaustive. And if you'd add another type of expression, all your dispatchers and visitors without wildcard would become non exhaustive, staying valid code from a compiler perspective at the same time.
Re: Pattern Matching for Java
#148Earlier quoted context omitted.
Yep, there is no way to check the exhaustiveness in the compile time. Say you have an Expression parent class and a list of child classes: say Add, Const, Var, and a corresponding visitor. Add new child class, and the visitor would be non-exhaustive. Multiple dispatch is not very different in that sense. I think that Alan Key spoke about dynamic nature of OOP, that's one of the examples. Even in the statically typed…
I'm lost here. Either people didn't read the article and are just relying on preconceived notions about what stuff can and can't happen with multiple dispatch, or I am missing something blindingly obvious. Anyway, consider this example: class Expression {} class Add : Expression {} class Const : Expression {} class Var : Expression {} void FSpecialization(Expression e1, Expression e2) { ... } void FSpecialization(Con…
Because what happens if you introduce a new expression, but not realize this means the implementation of F should be updated?
Regarding algebraic pattern matching, you can let Haskell check exhaustiveness and then drop the default case. The compiler will then warn you if you forgot one.
To give you an example, in our codebase we have an enum that is used 3068 times in one solution. A very similar one (you would need domain knowledge to understand the difference) is used 2985 times. Both are not defined in that solution by the way.
It's not out of the question they will receive a new enum member down the line. It would be very useful if the compiler was able to tell you a switch that uses either enum is no longer exhaustive.
If you could exhaustively switch on an enum, you could skip the default: case and have the compiler enforce you covered all cases. (I know, the C# enum==int would make that impossible.)
Re: Pattern Matching for Java
#149Earlier quoted context omitted.
Yep, there is no way to check the exhaustiveness in the compile time. Say you have an Expression parent class and a list of child classes: say Add, Const, Var, and a corresponding visitor. Add new child class, and the visitor would be non-exhaustive. Multiple dispatch is not very different in that sense. I think that Alan Key spoke about dynamic nature of OOP, that's one of the examples. Even in the statically typed…
I'm sorry would you mind going into a bit more depth about that example? It's not obvious to me why the compiler can't figure out the the visitor is no longer exhaustive.
Re: Pattern Matching for Java
#150Earlier quoted context omitted.
Scala gets this right, in my opinion, with sealed traits and case classes. "enum classes" feel like a much more limited option by comparison. Although I'd take either over the status quo to be sure.
What's the difference between the enum class I outlined and scalas case classes? I don't know Scala but reading the docs on its case classes, their description seem to fit exactly what I tried to describe (immutable types, closed for further inheritance etc). The example they give for notification = email | sms | voice looks a lot like what I was trying to sketch with the paymentMethod example. http://docs.scala-lang…
They also have more configurability - while a case class provides default implementations for things like equality, you can override those (this could be a pro or a con depending on whether you prefer flexibiltiy or performance, I guess - although arguably the flexibility should only have cost if you use it, assuming it's implemented well).
It's definitely not a huge difference, and arguably Scala can suffer from it's philosophy of providing tools that can do something rather than tools for something that means the language often comes across as huge and as having too much stuff in it. I like it, but it's not the C# way right now for sure.